monotone

monotone Mtn Source Tree

Root/cmd_list.cc

1// Copyright (C) 2002 Graydon Hoare <graydon@pobox.com>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10#include "base.hh"
11#include <algorithm>
12#include <map>
13#include <utility>
14#include <iostream>
15
16#include <boost/tuple/tuple.hpp>
17
18#include "basic_io.hh"
19#include "cert.hh"
20#include "charset.hh"
21#include "cmd.hh"
22#include "database.hh"
23#include "globish.hh"
24#include "keys.hh"
25#include "restrictions.hh"
26#include "revision.hh"
27#include "simplestring_xform.hh"
28#include "transforms.hh"
29#include "ui.hh"
30#include "vocab_cast.hh"
31#include "app_state.hh"
32
33using std::cout;
34using std::make_pair;
35using std::map;
36using std::ostream_iterator;
37using std::pair;
38using std::set;
39using std::sort;
40using std::copy;
41using std::string;
42using std::vector;
43
44CMD_GROUP(list, "list", "ls", CMD_REF(informative),
45 N_("Shows database objects"),
46 N_("This command is used to query information from the database. "
47 "It shows database objects, or the current workspace manifest, "
48 "or known, unknown, intentionally ignored, missing, or "
49 "changed-state files."));
50
51CMD(certs, "certs", "", CMD_REF(list), "ID",
52 N_("Lists certificates attached to an identifier"),
53 "",
54 options::opts::depth | options::opts::exclude)
55{
56 if (args.size() != 1)
57 throw usage(execid);
58
59 vector<cert> certs;
60
61 transaction_guard guard(app.db, false);
62
63 revision_id ident;
64 complete(app, idx(args, 0)(), ident);
65 vector< revision<cert> > ts;
66 // FIXME_PROJECTS: after projects are implemented,
67 // use the app.db version instead if no project is specified.
68 app.get_project().get_revision_certs(ident, ts);
69
70 for (size_t i = 0; i < ts.size(); ++i)
71 certs.push_back(idx(ts, i).inner());
72
73 {
74 set<rsa_keypair_id> checked;
75 for (size_t i = 0; i < certs.size(); ++i)
76 {
77 if (checked.find(idx(certs, i).key) == checked.end() &&
78 !app.db.public_key_exists(idx(certs, i).key))
79 P(F("no public key '%s' found in database")
80 % idx(certs, i).key);
81 checked.insert(idx(certs, i).key);
82 }
83 }
84
85 // Make the output deterministic; this is useful for the test suite, in
86 // particular.
87 sort(certs.begin(), certs.end());
88
89 string str = _("Key : %s\n"
90 "Sig : %s\n"
91 "Name : %s\n"
92 "Value : %s\n");
93 string extra_str = " : %s\n";
94
95 string::size_type colon_pos = str.find(':');
96
97 if (colon_pos != string::npos)
98 {
99 string substr(str, 0, colon_pos);
100 colon_pos = display_width(utf8(substr));
101 extra_str = string(colon_pos, ' ') + ": %s\n";
102 }
103
104 for (size_t i = 0; i < certs.size(); ++i)
105 {
106 cert_status status = check_cert(app, idx(certs, i));
107 cert_value tv;
108 decode_base64(idx(certs, i).value, tv);
109 string washed;
110 if (guess_binary(tv()))
111 {
112 washed = "<binary data>";
113 }
114 else
115 {
116 washed = tv();
117 }
118
119 string stat;
120 switch (status)
121 {
122 case cert_ok:
123 stat = _("ok");
124 break;
125 case cert_bad:
126 stat = _("bad");
127 break;
128 case cert_unknown:
129 stat = _("unknown");
130 break;
131 }
132
133 vector<string> lines;
134 split_into_lines(washed, lines);
135 I(lines.size() > 0);
136
137 cout << string(guess_terminal_width(), '-') << '\n'
138 << (i18n_format(str)
139 % idx(certs, i).key()
140 % stat
141 % idx(certs, i).name()
142 % idx(lines, 0));
143
144 for (size_t i = 1; i < lines.size(); ++i)
145 cout << (i18n_format(extra_str) % idx(lines, i));
146 }
147
148 if (certs.size() > 0)
149 cout << '\n';
150
151 guard.commit();
152}
153
154CMD(keys, "keys", "", CMD_REF(list), "[PATTERN]",
155 N_("Lists keys that match a pattern"),
156 "",
157 options::opts::depth | options::opts::exclude)
158{
159 vector<rsa_keypair_id> pubs;
160 vector<rsa_keypair_id> privkeys;
161 globish pattern("*");
162 if (args.size() == 1)
163 pattern = globish(idx(args, 0)());
164 else if (args.size() > 1)
165 throw usage(execid);
166
167 if (app.db.database_specified())
168 {
169 transaction_guard guard(app.db, false);
170 app.db.get_key_ids(pattern, pubs);
171 guard.commit();
172 }
173 app.keys.get_key_ids(pattern, privkeys);
174
175 // true if it is in the database, false otherwise
176 map<rsa_keypair_id, bool> pubkeys;
177 for (vector<rsa_keypair_id>::const_iterator i = pubs.begin();
178 i != pubs.end(); i++)
179 pubkeys[*i] = true;
180
181 bool all_in_db = true;
182 for (vector<rsa_keypair_id>::const_iterator i = privkeys.begin();
183 i != privkeys.end(); i++)
184 {
185 if (pubkeys.find(*i) == pubkeys.end())
186 {
187 pubkeys[*i] = false;
188 all_in_db = false;
189 }
190 }
191
192 if (pubkeys.size() > 0)
193 {
194 cout << "\n[public keys]\n";
195 for (map<rsa_keypair_id, bool>::iterator i = pubkeys.begin();
196 i != pubkeys.end(); i++)
197 {
198 base64<rsa_pub_key> pub_encoded;
199 hexenc<id> hash_code;
200 rsa_keypair_id keyid = i->first;
201 bool indb = i->second;
202
203 if (indb)
204 app.db.get_key(keyid, pub_encoded);
205 else
206 {
207 keypair kp;
208 app.keys.get_key_pair(keyid, kp);
209 pub_encoded = kp.pub;
210 }
211 key_hash_code(keyid, pub_encoded, hash_code);
212 if (indb)
213 cout << hash_code << ' ' << keyid << '\n';
214 else
215 cout << hash_code << ' ' << keyid << " (*)\n";
216 }
217 if (!all_in_db)
218 cout << (F("(*) - only in %s/")
219 % app.keys.get_key_dir()) << '\n';
220 cout << '\n';
221 }
222
223 if (privkeys.size() > 0)
224 {
225 cout << "\n[private keys]\n";
226 for (vector<rsa_keypair_id>::iterator i = privkeys.begin();
227 i != privkeys.end(); i++)
228 {
229 keypair kp;
230 hexenc<id> hash_code;
231 app.keys.get_key_pair(*i, kp);
232 key_hash_code(*i, kp.priv, hash_code);
233 cout << hash_code << ' ' << *i << '\n';
234 }
235 cout << '\n';
236 }
237
238 if (pubkeys.size() == 0 &&
239 privkeys.size() == 0)
240 {
241 if (args.size() == 0)
242 P(F("no keys found"));
243 else
244 W(F("no keys found matching '%s'") % idx(args, 0)());
245 }
246}
247
248CMD(branches, "branches", "", CMD_REF(list), "[PATTERN]",
249 N_("Lists branches in the database that match a pattern"),
250 "",
251 options::opts::depth | options::opts::exclude)
252{
253 globish inc("*");
254 if (args.size() == 1)
255 inc = globish(idx(args,0)());
256 else if (args.size() > 1)
257 throw usage(execid);
258
259 globish exc(app.opts.exclude_patterns);
260 set<branch_name> names;
261 app.get_project().get_branch_list(inc, names);
262
263 for (set<branch_name>::const_iterator i = names.begin();
264 i != names.end(); ++i)
265 if (!exc.matches((*i)()) && !app.lua.hook_ignore_branch(*i))
266 cout << *i << '\n';
267}
268
269CMD(epochs, "epochs", "", CMD_REF(list), "[BRANCH [...]]",
270 N_("Lists the current epoch of branches that match a pattern"),
271 "",
272 options::opts::depth | options::opts::exclude)
273{
274 map<branch_name, epoch_data> epochs;
275 app.db.get_epochs(epochs);
276
277 if (args.size() == 0)
278 {
279 for (map<branch_name, epoch_data>::const_iterator
280 i = epochs.begin();
281 i != epochs.end(); ++i)
282 {
283 cout << i->second << ' ' << i->first << '\n';
284 }
285 }
286 else
287 {
288 for (args_vector::const_iterator i = args.begin();
289 i != args.end();
290 ++i)
291 {
292 map<branch_name, epoch_data>::const_iterator j = epochs.find(branch_name((*i)()));
293 N(j != epochs.end(), F("no epoch for branch %s") % *i);
294 cout << j->second << ' ' << j->first << '\n';
295 }
296 }
297}
298
299CMD(tags, "tags", "", CMD_REF(list), "",
300 N_("Lists all tags in the database"),
301 "",
302 options::opts::depth | options::opts::exclude)
303{
304 set<tag_t> tags;
305 app.get_project().get_tags(tags);
306
307 for (set<tag_t>::const_iterator i = tags.begin(); i != tags.end(); ++i)
308 {
309 cout << i->name << ' '
310 << i->ident << ' '
311 << i->key << '\n';
312 }
313}
314
315CMD(vars, "vars", "", CMD_REF(list), "[DOMAIN]",
316 N_("Lists variables in the whole database or a domain"),
317 "",
318 options::opts::depth | options::opts::exclude)
319{
320 bool filterp;
321 var_domain filter;
322 if (args.size() == 0)
323 {
324 filterp = false;
325 }
326 else if (args.size() == 1)
327 {
328 filterp = true;
329 internalize_var_domain(idx(args, 0), filter);
330 }
331 else
332 throw usage(execid);
333
334 map<var_key, var_value> vars;
335 app.db.get_vars(vars);
336 for (map<var_key, var_value>::const_iterator i = vars.begin();
337 i != vars.end(); ++i)
338 {
339 if (filterp && !(i->first.first == filter))
340 continue;
341 external ext_domain, ext_name;
342 externalize_var_domain(i->first.first, ext_domain);
343 cout << ext_domain << ": "
344 << i->first.second << ' '
345 << i->second << '\n';
346 }
347}
348
349CMD(known, "known", "", CMD_REF(list), "",
350 N_("Lists workspace files that belong to the current branch"),
351 "",
352 options::opts::depth | options::opts::exclude)
353{
354 roster_t new_roster;
355 temp_node_id_source nis;
356
357 app.require_workspace();
358 app.work.get_current_roster_shape(new_roster, nis);
359
360 node_restriction mask(args_to_paths(args),
361 args_to_paths(app.opts.exclude_patterns),
362 app.opts.depth,
363 new_roster, app);
364
365 // to be printed sorted
366 vector<file_path> print_paths;
367
368 node_map const & nodes = new_roster.all_nodes();
369 for (node_map::const_iterator i = nodes.begin();
370 i != nodes.end(); ++i)
371 {
372 node_id nid = i->first;
373
374 if (!new_roster.is_root(nid)
375 && mask.includes(new_roster, nid))
376 {
377 file_path p;
378 new_roster.get_name(nid, p);
379 print_paths.push_back(p);
380 }
381 }
382
383 sort(print_paths.begin(), print_paths.end());
384 copy(print_paths.begin(), print_paths.end(),
385 ostream_iterator<file_path>(cout, "\n"));
386}
387
388CMD(unknown, "unknown", "ignored", CMD_REF(list), "",
389 N_("Lists workspace files that do not belong to the current branch"),
390 "",
391 options::opts::depth | options::opts::exclude)
392{
393 app.require_workspace();
394
395 vector<file_path> roots = args_to_paths(args);
396 path_restriction mask(roots, args_to_paths(app.opts.exclude_patterns),
397 app.opts.depth, app);
398 set<file_path> unknown, ignored;
399
400 // if no starting paths have been specified use the workspace root
401 if (roots.empty())
402 roots.push_back(file_path());
403
404 app.work.find_unknown_and_ignored(mask, roots, unknown, ignored);
405
406 utf8 const & realname = execid[execid.size() - 1];
407 if (realname() == "ignored")
408 copy(ignored.begin(), ignored.end(),
409 ostream_iterator<file_path>(cout, "\n"));
410 else
411 {
412 I(realname() == "unknown");
413 copy(unknown.begin(), unknown.end(),
414 ostream_iterator<file_path>(cout, "\n"));
415 }
416}
417
418CMD(missing, "missing", "", CMD_REF(list), "",
419 N_("Lists files that belong to the branch but are not in the workspace"),
420 "",
421 options::opts::depth | options::opts::exclude)
422{
423 temp_node_id_source nis;
424 roster_t current_roster_shape;
425 app.work.get_current_roster_shape(current_roster_shape, nis);
426 node_restriction mask(args_to_paths(args),
427 args_to_paths(app.opts.exclude_patterns),
428 app.opts.depth,
429 current_roster_shape, app);
430
431 set<file_path> missing;
432 app.work.find_missing(current_roster_shape, mask, missing);
433
434 copy(missing.begin(), missing.end(),
435 ostream_iterator<file_path>(cout, "\n"));
436}
437
438
439CMD(changed, "changed", "", CMD_REF(list), "",
440 N_("Lists files that have changed with respect to the current revision"),
441 "",
442 options::opts::depth | options::opts::exclude)
443{
444 parent_map parents;
445 roster_t new_roster;
446 temp_node_id_source nis;
447
448 app.require_workspace();
449
450 app.work.get_current_roster_shape(new_roster, nis);
451 app.work.update_current_roster_from_filesystem(new_roster);
452
453 app.work.get_parent_rosters(parents);
454
455 node_restriction mask(args_to_paths(args),
456 args_to_paths(app.opts.exclude_patterns),
457 app.opts.depth,
458 parents, new_roster, app);
459
460 revision_t rrev;
461 make_restricted_revision(parents, new_roster, mask, rrev);
462
463 // to be printed sorted, with duplicates removed
464 set<file_path> print_paths;
465
466 for (edge_map::const_iterator i = rrev.edges.begin();
467 i != rrev.edges.end(); i++)
468 {
469 set<node_id> nodes;
470 roster_t const & old_roster
471 = *safe_get(parents, edge_old_revision(i)).first;
472 select_nodes_modified_by_cset(edge_changes(i),
473 old_roster, new_roster, nodes);
474
475 for (set<node_id>::const_iterator i = nodes.begin(); i != nodes.end();
476 ++i)
477 {
478 file_path p;
479 if (new_roster.has_node(*i))
480 new_roster.get_name(*i, p);
481 else
482 old_roster.get_name(*i, p);
483 print_paths.insert(p);
484 }
485 }
486
487 copy(print_paths.begin(), print_paths.end(),
488 ostream_iterator<file_path>(cout, "\n"));
489}
490
491namespace
492{
493 namespace syms
494 {
495 symbol const key("key");
496 symbol const signature("signature");
497 symbol const name("name");
498 symbol const value("value");
499 symbol const trust("trust");
500
501 symbol const public_hash("public_hash");
502 symbol const private_hash("private_hash");
503 symbol const public_location("public_location");
504 symbol const private_location("private_location");
505 }
506};
507
508// Name: keys
509// Arguments: none
510// Added in: 1.1
511// Purpose: Prints all keys in the keystore, and if a database is given
512// also all keys in the database, in basic_io format.
513// Output format: For each key, a basic_io stanza is printed. The items in
514// the stanza are:
515// name - the key identifier
516// public_hash - the hash of the public half of the key
517// private_hash - the hash of the private half of the key
518// public_location - where the public half of the key is stored
519// private_location - where the private half of the key is stored
520// The *_location items may have multiple values, as shown below
521// for public_location.
522// If the private key does not exist, then the private_hash and
523// private_location items will be absent.
524//
525// Sample output:
526// name "tbrownaw@gmail.com"
527// public_hash [475055ec71ad48f5dfaf875b0fea597b5cbbee64]
528// private_hash [7f76dae3f91bb48f80f1871856d9d519770b7f8a]
529// public_location "database" "keystore"
530// private_location "keystore"
531//
532// name "njs@pobox.com"
533// public_hash [de84b575d5e47254393eba49dce9dc4db98ed42d]
534// public_location "database"
535//
536// name "foo@bar.com"
537// public_hash [7b6ce0bd83240438e7a8c7c207d8654881b763f6]
538// private_hash [bfc3263e3257087f531168850801ccefc668312d]
539// public_location "keystore"
540// private_location "keystore"
541//
542// Error conditions: None.
543CMD_AUTOMATE(keys, "",
544 N_("Lists all keys in the keystore"),
545 "",
546 options::opts::none)
547{
548 N(args.size() == 0,
549 F("no arguments needed"));
550
551 vector<rsa_keypair_id> dbkeys;
552 vector<rsa_keypair_id> kskeys;
553 // public_hash, private_hash, public_location, private_location
554 map<string, boost::tuple<hexenc<id>, hexenc<id>,
555 vector<string>,
556 vector<string> > > items;
557 if (app.db.database_specified())
558 {
559 transaction_guard guard(app.db, false);
560 app.db.get_key_ids(dbkeys);
561 guard.commit();
562 }
563 app.keys.get_key_ids(kskeys);
564
565 for (vector<rsa_keypair_id>::iterator i = dbkeys.begin();
566 i != dbkeys.end(); i++)
567 {
568 base64<rsa_pub_key> pub_encoded;
569 hexenc<id> hash_code;
570
571 app.db.get_key(*i, pub_encoded);
572 key_hash_code(*i, pub_encoded, hash_code);
573 items[(*i)()].get<0>() = hash_code;
574 items[(*i)()].get<2>().push_back("database");
575 }
576
577 for (vector<rsa_keypair_id>::iterator i = kskeys.begin();
578 i != kskeys.end(); i++)
579 {
580 keypair kp;
581 hexenc<id> privhash, pubhash;
582 app.keys.get_key_pair(*i, kp);
583 key_hash_code(*i, kp.pub, pubhash);
584 key_hash_code(*i, kp.priv, privhash);
585 items[(*i)()].get<0>() = pubhash;
586 items[(*i)()].get<1>() = privhash;
587 items[(*i)()].get<2>().push_back("keystore");
588 items[(*i)()].get<3>().push_back("keystore");
589 }
590 basic_io::printer prt;
591 for (map<string, boost::tuple<hexenc<id>, hexenc<id>,
592 vector<string>,
593 vector<string> > >::iterator
594 i = items.begin(); i != items.end(); ++i)
595 {
596 basic_io::stanza stz;
597 stz.push_str_pair(syms::name, i->first);
598 stz.push_hex_pair(syms::public_hash, i->second.get<0>());
599 if (!i->second.get<1>()().empty())
600 stz.push_hex_pair(syms::private_hash, i->second.get<1>());
601 stz.push_str_multi(syms::public_location, i->second.get<2>());
602 if (!i->second.get<3>().empty())
603 stz.push_str_multi(syms::private_location, i->second.get<3>());
604 prt.print_stanza(stz);
605 }
606 output.write(prt.buf.data(), prt.buf.size());
607}
608
609// Name: certs
610// Arguments:
611// 1: a revision id
612// Added in: 1.0
613// Purpose: Prints all certificates associated with the given revision
614// ID. Each certificate is contained in a basic IO stanza. For each
615// certificate, the following values are provided:
616//
617// 'key' : a string indicating the key used to sign this certificate.
618// 'signature': a string indicating the status of the signature.
619// Possible values of this string are:
620// 'ok' : the signature is correct
621// 'bad' : the signature is invalid
622// 'unknown' : signature was made with an unknown key
623// 'name' : the name of this certificate
624// 'value' : the value of this certificate
625// 'trust' : is this certificate trusted by the defined trust metric
626// Possible values of this string are:
627// 'trusted' : this certificate is trusted
628// 'untrusted' : this certificate is not trusted
629//
630// Output format: All stanzas are formatted by basic_io. Stanzas are
631// seperated by a blank line. Values will be escaped, '\' -> '\\' and
632// '"' -> '\"'.
633//
634// Error conditions: If a certificate is signed with an unknown public
635// key, a warning message is printed to stderr. If the revision
636// specified is unknown or invalid prints an error message to stderr
637// and exits with status 1.
638CMD_AUTOMATE(certs, N_("REV"),
639 N_("Prints all certificates attached to a revision"),
640 "",
641 options::opts::none)
642{
643 N(args.size() == 1,
644 F("wrong argument count"));
645
646 vector<cert> certs;
647
648 transaction_guard guard(app.db, false);
649
650 revision_id rid(idx(args, 0)());
651 N(app.db.revision_exists(rid), F("No such revision %s") % rid);
652 hexenc<id> ident(rid.inner());
653
654 vector< revision<cert> > ts;
655 // FIXME_PROJECTS: after projects are implemented,
656 // use the app.db version instead if no project is specified.
657 app.get_project().get_revision_certs(rid, ts);
658
659 for (size_t i = 0; i < ts.size(); ++i)
660 certs.push_back(idx(ts, i).inner());
661
662 {
663 set<rsa_keypair_id> checked;
664 for (size_t i = 0; i < certs.size(); ++i)
665 {
666 if (checked.find(idx(certs, i).key) == checked.end() &&
667 !app.db.public_key_exists(idx(certs, i).key))
668 W(F("no public key '%s' found in database")
669 % idx(certs, i).key);
670 checked.insert(idx(certs, i).key);
671 }
672 }
673
674 // Make the output deterministic; this is useful for the test suite,
675 // in particular.
676 sort(certs.begin(), certs.end());
677
678 basic_io::printer pr;
679
680 for (size_t i = 0; i < certs.size(); ++i)
681 {
682 basic_io::stanza st;
683 cert_status status = check_cert(app, idx(certs, i));
684 cert_value tv;
685 cert_name name = idx(certs, i).name;
686 set<rsa_keypair_id> signers;
687
688 decode_base64(idx(certs, i).value, tv);
689
690 rsa_keypair_id keyid = idx(certs, i).key;
691 signers.insert(keyid);
692
693 bool trusted =
694 app.lua.hook_get_revision_cert_trust(signers, ident,
695 name, tv);
696
697 st.push_str_pair(syms::key, keyid());
698
699 string stat;
700 switch (status)
701 {
702 case cert_ok:
703 stat = "ok";
704 break;
705 case cert_bad:
706 stat = "bad";
707 break;
708 case cert_unknown:
709 stat = "unknown";
710 break;
711 }
712 st.push_str_pair(syms::signature, stat);
713
714 st.push_str_pair(syms::name, name());
715 st.push_str_pair(syms::value, tv());
716 st.push_str_pair(syms::trust, (trusted ? "trusted" : "untrusted"));
717
718 pr.print_stanza(st);
719 }
720 output.write(pr.buf.data(), pr.buf.size());
721
722 guard.commit();
723}
724
725
726// Local Variables:
727// mode: C++
728// fill-column: 76
729// c-file-style: "gnu"
730// indent-tabs-mode: nil
731// End:
732// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status