]>
Commit | Line | Data |
---|---|---|
83893f26 SK |
1 | /* |
2 | * uuidparse.c --- Interpret uuid encoded information. This program | |
3 | * violates the UUID abstraction barrier by reaching into the | |
4 | * guts of a UUID. | |
5 | * | |
6 | * Based on libuuid/src/uuid_time.c | |
7 | * Copyright (C) 1998, 1999 Theodore Ts'o. | |
8 | * | |
9 | * All alterations (C) 2017 Sami Kerola | |
10 | * The 3-Clause BSD License | |
11 | * | |
12 | * Redistribution and use in source and binary forms, with or without | |
13 | * modification, are permitted provided that the following conditions | |
14 | * are met: | |
15 | * 1. Redistributions of source code must retain the above copyright | |
16 | * notice, and the entire permission notice in its entirety, | |
17 | * including the disclaimer of warranties. | |
18 | * 2. Redistributions in binary form must reproduce the above copyright | |
19 | * notice, this list of conditions and the following disclaimer in the | |
20 | * documentation and/or other materials provided with the distribution. | |
21 | * 3. The name of the author may not be used to endorse or promote | |
22 | * products derived from this software without specific prior | |
23 | * written permission. | |
24 | * | |
25 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
26 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
27 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF | |
28 | * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE | |
29 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
30 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT | |
31 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | |
32 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
33 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
34 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE | |
35 | * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH | |
36 | * DAMAGE. | |
37 | */ | |
38 | ||
39 | #include <assert.h> | |
40 | #include <getopt.h> | |
41 | #include <libsmartcols.h> | |
42 | #include <stdint.h> | |
43 | #include <stdio.h> | |
44 | #include <stdlib.h> | |
45 | #include <string.h> | |
46 | #include <time.h> | |
47 | #include <unistd.h> | |
48 | ||
49 | #include "c.h" | |
50 | #include "closestream.h" | |
51 | #include "nls.h" | |
52 | #include "optutils.h" | |
53 | #include "strutils.h" | |
54 | #include "timeutils.h" | |
55 | #include "uuid.h" | |
56 | #include "xalloc.h" | |
57 | ||
58 | #define UUID_STR_LEN 37 | |
59 | ||
60 | /* column IDs */ | |
61 | enum { | |
62 | COL_UUID = 0, | |
63 | COL_VARIANT, | |
64 | COL_TYPE, | |
65 | COL_TIME | |
66 | }; | |
67 | ||
68 | /* column names */ | |
69 | struct colinfo { | |
70 | const char *name; /* header */ | |
71 | double whint; /* width hint (N < 1 is in percent of termwidth) */ | |
72 | int flags; /* SCOLS_FL_* */ | |
73 | const char *help; | |
74 | }; | |
75 | ||
76 | /* columns descriptions */ | |
77 | static const struct colinfo infos[] = { | |
78 | [COL_UUID] = {"UUID", UUID_STR_LEN, 0, N_("unique identifier")}, | |
79 | [COL_VARIANT] = {"VARIANT", 9, 0, N_("variant name")}, | |
80 | [COL_TYPE] = {"TYPE", 10, 0, N_("type name")}, | |
81 | [COL_TIME] = {"TIME", 31, 0, N_("timestamp")} | |
82 | }; | |
83 | ||
84 | static int columns[ARRAY_SIZE(infos) * 2]; | |
85 | static size_t ncolumns; | |
86 | ||
87 | struct control { | |
88 | unsigned int | |
89 | json:1, | |
90 | no_headings:1, | |
91 | raw:1; | |
92 | }; | |
93 | ||
94 | static void __attribute__((__noreturn__)) usage(void) | |
95 | { | |
96 | size_t i; | |
97 | ||
98 | fputs(USAGE_HEADER, stdout); | |
99 | fprintf(stdout, _(" %s [options] <uuid ...>\n"), program_invocation_short_name); | |
100 | ||
101 | fputs(USAGE_OPTIONS, stdout); | |
4c2af168 KZ |
102 | puts(_(" -J, --json use JSON output format")); |
103 | puts(_(" -n, --noheadings don't print headings")); | |
104 | puts(_(" -o, --output <list> COLUMNS to display (see below)")); | |
105 | puts(_(" -r, --raw use the raw output format")); | |
f45f3ec3 | 106 | printf(USAGE_HELP_OPTIONS(24)); |
83893f26 SK |
107 | |
108 | fputs(USAGE_COLUMNS, stdout); | |
109 | for (i = 0; i < ARRAY_SIZE(infos); i++) | |
110 | fprintf(stdout, " %8s %s\n", infos[i].name, _(infos[i].help)); | |
111 | ||
f45f3ec3 | 112 | printf(USAGE_MAN_TAIL("uuidparse(1)")); |
83893f26 SK |
113 | exit(EXIT_SUCCESS); |
114 | } | |
115 | ||
116 | static int column_name_to_id(const char *name, size_t namesz) | |
117 | { | |
118 | size_t i; | |
119 | ||
120 | assert(name); | |
121 | ||
122 | for (i = 0; i < ARRAY_SIZE(infos); i++) { | |
123 | const char *cn = infos[i].name; | |
124 | if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) | |
125 | return i; | |
126 | } | |
127 | warnx(_("unknown column: %s"), name); | |
128 | return -1; | |
129 | } | |
130 | ||
131 | static int get_column_id(size_t num) | |
132 | { | |
133 | assert(num < ncolumns); | |
134 | assert(columns[num] < (int)ARRAY_SIZE(infos)); | |
135 | return columns[num]; | |
136 | } | |
137 | ||
138 | static const struct colinfo *get_column_info(int num) | |
139 | { | |
140 | return &infos[get_column_id(num)]; | |
141 | } | |
142 | ||
143 | static void fill_table_row(struct libscols_table *tb, char const *const uuid) | |
144 | { | |
145 | static struct libscols_line *ln; | |
146 | size_t i; | |
147 | uuid_t buf; | |
148 | int invalid = 0; | |
149 | int variant, type; | |
150 | ||
151 | assert(tb); | |
152 | assert(uuid); | |
153 | ||
154 | ln = scols_table_new_line(tb, NULL); | |
155 | if (!ln) | |
156 | errx(EXIT_FAILURE, _("failed to allocate output line")); | |
157 | ||
158 | if (uuid_parse(uuid, buf)) | |
159 | invalid = 1; | |
160 | else { | |
161 | variant = uuid_variant(buf); | |
162 | type = uuid_type(buf); | |
163 | } | |
164 | ||
165 | for (i = 0; i < ncolumns; i++) { | |
166 | char *str = NULL; | |
167 | ||
168 | switch (get_column_id(i)) { | |
169 | case COL_UUID: | |
170 | str = xstrdup(uuid); | |
171 | break; | |
172 | case COL_VARIANT: | |
173 | if (invalid) { | |
174 | str = xstrdup(_("invalid")); | |
175 | break; | |
176 | } | |
177 | switch (variant) { | |
178 | case UUID_VARIANT_NCS: | |
179 | str = xstrdup("NCS"); | |
180 | break; | |
181 | case UUID_VARIANT_DCE: | |
182 | str = xstrdup("DCE"); | |
183 | break; | |
184 | case UUID_VARIANT_MICROSOFT: | |
185 | str = xstrdup("Microsoft"); | |
186 | break; | |
187 | default: | |
188 | str = xstrdup(_("other")); | |
189 | } | |
190 | break; | |
191 | case COL_TYPE: | |
192 | if (invalid) { | |
193 | str = xstrdup(_("invalid")); | |
194 | break; | |
195 | } | |
196 | switch (type) { | |
197 | case 0: | |
8cfbd350 SK |
198 | if (strspn(uuid, "0-") == 36) |
199 | str = xstrdup(_("nil")); | |
200 | else | |
201 | str = xstrdup(_("unknown")); | |
83893f26 SK |
202 | break; |
203 | case 1: | |
204 | str = xstrdup(_("time-based")); | |
205 | break; | |
206 | case 2: | |
207 | str = xstrdup("DCE"); | |
208 | break; | |
209 | case 3: | |
210 | str = xstrdup(_("name-based")); | |
211 | break; | |
212 | case 4: | |
213 | str = xstrdup(_("random")); | |
214 | break; | |
215 | case 5: | |
216 | str = xstrdup(_("sha1-based")); | |
217 | break; | |
218 | default: | |
219 | str = xstrdup(_("unknown")); | |
220 | } | |
221 | break; | |
222 | case COL_TIME: | |
223 | if (invalid) { | |
224 | str = xstrdup(_("invalid")); | |
225 | break; | |
226 | } | |
227 | if (variant == UUID_VARIANT_DCE && type == 1) { | |
228 | struct timeval tv; | |
229 | char date_buf[ISO_8601_BUFSIZ + 4]; | |
230 | ||
231 | uuid_time(buf, &tv); | |
232 | strtimeval_iso(&tv, | |
233 | ISO_8601_DATE | | |
234 | ISO_8601_TIME | | |
235 | ISO_8601_COMMAUSEC | | |
236 | ISO_8601_TIMEZONE | | |
237 | ISO_8601_SPACE, | |
238 | date_buf, | |
239 | sizeof(date_buf)); | |
240 | str = xstrdup(date_buf); | |
241 | } | |
242 | break; | |
243 | default: | |
244 | abort(); | |
245 | } | |
246 | if (str && scols_line_refer_data(ln, i, str)) | |
247 | errx(EXIT_FAILURE, _("failed to add output data")); | |
248 | } | |
249 | } | |
250 | ||
251 | static void print_output(struct control const *const ctrl, int argc, | |
252 | char **argv) | |
253 | { | |
254 | struct libscols_table *tb; | |
255 | size_t i; | |
256 | ||
257 | scols_init_debug(0); | |
258 | tb = scols_new_table(); | |
259 | if (!tb) | |
260 | err(EXIT_FAILURE, _("failed to allocate output table")); | |
261 | ||
e8fd8c6b KZ |
262 | if (ctrl->json) { |
263 | scols_table_enable_json(tb, 1); | |
264 | scols_table_set_name(tb, "uuids"); | |
265 | } | |
83893f26 SK |
266 | scols_table_enable_noheadings(tb, ctrl->no_headings); |
267 | scols_table_enable_raw(tb, ctrl->raw); | |
268 | ||
269 | for (i = 0; i < ncolumns; i++) { | |
270 | const struct colinfo *col = get_column_info(i); | |
271 | ||
272 | if (!scols_table_new_column(tb, col->name, col->whint, | |
273 | col->flags)) | |
274 | err(EXIT_FAILURE, | |
275 | _("failed to initialize output column")); | |
276 | } | |
277 | ||
278 | for (i = 0; i < (size_t) argc; i++) | |
279 | fill_table_row(tb, argv[i]); | |
280 | ||
281 | if (i == 0) { | |
282 | char uuid[UUID_STR_LEN]; | |
283 | ||
284 | while (scanf(" %" stringify_value(UUID_STR_LEN) | |
285 | "[^ \t\n]%*c", uuid) && !feof(stdin)) | |
286 | fill_table_row(tb, uuid); | |
287 | } | |
288 | scols_print_table(tb); | |
289 | scols_unref_table(tb); | |
290 | } | |
291 | ||
292 | int main(int argc, char **argv) | |
293 | { | |
294 | struct control ctrl = { 0 }; | |
295 | char *outarg = NULL; | |
296 | int c; | |
297 | ||
298 | static const struct option longopts[] = { | |
299 | {"json", no_argument, NULL, 'J'}, | |
300 | {"noheadings", no_argument, NULL, 'n'}, | |
301 | {"output", required_argument, NULL, 'o'}, | |
302 | {"raw", no_argument, NULL, 'r'}, | |
303 | {"version", no_argument, NULL, 'V'}, | |
304 | {"help", no_argument, NULL, 'h'}, | |
f2bd6881 | 305 | {NULL, 0, NULL, 0} |
83893f26 SK |
306 | }; |
307 | static const ul_excl_t excl[] = { | |
308 | {'J', 'r'}, | |
309 | {0} | |
310 | }; | |
311 | int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; | |
312 | ||
313 | setlocale(LC_ALL, ""); | |
314 | bindtextdomain(PACKAGE, LOCALEDIR); | |
315 | textdomain(PACKAGE); | |
316 | atexit(close_stdout); | |
317 | ||
318 | while ((c = getopt_long(argc, argv, "Jno:rVh", longopts, NULL)) != -1) { | |
319 | err_exclusive_options(c, longopts, excl, excl_st); | |
320 | switch (c) { | |
321 | case 'J': | |
322 | ctrl.json = 1; | |
323 | break; | |
324 | case 'n': | |
325 | ctrl.no_headings = 1; | |
326 | break; | |
327 | case 'o': | |
328 | outarg = optarg; | |
329 | break; | |
330 | case 'r': | |
331 | ctrl.raw = 1; | |
332 | break; | |
333 | case 'V': | |
334 | printf(UTIL_LINUX_VERSION); | |
335 | return EXIT_SUCCESS; | |
336 | case 'h': | |
337 | usage(); | |
338 | default: | |
339 | errtryhelp(EXIT_FAILURE); | |
340 | } | |
341 | } | |
342 | argc -= optind; | |
343 | argv += optind; | |
344 | ||
345 | columns[ncolumns++] = COL_UUID; | |
346 | columns[ncolumns++] = COL_VARIANT; | |
347 | columns[ncolumns++] = COL_TYPE; | |
348 | columns[ncolumns++] = COL_TIME; | |
349 | ||
350 | if (outarg | |
351 | && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns), | |
352 | &ncolumns, column_name_to_id) < 0) | |
353 | return EXIT_FAILURE; | |
354 | ||
355 | print_output(&ctrl, argc, argv); | |
356 | ||
357 | return EXIT_SUCCESS; | |
358 | } |