Imported Upstream version 0.19.1
[platform/upstream/p11-kit.git] / tools / tool.c
1 /*
2  * Copyright (c) 2011, Collabora Ltd.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  *     * Redistributions of source code must retain the above
9  *       copyright notice, this list of conditions and the
10  *       following disclaimer.
11  *     * Redistributions in binary form must reproduce the
12  *       above copyright notice, this list of conditions and
13  *       the following disclaimer in the documentation and/or
14  *       other materials provided with the distribution.
15  *     * The names of contributors to this software may not be
16  *       used to endorse or promote products derived from this
17  *       software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
29  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
30  * DAMAGE.
31  *
32  * Author: Stef Walter <stefw@collabora.co.uk>
33  */
34
35 #include "config.h"
36
37 #include "buffer.h"
38 #include "compat.h"
39 #include "debug.h"
40 #include "message.h"
41 #include "path.h"
42 #include "p11-kit.h"
43
44 #include <assert.h>
45 #include <ctype.h>
46 #include <getopt.h>
47 #include <string.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <unistd.h>
51
52 #include "tool.h"
53
54 struct {
55         const char *name;
56         int (*function) (int, char*[]);
57         const char *text;
58 } commands[] = {
59 #ifdef WITH_ASN1
60         { "extract", p11_tool_extract, "Extract certificates" },
61 #endif
62         { "list-modules", p11_tool_list_modules, "List modules and tokens"},
63         { 0, }
64 };
65
66 static char
67 short_option (int opt)
68 {
69         if (isalpha (opt) || isdigit (opt))
70                 return (char)opt;
71         return 0;
72 }
73
74 static const struct option *
75 find_option (const struct option *longopts,
76              int opt)
77 {
78         int i;
79
80         for (i = 0; longopts[i].name != NULL; i++) {
81                 if (longopts[i].val == opt)
82                         return longopts + i;
83         }
84
85         return NULL;
86 }
87
88 void
89 p11_tool_usage (const p11_tool_desc *usages,
90                 const struct option *longopts)
91 {
92         const struct option *longopt;
93         const int indent = 22;
94         const char *long_name;
95         const char *description;
96         const char *next;
97         char short_name;
98         int spaces;
99         int len;
100         int i;
101
102         for (i = 0; usages[i].text != NULL; i++) {
103
104                 /* If no option, then this is a heading */
105                 if (!usages[i].option) {
106                         printf ("%s\n\n", usages[i].text);
107                         continue;
108                 }
109
110                 longopt = find_option (longopts, usages[i].option);
111                 long_name = longopt ? longopt->name : NULL;
112                 short_name = short_option (usages[i].option);
113                 description = usages[i].text;
114
115                 if (short_name && long_name)
116                         len = printf ("  -%c, --%s", (int)short_name, long_name);
117                 else if (long_name)
118                         len = printf ("  --%s", long_name);
119                 else
120                         len = printf ("  -%c", (int)short_name);
121                 if (longopt && longopt->has_arg)
122                         len += printf ("%s<%s>",
123                                        long_name ? "=" : " ",
124                                        usages[i].arg ? usages[i].arg : "...");
125                 if (len < indent) {
126                         spaces = indent - len;
127                 } else {
128                         printf ("\n");
129                         spaces = indent;
130                 }
131                 while (description) {
132                         while (spaces-- > 0)
133                                 fputc (' ', stdout);
134                         next = strchr (description, '\n');
135                         if (next) {
136                                 next += 1;
137                                 printf ("%.*s", (int)(next - description), description);
138                                 description = next;
139                                 spaces = indent;
140                         } else {
141                                 printf ("%s\n", description);
142                                 break;
143                         }
144                 }
145
146         }
147 }
148
149 int
150 p11_tool_getopt (int argc,
151                  char *argv[],
152                  const struct option *longopts)
153 {
154         p11_buffer buf;
155         int ret;
156         char opt;
157         int i;
158
159         if (!p11_buffer_init_null (&buf, 64))
160                 return_val_if_reached (-1);
161
162         for (i = 0; longopts[i].name != NULL; i++) {
163                 opt = short_option (longopts[i].val);
164                 if (opt != 0) {
165                         p11_buffer_add (&buf, &opt, 1);
166                         assert (longopts[i].has_arg != optional_argument);
167                         if (longopts[i].has_arg == required_argument)
168                                 p11_buffer_add (&buf, ":", 1);
169                 }
170         }
171
172         ret = getopt_long (argc, argv, buf.data, longopts, NULL);
173
174         p11_buffer_uninit (&buf);
175
176         return ret;
177 }
178
179 static void
180 command_usage (void)
181 {
182         int i;
183
184         printf ("usage: p11-kit command <args>...\n");
185         printf ("\nCommon p11-kit commands are:\n");
186         for (i = 0; commands[i].name != NULL; i++)
187                 printf ("  %-15s  %s\n", commands[i].name, commands[i].text);
188         printf ("\nSee 'p11-kit <command> --help' for more information\n");
189 }
190
191 static void
192 exec_external (const char *command,
193                int argc,
194                char *argv[])
195 {
196         char *filename;
197         const char *path;
198         char *env;
199
200         if (!asprintf (&filename, "p11-kit-%s", command) < 0)
201                 return_if_reached ();
202
203         /* Add our libexec directory to the path */
204         path = getenv ("PATH");
205         if (!asprintf (&env, "PATH=%s%s%s", path ? path : "", path ? P11_PATH_SEP : "", PRIVATEDIR))
206                 return_if_reached ();
207         putenv (env);
208
209         argv[0] = filename;
210         execvp (filename, argv);
211 }
212
213 static void
214 verbose_arg (void)
215 {
216         putenv ("P11_KIT_DEBUG=all");
217         p11_kit_be_loud ();
218         p11_message_loud ();
219 }
220
221 static void
222 quiet_arg (void)
223 {
224         putenv ("P11_KIT_DEBUG=");
225         p11_kit_be_quiet ();
226         p11_message_quiet ();
227 }
228
229 int
230 main (int argc, char *argv[])
231 {
232         char *command = NULL;
233         bool want_help = false;
234         bool skip;
235         int in, out;
236         int i;
237
238         /*
239          * Parse the global options. We rearrange the options as
240          * necessary, in order to pass relevant options through
241          * to the commands, but also have them take effect globally.
242          */
243
244         for (in = 1, out = 1; in < argc; in++, out++) {
245
246                 /* The non-option is the command, take it out of the arguments */
247                 if (argv[in][0] != '-') {
248                         if (!command) {
249                                 skip = true;
250                                 command = argv[in];
251                         }
252
253                 /* The global long options */
254                 } else if (argv[in][1] == '-') {
255                         skip = false;
256
257                         if (strcmp (argv[in], "--") == 0) {
258                                 if (!command) {
259                                         p11_message ("no command specified");
260                                         return 2;
261                                 } else {
262                                         break;
263                                 }
264
265                         } else if (strcmp (argv[in], "--verbose") == 0) {
266                                 verbose_arg ();
267
268                         } else if (strcmp (argv[in], "--quiet") == 0) {
269                                 quiet_arg ();
270
271                         } else if (strcmp (argv[in], "--help") == 0) {
272                                 want_help = true;
273
274                         } else if (!command) {
275                                 p11_message ("unknown global option: %s", argv[in]);
276                                 return 2;
277                         }
278
279                 /* The global short options */
280                 } else {
281                         skip = false;
282
283                         for (i = 1; argv[in][i] != '\0'; i++) {
284                                 switch (argv[in][i]) {
285                                 case 'h':
286                                         want_help = true;
287                                         break;
288
289                                 /* Compatibility option */
290                                 case 'l':
291                                         command = "list-modules";
292                                         break;
293
294                                 case 'v':
295                                         verbose_arg ();
296                                         break;
297
298                                 case 'q':
299                                         quiet_arg ();
300                                         break;
301
302                                 default:
303                                         if (!command) {
304                                                 p11_message ("unknown global option: -%c", (int)argv[in][i]);
305                                                 return 2;
306                                         }
307                                         break;
308                                 }
309                         }
310                 }
311
312                 /* Skipping this argument? */
313                 if (skip)
314                         out--;
315                 else
316                         argv[out] = argv[in];
317         }
318
319         /* Initialize tool's debugging after setting env vars above */
320         p11_debug_init ();
321
322         if (command == NULL) {
323                 /* As a special favor if someone just typed 'p11-kit', help them out */
324                 if (argc == 1) {
325                         command_usage ();
326                         return 2;
327                 } else if (want_help) {
328                         command_usage ();
329                         return 0;
330                 } else {
331                         p11_message ("no command specified");
332                         return 2;
333                 }
334         }
335
336         argc = out;
337
338         /* Look for the command */
339         for (i = 0; commands[i].name != NULL; i++) {
340                 if (strcmp (commands[i].name, command) == 0) {
341                         argv[0] = command;
342                         return (commands[i].function) (argc, argv);
343                 }
344         }
345
346         /* Got here because no command matched */
347         exec_external (command, argc, argv);
348
349         /* At this point we have no command */
350         p11_message ("'%s' is not a valid p11-kit command. See 'p11-kit --help'", command);
351         return 2;
352 }