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