1 /*
2 * blkid.c - User command-line interface for libblkid
3 *
4 * Copyright (C) 2001 Andreas Dilger
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the
8 * GNU Lesser General Public License.
9 * %End-Header%
10 */
11
12 #include "config.h"
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <string.h>
17 #ifdef HAVE_TERMIOS_H
18 #include <termios.h>
19 #endif
20 #undef HAVE_TERMIO_H
21 #ifdef HAVE_TERMIO_H
22 #include <termio.h>
23 #endif
24 #ifdef HAVE_SYS_IOCTL_H
25 #include <sys/ioctl.h>
26 #endif
27 #ifdef HAVE_GETOPT_H
28 #include <getopt.h>
29 #else
30 extern int getopt(int argc, char * const argv[], const char *optstring);
31 extern char *optarg;
32 extern int optind;
33 #endif
34
35 #define OUTPUT_VALUE_ONLY 0x0001
36 #define OUTPUT_DEVICE_ONLY 0x0002
37 #define OUTPUT_PRETTY_LIST 0x0004
38
39 #include "ext2fs/ext2fs.h"
40 #include "blkid/blkid.h"
41 #include "blkidP.h"
42 #include "iconv.h"
43 #include "securec.h"
44
45 static const char *progname = "blkid";
46
print_version(FILE * out)47 static void print_version(FILE *out)
48 {
49 fprintf(out, "%s %s (%s)\n", progname, BLKID_VERSION, BLKID_DATE);
50 }
51
usage(int error)52 static void usage(int error)
53 {
54 FILE *out = error ? stderr : stdout;
55
56 print_version(out);
57 fprintf(out,
58 "usage:\t%s [-c <file>] [-ghlLv] [-o format] "
59 "[-s <tag>] [-t <token>]\n [-w <file>] [dev ...]\n"
60 "\t-c\tcache file (default: /etc/blkid.tab, /dev/null = none)\n"
61 "\t-h\tprint this usage message and exit\n"
62 "\t-g\tgarbage collect the blkid cache\n"
63 "\t-s\tshow specified tag(s) (default show all tags)\n"
64 "\t-t\tfind device with a specific token (NAME=value pair)\n"
65 "\t-l\tlookup the the first device with arguments specified by -t\n"
66 "\t-v\tprint version and exit\n"
67 "\t-w\twrite cache to different file (/dev/null = no write)\n"
68 "\tdev\tspecify device(s) to probe (default: all devices)\n",
69 progname);
70 exit(error);
71 }
72
73 /*
74 * This function does "safe" printing. It will convert non-printable
75 * ASCII characters using '^' and M- notation.
76 */
safe_print(const char * cp,int len)77 static void safe_print(const char *cp, int len)
78 {
79 unsigned char ch;
80
81 if (len < 0)
82 len = strlen(cp);
83
84 while (len--) {
85 ch = *cp++;
86 if (ch > 128) {
87 fputs("M-", stdout);
88 ch -= 128;
89 }
90 if ((ch < 32) || (ch == 0x7f)) {
91 fputc('^', stdout);
92 ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */
93 }
94 if (ch != '"') {
95 fputc(ch, stdout);
96 }
97 }
98 }
99
get_terminal_width(void)100 static int get_terminal_width(void)
101 {
102 #ifdef TIOCGSIZE
103 struct ttysize t_win;
104 #endif
105 #ifdef TIOCGWINSZ
106 struct winsize w_win;
107 #endif
108 const char *cp;
109 int width = 80;
110
111 #ifdef TIOCGSIZE
112 if (ioctl (0, TIOCGSIZE, &t_win) == 0) {
113 width = t_win.ts_cols;
114 goto got_it;
115 }
116 #endif
117 #ifdef TIOCGWINSZ
118 if (ioctl (0, TIOCGWINSZ, &w_win) == 0) {
119 width = w_win.ws_col;
120 goto got_it;
121 }
122 #endif
123 cp = getenv("COLUMNS");
124 if (cp)
125 width = atoi(cp);
126 got_it:
127 if (width > 4096)
128 return 4096; /* sanity check */
129 return width;
130 }
131
pretty_print_word(const char * str,int max_len,int left_len,int overflow_nl)132 static int pretty_print_word(const char *str, int max_len,
133 int left_len, int overflow_nl)
134 {
135 int len = strlen(str) + left_len;
136 int ret = 0;
137
138 fputs(str, stdout);
139 if (overflow_nl && len > max_len) {
140 fputc('\n', stdout);
141 len = 0;
142 } else if (len > max_len)
143 ret = len - max_len;
144 do {
145 fputc(' ', stdout);
146 } while (len++ < max_len);
147 return ret;
148 }
149
pretty_print_line(const char * device,const char * fs_type,const char * label,const char * mtpt,const char * uuid)150 static void pretty_print_line(const char *device, const char *fs_type,
151 const char *label, const char *mtpt,
152 const char *uuid)
153 {
154 static int device_len = 10, fs_type_len = 7;
155 static int label_len = 8, mtpt_len = 14;
156 static int term_width = -1;
157 int len, w;
158
159 if (term_width < 0) {
160 term_width = get_terminal_width();
161
162 if (term_width > 80) {
163 term_width -= 80;
164 w = term_width / 10;
165 if (w > 8)
166 w = 8;
167 term_width -= 2*w;
168 label_len += w;
169 fs_type_len += w;
170 w = term_width/2;
171 device_len += w;
172 mtpt_len +=w;
173 }
174 }
175
176 len = pretty_print_word(device, device_len, 0, 1);
177 len = pretty_print_word(fs_type, fs_type_len, len, 0);
178 len = pretty_print_word(label, label_len, len, 0);
179 len = pretty_print_word(mtpt, mtpt_len, len, 0);
180 fputs(uuid, stdout);
181 fputc('\n', stdout);
182 }
183
pretty_print_dev(blkid_dev dev)184 static void pretty_print_dev(blkid_dev dev)
185 {
186 blkid_tag_iterate iter;
187 const char *type, *value, *devname;
188 const char *uuid = "", *fs_type = "", *label = "";
189 int len, mount_flags;
190 char mtpt[80];
191 errcode_t retval;
192
193 if (dev == NULL) {
194 pretty_print_line("device", "fs_type", "label",
195 "mount point", "UUID");
196 for (len=get_terminal_width()-1; len > 0; len--)
197 fputc('-', stdout);
198 fputc('\n', stdout);
199 return;
200 }
201
202 devname = blkid_dev_devname(dev);
203 if (access(devname, F_OK))
204 return;
205
206 /* Get the uuid, label, type */
207 iter = blkid_tag_iterate_begin(dev);
208 while (blkid_tag_next(iter, &type, &value) == 0) {
209 if (!strcmp(type, "UUID"))
210 uuid = value;
211 if (!strcmp(type, "TYPE"))
212 fs_type = value;
213 if (!strcmp(type, "LABEL"))
214 label = value;
215 }
216 blkid_tag_iterate_end(iter);
217
218 /* Get the mount point */
219 mtpt[0] = 0;
220 retval = ext2fs_check_mount_point(devname, &mount_flags,
221 mtpt, sizeof(mtpt));
222 if (retval == 0) {
223 if (mount_flags & EXT2_MF_MOUNTED) {
224 if (!mtpt[0])
225 strcpy(mtpt, "(mounted, mtpt unknown)");
226 } else if (mount_flags & EXT2_MF_BUSY)
227 strcpy(mtpt, "(in use)");
228 else
229 strcpy(mtpt, "(not mounted)");
230 }
231
232 pretty_print_line(devname, fs_type, label, mtpt, uuid);
233 }
234
is_str_utf8(const char * str)235 static int is_str_utf8(const char* str)
236 {
237 unsigned int nBytes = 0;
238 unsigned char chr = *str;
239 int bAllAscii = 1;
240
241 for (unsigned int i = 0; str[i] != '\0'; ++i) {
242 chr = *(str + i);
243 if ((chr & 0x80) != 0)
244 bAllAscii = 0;
245 if(nBytes == 0 && ((chr & 0x80) != 0)) {
246 while((chr & 0x80) != 0) {
247 chr <<= 1;
248 nBytes ++;
249 }
250 if((nBytes < 2) || (nBytes > 6)) {
251 return 0;
252 }
253 nBytes --;
254 } else if (nBytes != 0) {
255 if((chr & 0xc0) != 0x80) {
256 return 0;
257 }
258 nBytes --;
259 }
260 }
261 if (nBytes && !bAllAscii) {
262 return 0;
263 }
264
265 return 1;
266 }
267
268
code_convert(char * from_charset,char * to_charset,char * inbuf,size_t inlen,char * outbuf,size_t outlen)269 static int code_convert(char *from_charset, char *to_charset,char *inbuf, size_t inlen, char *outbuf, size_t outlen)
270 {
271 iconv_t cd;
272 char **pin = &inbuf;
273 char **pout = &outbuf;
274
275 cd = iconv_open(to_charset, from_charset);
276 if (cd == 0) return -1;
277 if (memset_s(outbuf, outlen, 0, outlen) != EOK) return -1;
278 if (iconv(cd, pin, &inlen, pout, &outlen) == (size_t)-1) return -1;
279 iconv_close(cd);
280 return 0;
281 }
282
print_tags(blkid_dev dev,char * show[],int numtag,int output)283 static void print_tags(blkid_dev dev, char *show[], int numtag, int output)
284 {
285 blkid_tag_iterate iter;
286 const char *type, *value;
287 int i, first = 1;
288
289 if (!dev)
290 return;
291
292 if (output & OUTPUT_PRETTY_LIST) {
293 pretty_print_dev(dev);
294 return;
295 }
296
297 if (output & OUTPUT_DEVICE_ONLY) {
298 printf("%s\n", blkid_dev_devname(dev));
299 return;
300 }
301
302 iter = blkid_tag_iterate_begin(dev);
303 while (blkid_tag_next(iter, &type, &value) == 0) {
304 if (numtag && show) {
305 for (i=0; i < numtag; i++)
306 if (!strcmp(type, show[i]))
307 break;
308 if (i >= numtag)
309 continue;
310 }
311 if (output & OUTPUT_VALUE_ONLY) {
312 if (!strncmp(type, "LABEL", 5) && !strncmp(dev->bid_type, "vfat", 4) && !is_str_utf8(value)) {
313 char outbuf[255];
314 int res = code_convert("gbk","utf-8", (char *)value, strlen(value), outbuf, 255);
315 if (!res) {
316 fputs(outbuf, stdout);
317 } else {
318 fputs(value, stdout);
319 }
320 } else {
321 fputs(value, stdout);
322 }
323 fputc('\n', stdout);
324 } else {
325 if (first) {
326 printf("%s: ", blkid_dev_devname(dev));
327 first = 0;
328 }
329 fputs(type, stdout);
330 fputs("=\"", stdout);
331 safe_print(value, -1);
332 fputs("\" ", stdout);
333 }
334 }
335 blkid_tag_iterate_end(iter);
336
337 if (!first && !(output & OUTPUT_VALUE_ONLY))
338 printf("\n");
339 }
340
main(int argc,char ** argv)341 int main(int argc, char **argv)
342 {
343 blkid_cache cache = NULL;
344 char *devices[128] = { NULL, };
345 char *show[128] = { NULL, };
346 char *search_type = NULL, *search_value = NULL;
347 char *read = NULL;
348 char *write = NULL;
349 unsigned int numdev = 0, numtag = 0;
350 int version = 0;
351 int err = 4;
352 unsigned int i;
353 int output_format = 0;
354 int lookup = 0, gc = 0;
355 int c;
356
357 while ((c = getopt (argc, argv, "c:f:ghlLo:s:t:w:v")) != EOF)
358 switch (c) {
359 case 'c':
360 read = optarg;
361 if (!write)
362 write = read;
363 break;
364 case 'l':
365 lookup++;
366 break;
367 case 'L':
368 output_format = OUTPUT_PRETTY_LIST;
369 break;
370 case 'g':
371 gc = 1;
372 break;
373 case 'o':
374 if (!strcmp(optarg, "value"))
375 output_format = OUTPUT_VALUE_ONLY;
376 else if (!strcmp(optarg, "device"))
377 output_format = OUTPUT_DEVICE_ONLY;
378 else if (!strcmp(optarg, "list"))
379 output_format = OUTPUT_PRETTY_LIST;
380 else if (!strcmp(optarg, "full"))
381 output_format = 0;
382 else {
383 fprintf(stderr, "Invalid output format %s. "
384 "Choose from value,\n\t"
385 "device, list, or full\n", optarg);
386 exit(1);
387 }
388 break;
389 case 's':
390 if (numtag >= sizeof(show) / sizeof(*show)) {
391 fprintf(stderr, "Too many tags specified\n");
392 usage(err);
393 }
394 show[numtag++] = optarg;
395 break;
396 case 't':
397 if (search_type) {
398 fprintf(stderr, "Can only search for "
399 "one NAME=value pair\n");
400 usage(err);
401 }
402 if (blkid_parse_tag_string(optarg,
403 &search_type,
404 &search_value)) {
405 fprintf(stderr, "-t needs NAME=value pair\n");
406 usage(err);
407 }
408 break;
409 case 'v':
410 version = 1;
411 break;
412 case 'w':
413 write = optarg;
414 break;
415 case 'h':
416 err = 0;
417 /* fallthrough */
418 default:
419 usage(err);
420 }
421
422 while (optind < argc)
423 devices[numdev++] = argv[optind++];
424
425 if (version) {
426 print_version(stdout);
427 goto exit;
428 }
429
430 if (blkid_get_cache(&cache, read) < 0)
431 goto exit;
432
433 err = 2;
434 if (gc) {
435 blkid_gc_cache(cache);
436 goto exit;
437 }
438 if (output_format & OUTPUT_PRETTY_LIST)
439 pretty_print_dev(NULL);
440
441 if (lookup) {
442 blkid_dev dev;
443
444 if (!search_type) {
445 fprintf(stderr, "The lookup option requires a "
446 "search type specified using -t\n");
447 exit(1);
448 }
449 /* Load any additional devices not in the cache */
450 for (i = 0; i < numdev; i++)
451 blkid_get_dev(cache, devices[i], BLKID_DEV_NORMAL);
452
453 if ((dev = blkid_find_dev_with_tag(cache, search_type,
454 search_value))) {
455 print_tags(dev, show, numtag, output_format);
456 err = 0;
457 }
458 /* If we didn't specify a single device, show all available devices */
459 } else if (!numdev) {
460 blkid_dev_iterate iter;
461 blkid_dev dev;
462
463 blkid_probe_all(cache);
464
465 iter = blkid_dev_iterate_begin(cache);
466 blkid_dev_set_search(iter, search_type, search_value);
467 while (blkid_dev_next(iter, &dev) == 0) {
468 dev = blkid_verify(cache, dev);
469 if (!dev)
470 continue;
471 print_tags(dev, show, numtag, output_format);
472 err = 0;
473 }
474 blkid_dev_iterate_end(iter);
475 /* Add all specified devices to cache (optionally display tags) */
476 } else for (i = 0; i < numdev; i++) {
477 blkid_dev dev = blkid_get_dev(cache, devices[i],
478 BLKID_DEV_NORMAL);
479
480 if (dev) {
481 if (search_type &&
482 !blkid_dev_has_tag(dev, search_type,
483 search_value))
484 continue;
485 print_tags(dev, show, numtag, output_format);
486 err = 0;
487 }
488 }
489
490 exit:
491 free(search_type);
492 free(search_value);
493 blkid_put_cache(cache);
494 return err;
495 }
496