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