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 string pattern;
162 if (args.size() == 1)
163 pattern = 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 globish exc;
255 if (args.size() == 1)
256 inc = globish(idx(args,0)());
257 else if (args.size() > 1)
258 throw usage(execid);
259 vector<globish> excludes;
260 typecast_vocab_container(app.opts.exclude_patterns, excludes);
261 combine_and_check_globish(excludes, exc);
262 globish_matcher match(inc, exc);
263 set<branch_name> names;
264 app.get_project().get_branch_list(names);
265
266 for (set<branch_name>::const_iterator i = names.begin();
267 i != names.end(); ++i)
268 {
269 if (match((*i)()) && !app.lua.hook_ignore_branch(*i))
270 {
271 cout << *i << '\n';
272 }
273 }
274}
275
276CMD(epochs, "epochs", "", CMD_REF(list), "[BRANCH [...]]",
277 N_("Lists the current epoch of branches that match a pattern"),
278 "",
279 options::opts::depth | options::opts::exclude)
280{
281 map<branch_name, epoch_data> epochs;
282 app.db.get_epochs(epochs);
283
284 if (args.size() == 0)
285 {
286 for (map<branch_name, epoch_data>::const_iterator
287 i = epochs.begin();
288 i != epochs.end(); ++i)
289 {
290 cout << i->second << ' ' << i->first << '\n';
291 }
292 }
293 else
294 {
295 for (args_vector::const_iterator i = args.begin();
296 i != args.end();
297 ++i)
298 {
299 map<branch_name, epoch_data>::const_iterator j = epochs.find(branch_name((*i)()));
300 N(j != epochs.end(), F("no epoch for branch %s") % *i);
301 cout << j->second << ' ' << j->first << '\n';
302 }
303 }
304}
305
306CMD(tags, "tags", "", CMD_REF(list), "",
307 N_("Lists all tags in the database"),
308 "",
309 options::opts::depth | options::opts::exclude)
310{
311 set<tag_t> tags;
312 app.get_project().get_tags(tags);
313
314 for (set<tag_t>::const_iterator i = tags.begin(); i != tags.end(); ++i)
315 {
316 cout << i->name << ' '
317 << i->ident << ' '
318 << i->key << '\n';
319 }
320}
321
322CMD(vars, "vars", "", CMD_REF(list), "[DOMAIN]",
323 N_("Lists variables in the whole database or a domain"),
324 "",
325 options::opts::depth | options::opts::exclude)
326{
327 bool filterp;
328 var_domain filter;
329 if (args.size() == 0)
330 {
331 filterp = false;
332 }
333 else if (args.size() == 1)
334 {
335 filterp = true;
336 internalize_var_domain(idx(args, 0), filter);
337 }
338 else
339 throw usage(execid);
340
341 map<var_key, var_value> vars;
342 app.db.get_vars(vars);
343 for (map<var_key, var_value>::const_iterator i = vars.begin();
344 i != vars.end(); ++i)
345 {
346 if (filterp && !(i->first.first == filter))
347 continue;
348 external ext_domain, ext_name;
349 externalize_var_domain(i->first.first, ext_domain);
350 cout << ext_domain << ": "
351 << i->first.second << ' '
352 << i->second << '\n';
353 }
354}
355
356CMD(known, "known", "", CMD_REF(list), "",
357 N_("Lists workspace files that belong to the current branch"),
358 "",
359 options::opts::depth | options::opts::exclude)
360{
361 roster_t new_roster;
362 temp_node_id_source nis;
363
364 app.require_workspace();
365 app.work.get_current_roster_shape(new_roster, nis);
366
367 node_restriction mask(args_to_paths(args),
368 args_to_paths(app.opts.exclude_patterns),
369 app.opts.depth,
370 new_roster, app);
371
372 // to be printed sorted
373 vector<file_path> print_paths;
374
375 node_map const & nodes = new_roster.all_nodes();
376 for (node_map::const_iterator i = nodes.begin();
377 i != nodes.end(); ++i)
378 {
379 node_id nid = i->first;
380
381 if (!new_roster.is_root(nid)
382 && mask.includes(new_roster, nid))
383 {
384 file_path p;
385 new_roster.get_name(nid, p);
386 print_paths.push_back(p);
387 }
388 }
389
390 sort(print_paths.begin(), print_paths.end());
391 copy(print_paths.begin(), print_paths.end(),
392 ostream_iterator<file_path>(cout, "\n"));
393}
394
395CMD(unknown, "unknown", "ignored", CMD_REF(list), "",
396 N_("Lists workspace files that do not belong to the current branch"),
397 "",
398 options::opts::depth | options::opts::exclude)
399{
400 app.require_workspace();
401
402 vector<file_path> roots = args_to_paths(args);
403 path_restriction mask(roots, args_to_paths(app.opts.exclude_patterns),
404 app.opts.depth, app);
405 set<file_path> unknown, ignored;
406
407 // if no starting paths have been specified use the workspace root
408 if (roots.empty())
409 roots.push_back(file_path());
410
411 app.work.find_unknown_and_ignored(mask, roots, unknown, ignored);
412
413 utf8 const & realname = execid[execid.size() - 1];
414 if (realname() == "ignored")
415 copy(ignored.begin(), ignored.end(),
416 ostream_iterator<file_path>(cout, "\n"));
417 else
418 {
419 I(realname() == "unknown");
420 copy(unknown.begin(), unknown.end(),
421 ostream_iterator<file_path>(cout, "\n"));
422 }
423}
424
425CMD(missing, "missing", "", CMD_REF(list), "",
426 N_("Lists files that belong to the branch but are not in the workspace"),
427 "",
428 options::opts::depth | options::opts::exclude)
429{
430 temp_node_id_source nis;
431 roster_t current_roster_shape;
432 app.work.get_current_roster_shape(current_roster_shape, nis);
433 node_restriction mask(args_to_paths(args),
434 args_to_paths(app.opts.exclude_patterns),
435 app.opts.depth,
436 current_roster_shape, app);
437
438 set<file_path> missing;
439 app.work.find_missing(current_roster_shape, mask, missing);
440
441 copy(missing.begin(), missing.end(),
442 ostream_iterator<file_path>(cout, "\n"));
443}
444
445
446CMD(changed, "changed", "", CMD_REF(list), "",
447 N_("Lists files that have changed with respect to the current revision"),
448 "",
449 options::opts::depth | options::opts::exclude)
450{
451 parent_map parents;
452 roster_t new_roster;
453 temp_node_id_source nis;
454
455 app.require_workspace();
456
457 app.work.get_current_roster_shape(new_roster, nis);
458 app.work.update_current_roster_from_filesystem(new_roster);
459
460 app.work.get_parent_rosters(parents);
461
462 node_restriction mask(args_to_paths(args),
463 args_to_paths(app.opts.exclude_patterns),
464 app.opts.depth,
465 parents, new_roster, app);
466
467 revision_t rrev;
468 make_restricted_revision(parents, new_roster, mask, rrev);
469
470 // to be printed sorted, with duplicates removed
471 set<file_path> print_paths;
472
473 for (edge_map::const_iterator i = rrev.edges.begin();
474 i != rrev.edges.end(); i++)
475 {
476 set<node_id> nodes;
477 roster_t const & old_roster
478 = *safe_get(parents, edge_old_revision(i)).first;
479 select_nodes_modified_by_cset(edge_changes(i),
480 old_roster, new_roster, nodes);
481
482 for (set<node_id>::const_iterator i = nodes.begin(); i != nodes.end();
483 ++i)
484 {
485 file_path p;
486 if (new_roster.has_node(*i))
487 new_roster.get_name(*i, p);
488 else
489 old_roster.get_name(*i, p);
490 print_paths.insert(p);
491 }
492 }
493
494 copy(print_paths.begin(), print_paths.end(),
495 ostream_iterator<file_path>(cout, "\n"));
496}
497
498namespace
499{
500 namespace syms
501 {
502 symbol const key("key");
503 symbol const signature("signature");
504 symbol const name("name");
505 symbol const value("value");
506 symbol const trust("trust");
507
508 symbol const public_hash("public_hash");
509 symbol const private_hash("private_hash");
510 symbol const public_location("public_location");
511 symbol const private_location("private_location");
512 }
513};
514
515// Name: keys
516// Arguments: none
517// Added in: 1.1
518// Purpose: Prints all keys in the keystore, and if a database is given
519// also all keys in the database, in basic_io format.
520// Output format: For each key, a basic_io stanza is printed. The items in
521// the stanza are:
522// name - the key identifier
523// public_hash - the hash of the public half of the key
524// private_hash - the hash of the private half of the key
525// public_location - where the public half of the key is stored
526// private_location - where the private half of the key is stored
527// The *_location items may have multiple values, as shown below
528// for public_location.
529// If the private key does not exist, then the private_hash and
530// private_location items will be absent.
531//
532// Sample output:
533// name "tbrownaw@gmail.com"
534// public_hash [475055ec71ad48f5dfaf875b0fea597b5cbbee64]
535// private_hash [7f76dae3f91bb48f80f1871856d9d519770b7f8a]
536// public_location "database" "keystore"
537// private_location "keystore"
538//
539// name "njs@pobox.com"
540// public_hash [de84b575d5e47254393eba49dce9dc4db98ed42d]
541// public_location "database"
542//
543// name "foo@bar.com"
544// public_hash [7b6ce0bd83240438e7a8c7c207d8654881b763f6]
545// private_hash [bfc3263e3257087f531168850801ccefc668312d]
546// public_location "keystore"
547// private_location "keystore"
548//
549// Error conditions: None.
550CMD_AUTOMATE(keys, "",
551 N_("Lists all keys in the keystore"),
552 "",
553 options::opts::none)
554{
555 N(args.size() == 0,
556 F("no arguments needed"));
557
558 vector<rsa_keypair_id> dbkeys;
559 vector<rsa_keypair_id> kskeys;
560 // public_hash, private_hash, public_location, private_location
561 map<string, boost::tuple<hexenc<id>, hexenc<id>,
562 vector<string>,
563 vector<string> > > items;
564 if (app.db.database_specified())
565 {
566 transaction_guard guard(app.db, false);
567 app.db.get_key_ids("", dbkeys);
568 guard.commit();
569 }
570 app.keys.get_key_ids("", kskeys);
571
572 for (vector<rsa_keypair_id>::iterator i = dbkeys.begin();
573 i != dbkeys.end(); i++)
574 {
575 base64<rsa_pub_key> pub_encoded;
576 hexenc<id> hash_code;
577
578 app.db.get_key(*i, pub_encoded);
579 key_hash_code(*i, pub_encoded, hash_code);
580 items[(*i)()].get<0>() = hash_code;
581 items[(*i)()].get<2>().push_back("database");
582 }
583
584 for (vector<rsa_keypair_id>::iterator i = kskeys.begin();
585 i != kskeys.end(); i++)
586 {
587 keypair kp;
588 hexenc<id> privhash, pubhash;
589 app.keys.get_key_pair(*i, kp);
590 key_hash_code(*i, kp.pub, pubhash);
591 key_hash_code(*i, kp.priv, privhash);
592 items[(*i)()].get<0>() = pubhash;
593 items[(*i)()].get<1>() = privhash;
594 items[(*i)()].get<2>().push_back("keystore");
595 items[(*i)()].get<3>().push_back("keystore");
596 }
597 basic_io::printer prt;
598 for (map<string, boost::tuple<hexenc<id>, hexenc<id>,
599 vector<string>,
600 vector<string> > >::iterator
601 i = items.begin(); i != items.end(); ++i)
602 {
603 basic_io::stanza stz;
604 stz.push_str_pair(syms::name, i->first);
605 stz.push_hex_pair(syms::public_hash, i->second.get<0>());
606 if (!i->second.get<1>()().empty())
607 stz.push_hex_pair(syms::private_hash, i->second.get<1>());
608 stz.push_str_multi(syms::public_location, i->second.get<2>());
609 if (!i->second.get<3>().empty())
610 stz.push_str_multi(syms::private_location, i->second.get<3>());
611 prt.print_stanza(stz);
612 }
613 output.write(prt.buf.data(), prt.buf.size());
614}
615
616// Name: certs
617// Arguments:
618// 1: a revision id
619// Added in: 1.0
620// Purpose: Prints all certificates associated with the given revision
621// ID. Each certificate is contained in a basic IO stanza. For each
622// certificate, the following values are provided:
623//
624// 'key' : a string indicating the key used to sign this certificate.
625// 'signature': a string indicating the status of the signature.
626// Possible values of this string are:
627// 'ok' : the signature is correct
628// 'bad' : the signature is invalid
629// 'unknown' : signature was made with an unknown key
630// 'name' : the name of this certificate
631// 'value' : the value of this certificate
632// 'trust' : is this certificate trusted by the defined trust metric
633// Possible values of this string are:
634// 'trusted' : this certificate is trusted
635// 'untrusted' : this certificate is not trusted
636//
637// Output format: All stanzas are formatted by basic_io. Stanzas are
638// seperated by a blank line. Values will be escaped, '\' -> '\\' and
639// '"' -> '\"'.
640//
641// Error conditions: If a certificate is signed with an unknown public
642// key, a warning message is printed to stderr. If the revision
643// specified is unknown or invalid prints an error message to stderr
644// and exits with status 1.
645CMD_AUTOMATE(certs, N_("REV"),
646 N_("Prints all certificates attached to a revision"),
647 "",
648 options::opts::none)
649{
650 N(args.size() == 1,
651 F("wrong argument count"));
652
653 vector<cert> certs;
654
655 transaction_guard guard(app.db, false);
656
657 revision_id rid(idx(args, 0)());
658 N(app.db.revision_exists(rid), F("No such revision %s") % rid);
659 hexenc<id> ident(rid.inner());
660
661 vector< revision<cert> > ts;
662 // FIXME_PROJECTS: after projects are implemented,
663 // use the app.db version instead if no project is specified.
664 app.get_project().get_revision_certs(rid, ts);
665
666 for (size_t i = 0; i < ts.size(); ++i)
667 certs.push_back(idx(ts, i).inner());
668
669 {
670 set<rsa_keypair_id> checked;
671 for (size_t i = 0; i < certs.size(); ++i)
672 {
673 if (checked.find(idx(certs, i).key) == checked.end() &&
674 !app.db.public_key_exists(idx(certs, i).key))
675 W(F("no public key '%s' found in database")
676 % idx(certs, i).key);
677 checked.insert(idx(certs, i).key);
678 }
679 }
680
681 // Make the output deterministic; this is useful for the test suite,
682 // in particular.
683 sort(certs.begin(), certs.end());
684
685 basic_io::printer pr;
686
687 for (size_t i = 0; i < certs.size(); ++i)
688 {
689 basic_io::stanza st;
690 cert_status status = check_cert(app, idx(certs, i));
691 cert_value tv;
692 cert_name name = idx(certs, i).name;
693 set<rsa_keypair_id> signers;
694
695 decode_base64(idx(certs, i).value, tv);
696
697 rsa_keypair_id keyid = idx(certs, i).key;
698 signers.insert(keyid);
699
700 bool trusted =
701 app.lua.hook_get_revision_cert_trust(signers, ident,
702 name, tv);
703
704 st.push_str_pair(syms::key, keyid());
705
706 string stat;
707 switch (status)
708 {
709 case cert_ok:
710 stat = "ok";
711 break;
712 case cert_bad:
713 stat = "bad";
714 break;
715 case cert_unknown:
716 stat = "unknown";
717 break;
718 }
719 st.push_str_pair(syms::signature, stat);
720
721 st.push_str_pair(syms::name, name());
722 st.push_str_pair(syms::value, tv());
723 st.push_str_pair(syms::trust, (trusted ? "trusted" : "untrusted"));
724
725 pr.print_stanza(st);
726 }
727 output.write(pr.buf.data(), pr.buf.size());
728
729 guard.commit();
730}
731
732
733// Local Variables:
734// mode: C++
735// fill-column: 76
736// c-file-style: "gnu"
737// indent-tabs-mode: nil
738// End:
739// 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