]> git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/lslocks.c
Merge branch 'sed' of https://github.com/t-8ch/util-linux
[thirdparty/util-linux.git] / misc-utils / lslocks.c
1 /*
2 * lslocks(8) - list local system locks
3 *
4 * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org>
5 *
6 * Very generally based on lslk(8) by Victor A. Abell <abe@purdue.edu>
7 * Since it stopped being maintained over a decade ago, this
8 * program should be considered its replacement.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it would be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <getopt.h>
28 #include <stdlib.h>
29 #include <assert.h>
30 #include <dirent.h>
31 #include <unistd.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <stdbool.h>
35 #include <search.h>
36
37 #include <libmount.h>
38 #include <libsmartcols.h>
39
40 #include "pathnames.h"
41 #include "canonicalize.h"
42 #include "nls.h"
43 #include "xalloc.h"
44 #include "strutils.h"
45 #include "c.h"
46 #include "list.h"
47 #include "closestream.h"
48 #include "optutils.h"
49 #include "procfs.h"
50 #include "column-list-table.h"
51 #include "fileutils.h"
52
53 /* column IDs */
54 enum {
55 COL_SRC = 0,
56 COL_PID,
57 COL_TYPE,
58 COL_SIZE,
59 COL_INODE,
60 COL_MAJMIN,
61 COL_MODE,
62 COL_M,
63 COL_START,
64 COL_END,
65 COL_PATH,
66 COL_BLOCKER,
67 COL_HOLDERS,
68 };
69
70 /* column names */
71 struct colinfo {
72 const char * const name; /* header */
73 double whint; /* width hint (N < 1 is in percent of termwidth) */
74 int flags; /* SCOLS_FL_* */
75 const char *help;
76 };
77
78 /* columns descriptions */
79 static struct colinfo infos[] = {
80 [COL_SRC] = { "COMMAND",15, 0, N_("command of the process holding the lock") },
81 [COL_PID] = { "PID", 5, SCOLS_FL_RIGHT, N_("PID of the process holding the lock") },
82 [COL_TYPE] = { "TYPE", 5, SCOLS_FL_RIGHT, N_("kind of lock") },
83 [COL_SIZE] = { "SIZE", 4, SCOLS_FL_RIGHT, N_("size of the lock, use <number> if --bytes is given") },
84 [COL_INODE] = { "INODE", 5, SCOLS_FL_RIGHT, N_("inode number") },
85 [COL_MAJMIN] = { "MAJ:MIN", 6, 0, N_("major:minor device number") },
86 [COL_MODE] = { "MODE", 5, 0, N_("lock access mode") },
87 [COL_M] = { "M", 1, 0, N_("mandatory state of the lock: 0 (none), 1 (set)")},
88 [COL_START] = { "START", 10, SCOLS_FL_RIGHT, N_("relative byte offset of the lock")},
89 [COL_END] = { "END", 10, SCOLS_FL_RIGHT, N_("ending offset of the lock")},
90 [COL_PATH] = { "PATH", 0, SCOLS_FL_TRUNC, N_("path of the locked file")},
91 [COL_BLOCKER] = { "BLOCKER", 0, SCOLS_FL_RIGHT, N_("PID of the process blocking the lock") },
92 [COL_HOLDERS] = { "HOLDERS", 0, SCOLS_FL_WRAP, N_("HOLDERS of the lock") },
93 };
94
95 static int columns[ARRAY_SIZE(infos) * 2];
96 static size_t ncolumns;
97
98 static struct libmnt_table *tab; /* /proc/self/mountinfo */
99
100 /* basic output flags */
101 static int no_headings;
102 static int no_inaccessible;
103 static int raw;
104 static int json;
105 static int bytes;
106
107 struct lock {
108 struct list_head locks;
109
110 char *cmdname;
111 pid_t pid;
112 char *path;
113 char *type;
114 char *mode;
115 off_t start;
116 off_t end;
117 ino_t inode;
118 dev_t dev;
119 unsigned int mandatory :1,
120 blocked :1;
121 uint64_t size;
122 int fd;
123 int id;
124 };
125
126 struct lock_tnode {
127 dev_t dev;
128 ino_t inode;
129
130 struct list_head chain;
131 };
132
133 static int lock_tnode_compare(const void *a, const void *b)
134 {
135 struct lock_tnode *anode = ((struct lock_tnode *)a);
136 struct lock_tnode *bnode = ((struct lock_tnode *)b);
137
138 if (anode->dev > bnode->dev)
139 return 1;
140 else if (anode->dev < bnode->dev)
141 return -1;
142
143 if (anode->inode > bnode->inode)
144 return 1;
145 else if (anode->inode < bnode->inode)
146 return -1;
147
148 return 0;
149 }
150
151 static void add_to_tree(void *troot, struct lock *l)
152 {
153 struct lock_tnode tmp = { .dev = l->dev, .inode = l->inode, };
154 struct lock_tnode **head = tfind(&tmp, troot, lock_tnode_compare);
155 struct lock_tnode *new_head;
156
157 if (head) {
158 list_add_tail(&l->locks, &(*head)->chain);
159 return;
160 }
161
162 new_head = xmalloc(sizeof(*new_head));
163 new_head->dev = l->dev;
164 new_head->inode = l->inode;
165 INIT_LIST_HEAD(&new_head->chain);
166 if (tsearch(new_head, troot, lock_tnode_compare) == NULL)
167 errx(EXIT_FAILURE, _("failed to allocate memory"));
168
169 list_add_tail(&l->locks, &new_head->chain);
170 }
171
172 static void rem_lock(struct lock *lock)
173 {
174 if (!lock)
175 return;
176
177 free(lock->path);
178 free(lock->mode);
179 free(lock->cmdname);
180 free(lock->type);
181 list_del(&lock->locks);
182 free(lock);
183 }
184
185 static void disable_columns_truncate(void)
186 {
187 size_t i;
188
189 for (i = 0; i < ARRAY_SIZE(infos); i++)
190 infos[i].flags &= ~SCOLS_FL_TRUNC;
191 }
192
193 /*
194 * Associate the device's mountpoint for a filename
195 */
196 static char *get_fallback_filename(dev_t dev)
197 {
198 struct libmnt_fs *fs;
199 char *res = NULL;
200
201 if (!tab) {
202 tab = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO);
203 if (!tab)
204 return NULL;
205 }
206
207 fs = mnt_table_find_devno(tab, dev, MNT_ITER_BACKWARD);
208 if (!fs)
209 return NULL;
210
211 xasprintf(&res, "%s...", mnt_fs_get_target(fs));
212 return res;
213 }
214
215 /*
216 * Return the absolute path of a file from
217 * a given inode number (and its size)
218 */
219 static char *get_filename_sz(ino_t inode, pid_t lock_pid, size_t *size)
220 {
221 struct stat sb;
222 struct dirent *dp;
223 DIR *dirp;
224 size_t sz;
225 int fd;
226 char path[PATH_MAX] = { 0 },
227 sym[PATH_MAX] = { 0 }, *ret = NULL;
228
229 *size = 0;
230
231 if (lock_pid < 0)
232 /* pid could be -1 for OFD locks */
233 return NULL;
234
235 /*
236 * We know the pid so we don't have to
237 * iterate the *entire* filesystem searching
238 * for the damn file.
239 */
240 snprintf(path, sizeof(path), "/proc/%d/fd/", lock_pid);
241 if (!(dirp = opendir(path)))
242 return NULL;
243
244 if ((sz = strlen(path)) >= (sizeof(path) - 2))
245 goto out;
246
247 if ((fd = dirfd(dirp)) < 0 )
248 goto out;
249
250 while ((dp = xreaddir(dirp))) {
251 ssize_t len;
252
253 errno = 0;
254
255 /* care only for numerical descriptors */
256 if (!strtol(dp->d_name, (char **) NULL, 10) || errno)
257 continue;
258
259 if (!fstatat(fd, dp->d_name, &sb, 0)
260 && inode != sb.st_ino)
261 continue;
262
263 if ((len = readlinkat(fd, dp->d_name, sym, sizeof(sym) - 1)) < 1)
264 goto out;
265
266 *size = sb.st_size;
267 sym[len] = '\0';
268
269 ret = xstrdup(sym);
270 break;
271 }
272 out:
273 closedir(dirp);
274 return ret;
275 }
276
277 /*
278 * Return the inode number from a string
279 */
280 static ino_t get_dev_inode(char *str, dev_t *dev)
281 {
282 unsigned int maj = 0, min = 0;
283 ino_t inum = 0;
284
285 if (sscanf(str, "%x:%x:%ju", &maj, &min, &inum) != 3)
286 errx(EXIT_FAILURE, _("failed to parse '%s'"), str);
287
288 *dev = (dev_t) makedev(maj, min);
289 return inum;
290 }
291
292 struct override_info {
293 pid_t pid;
294 const char *cmdname;
295 };
296
297 static bool is_holder(struct lock *l, struct lock *m)
298 {
299 return (l->start == m->start &&
300 l->end == m->end &&
301 l->inode == m->inode &&
302 l->dev == m->dev &&
303 l->mandatory == m->mandatory &&
304 l->blocked == m->blocked &&
305 strcmp(l->type, m->type) == 0 &&
306 strcmp(l->mode, m->mode) == 0);
307 }
308
309 static void patch_lock(struct lock *l, void *fallback)
310 {
311 struct lock_tnode tmp = { .dev = l->dev, .inode = l->inode, };
312 struct lock_tnode **head = tfind(&tmp, fallback, lock_tnode_compare);
313 struct list_head *p;
314
315 if (!head)
316 return;
317
318 list_for_each(p, &(*head)->chain) {
319 struct lock *m = list_entry(p, struct lock, locks);
320 if (is_holder(l, m)) {
321 /* size and id can be ignored. */
322 l->pid = m->pid;
323 l->cmdname = xstrdup(m->cmdname);
324 break;
325 }
326 }
327 }
328
329 static void add_to_list(void *locks, struct lock *l)
330 {
331 list_add(&l->locks, locks);
332 }
333
334 static struct lock *get_lock(char *buf, struct override_info *oinfo, void *fallback)
335 {
336 int i;
337 char *tok = NULL;
338 size_t sz;
339 struct lock *l = xcalloc(1, sizeof(*l));
340 INIT_LIST_HEAD(&l->locks);
341 l->fd = -1;
342
343 bool cmdname_unknown = false;
344
345 for (tok = strtok(buf, " "), i = 0; tok;
346 tok = strtok(NULL, " "), i++) {
347
348 /*
349 * /proc/locks has *exactly* 8 "blocks" of text
350 * separated by ' ' - check <kernel>/fs/locks.c
351 */
352 switch (i) {
353 case 0: /* ID: */
354 if (oinfo)
355 l->id = -1;
356 else {
357 tok[strlen(tok) - 1] = '\0';
358 l->id = strtos32_or_err(tok, _("failed to parse ID"));
359 }
360 break;
361 case 1: /* posix, flock, etc */
362 if (strcmp(tok, "->") == 0) { /* optional field */
363 l->blocked = 1;
364 i--;
365 } else
366 l->type = xstrdup(tok);
367 break;
368
369 case 2: /* is this a mandatory lock? other values are advisory or noinode */
370 l->mandatory = *tok == 'M' ? 1 : 0;
371 break;
372 case 3: /* lock mode */
373 l->mode = xstrdup(tok);
374 break;
375
376 case 4: /* PID */
377 /*
378 * If user passed a pid we filter it later when adding
379 * to the list, no need to worry now. OFD locks use -1 PID.
380 */
381 if (oinfo) {
382 l->pid = oinfo->pid;
383 l->cmdname = xstrdup(oinfo->cmdname);
384 } else {
385 l->pid = strtos32_or_err(tok, _("failed to parse pid"));
386 if (l->pid > 0) {
387 l->cmdname = pid_get_cmdname(l->pid);
388 if (!l->cmdname) {
389 l->cmdname = NULL;
390 cmdname_unknown = true;
391 }
392 } else
393 l->cmdname = NULL;
394 }
395 break;
396
397 case 5: /* device major:minor and inode number */
398 l->inode = get_dev_inode(tok, &l->dev);
399 break;
400
401 case 6: /* start */
402 l->start = !strcmp(tok, "EOF") ? 0 :
403 strtou64_or_err(tok, _("failed to parse start"));
404 break;
405
406 case 7: /* end */
407 /* replace '\n' character */
408 tok[strlen(tok)-1] = '\0';
409 l->end = !strcmp(tok, "EOF") ? 0 :
410 strtou64_or_err(tok, _("failed to parse end"));
411 break;
412 default:
413 break;
414 }
415 }
416
417 if ((!l->blocked) && fallback && !l->cmdname)
418 patch_lock(l, fallback);
419 if (!l->cmdname) {
420 if (cmdname_unknown)
421 l->cmdname = xstrdup(_("(unknown)"));
422 else
423 l->cmdname = xstrdup(_("(undefined)"));
424 }
425 l->path = get_filename_sz(l->inode, l->pid, &sz);
426
427 /* no permissions -- ignore */
428 if (!l->path && no_inaccessible) {
429 rem_lock(l);
430 return NULL;
431 }
432
433 if (!l->path) {
434 /* probably no permission to peek into l->pid's path */
435 l->path = get_fallback_filename(l->dev);
436 l->size = 0;
437 } else
438 l->size = sz;
439
440 return l;
441 }
442
443 static int get_pid_lock(void *locks, void (*add_lock)(void *, struct lock *), FILE *fp,
444 pid_t pid, const char *cmdname, int fd)
445 {
446 char buf[PATH_MAX];
447 struct override_info oinfo = {
448 .pid = pid,
449 .cmdname = cmdname,
450 };
451
452 while (fgets(buf, sizeof(buf), fp)) {
453 struct lock *l;
454 if (strncmp(buf, "lock:\t", 6))
455 continue;
456 l = get_lock(buf + 6, &oinfo, NULL);
457 if (l) {
458 add_lock(locks, l);
459 l->fd = fd;
460 }
461 /* no break here.
462 Multiple recode locks can be taken via one fd. */
463 }
464
465 return 0;
466 }
467
468 static int get_pid_locks(void *locks, void (*add_lock)(void *, struct lock *), struct path_cxt *pc,
469 pid_t pid, const char *cmdname)
470 {
471 DIR *sub = NULL;
472 struct dirent *d = NULL;
473 int rc = 0;
474
475 while (ul_path_next_dirent(pc, &sub, "fdinfo", &d) == 0) {
476 uint64_t num;
477 FILE *fdinfo;
478
479 if (ul_strtou64(d->d_name, &num, 10) != 0) /* only numbers */
480 continue;
481
482 fdinfo = ul_path_fopenf(pc, "r", "fdinfo/%ju", num);
483 if (fdinfo == NULL)
484 continue;
485
486 get_pid_lock(locks, add_lock, fdinfo, pid, cmdname, (int)num);
487 fclose(fdinfo);
488 }
489
490 return rc;
491 }
492
493 static void get_pids_locks(void *locks, void (*add_lock)(void *, struct lock *))
494 {
495 DIR *dir;
496 struct dirent *d;
497 struct path_cxt *pc = NULL;
498
499 pc = ul_new_path(NULL);
500 if (!pc)
501 err(EXIT_FAILURE, _("failed to alloc procfs handler"));
502
503 dir = opendir(_PATH_PROC);
504 if (!dir)
505 err(EXIT_FAILURE, _("failed to open /proc"));
506
507 while ((d = readdir(dir))) {
508 pid_t pid;
509 char buf[BUFSIZ];
510 const char *cmdname = NULL;
511
512 if (procfs_dirent_get_pid(d, &pid) != 0)
513 continue;
514
515 if (procfs_process_init_path(pc, pid) != 0)
516 continue;
517
518 if (procfs_process_get_cmdname(pc, buf, sizeof(buf)) <= 0)
519 continue;
520 cmdname = buf;
521
522 get_pid_locks(locks, add_lock, pc, pid, cmdname);
523 }
524
525 closedir(dir);
526 ul_unref_path(pc);
527
528 return;
529 }
530
531 static int get_proc_locks(void *locks, void (*add_lock)(void *, struct lock *), void *fallback)
532 {
533 FILE *fp;
534 char buf[PATH_MAX];
535
536 if (!(fp = fopen(_PATH_PROC_LOCKS, "r")))
537 return -1;
538
539 while (fgets(buf, sizeof(buf), fp)) {
540 struct lock *l = get_lock(buf, NULL, fallback);
541 if (l)
542 add_lock(locks, l);
543 }
544
545 fclose(fp);
546 return 0;
547 }
548
549 static int column_name_to_id(const char *name, size_t namesz)
550 {
551 size_t i;
552
553 assert(name);
554
555 for (i = 0; i < ARRAY_SIZE(infos); i++) {
556 const char *cn = infos[i].name;
557
558 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
559 return i;
560 }
561 warnx(_("unknown column: %s"), name);
562 return -1;
563 }
564
565 static inline int get_column_id(int num)
566 {
567 assert(num >= 0);
568 assert((size_t) num < ncolumns);
569 assert(columns[num] < (int) ARRAY_SIZE(infos));
570
571 return columns[num];
572 }
573
574
575 static inline const struct colinfo *get_column_info(unsigned num)
576 {
577 return &infos[ get_column_id(num) ];
578 }
579
580 static pid_t get_blocker(int id, struct list_head *locks)
581 {
582 struct list_head *p;
583
584 list_for_each(p, locks) {
585 struct lock *l = list_entry(p, struct lock, locks);
586
587 if (l->id == id && !l->blocked)
588 return l->pid;
589 }
590
591 return 0;
592 }
593
594 static void xstrcoholder(char **str, struct lock *l)
595 {
596 xstrfappend(str, "%d,%s,%d",
597 l->pid, l->cmdname, l->fd);
598 }
599
600 static void add_scols_line(struct libscols_table *table, struct lock *l, struct list_head *locks, void *pid_locks)
601 {
602 size_t i;
603 struct libscols_line *line;
604 /*
605 * Whenever cmdname or filename is NULL it is most
606 * likely because there's no read permissions
607 * for the specified process.
608 */
609 const char *notfnd = "";
610
611 assert(l);
612 assert(table);
613
614 line = scols_table_new_line(table, NULL);
615 if (!line)
616 err(EXIT_FAILURE, _("failed to allocate output line"));
617
618 for (i = 0; i < ncolumns; i++) {
619 char *str = NULL;
620
621 switch (get_column_id(i)) {
622 case COL_SRC:
623 xasprintf(&str, "%s", l->cmdname ? l->cmdname : notfnd);
624 break;
625 case COL_PID:
626 xasprintf(&str, "%d", l->pid);
627 break;
628 case COL_TYPE:
629 xasprintf(&str, "%s", l->type);
630 break;
631 case COL_INODE:
632 xasprintf(&str, "%ju", (uintmax_t) l->inode);
633 break;
634 case COL_MAJMIN:
635 if (json || raw)
636 xasprintf(&str, "%u:%u", major(l->dev), minor(l->dev));
637 else
638 xasprintf(&str, "%3u:%-3u", major(l->dev), minor(l->dev));
639 break;
640 case COL_SIZE:
641 if (!l->size)
642 break;
643 if (bytes)
644 xasprintf(&str, "%ju", l->size);
645 else
646 str = size_to_human_string(SIZE_SUFFIX_1LETTER, l->size);
647 break;
648 case COL_MODE:
649 xasprintf(&str, "%s%s", l->mode, l->blocked ? "*" : "");
650 break;
651 case COL_M:
652 xasprintf(&str, "%d", l->mandatory ? 1 : 0);
653 break;
654 case COL_START:
655 xasprintf(&str, "%jd", l->start);
656 break;
657 case COL_END:
658 xasprintf(&str, "%jd", l->end);
659 break;
660 case COL_PATH:
661 xasprintf(&str, "%s", l->path ? l->path : notfnd);
662 break;
663 case COL_BLOCKER:
664 {
665 pid_t bl = l->blocked && l->id ?
666 get_blocker(l->id, locks) : 0;
667 if (bl)
668 xasprintf(&str, "%d", (int) bl);
669 break;
670 }
671 case COL_HOLDERS:
672 {
673 struct lock_tnode tmp = { .dev = l->dev, .inode = l->inode, };
674 struct lock_tnode **head = tfind(&tmp, pid_locks, lock_tnode_compare);
675 struct list_head *p;
676
677 if (!head)
678 break;
679
680 list_for_each(p, &(*head)->chain) {
681 struct lock *m = list_entry(p, struct lock, locks);
682
683 if (!is_holder(l, m))
684 continue;
685
686 if (str)
687 xstrputc(&str, '\n');
688 xstrcoholder(&str, m);
689 }
690 break;
691 }
692 default:
693 break;
694 }
695
696 if (str && scols_line_refer_data(line, i, str))
697 err(EXIT_FAILURE, _("failed to add output data"));
698 }
699 }
700
701 static void rem_locks(struct list_head *locks)
702 {
703 struct list_head *p, *pnext;
704
705 /* destroy the list */
706 list_for_each_safe(p, pnext, locks) {
707 struct lock *l = list_entry(p, struct lock, locks);
708 rem_lock(l);
709 }
710 }
711
712 static void rem_tnode(void *node)
713 {
714 struct lock_tnode *tnode = node;
715
716 rem_locks(&tnode->chain);
717 free(node);
718 }
719
720 static int get_json_type_for_column(int column_id, int representing_in_bytes)
721 {
722 switch (column_id) {
723 case COL_SIZE:
724 if (!representing_in_bytes)
725 return SCOLS_JSON_STRING;
726 /* fallthrough */
727 case COL_PID:
728 case COL_START:
729 case COL_END:
730 case COL_BLOCKER:
731 case COL_INODE:
732 return SCOLS_JSON_NUMBER;
733 case COL_M:
734 return SCOLS_JSON_BOOLEAN;
735 case COL_HOLDERS:
736 return SCOLS_JSON_ARRAY_STRING;
737 default:
738 return SCOLS_JSON_STRING;
739 }
740 }
741
742 static int show_locks(struct list_head *locks, pid_t target_pid, void *pid_locks)
743 {
744 int rc = 0;
745 size_t i;
746 struct list_head *p;
747 struct libscols_table *table;
748
749 table = scols_new_table();
750 if (!table)
751 err(EXIT_FAILURE, _("failed to allocate output table"));
752
753 scols_table_enable_raw(table, raw);
754 scols_table_enable_json(table, json);
755 scols_table_enable_noheadings(table, no_headings);
756
757 if (json)
758 scols_table_set_name(table, "locks");
759
760 for (i = 0; i < ncolumns; i++) {
761 struct libscols_column *cl;
762 const struct colinfo *col = get_column_info(i);
763
764 cl = scols_table_new_column(table, col->name, col->whint, col->flags);
765 if (!cl)
766 err(EXIT_FAILURE, _("failed to allocate output column"));
767
768 if (col->flags & SCOLS_FL_WRAP) {
769 scols_column_set_wrapfunc(cl,
770 scols_wrapnl_chunksize,
771 scols_wrapnl_nextchunk,
772 NULL);
773 scols_column_set_safechars(cl, "\n");
774 }
775
776 if (json) {
777 int id = get_column_id(i);
778 int json_type = get_json_type_for_column(id, bytes);
779 scols_column_set_json_type(cl, json_type);
780 }
781
782 }
783
784 /* prepare data for output */
785 list_for_each(p, locks) {
786 struct lock *l = list_entry(p, struct lock, locks);
787
788 if (target_pid && target_pid != l->pid)
789 continue;
790
791 add_scols_line(table, l, locks, pid_locks);
792 }
793
794 scols_print_table(table);
795 scols_unref_table(table);
796 return rc;
797 }
798
799
800 static void __attribute__((__noreturn__)) usage(void)
801 {
802 FILE *out = stdout;
803
804 fputs(USAGE_HEADER, out);
805
806 fprintf(out,
807 _(" %s [options]\n"), program_invocation_short_name);
808
809 fputs(USAGE_SEPARATOR, out);
810 fputs(_("List local system locks.\n"), out);
811
812 fputs(USAGE_OPTIONS, out);
813 fputs(_(" -b, --bytes print SIZE in bytes rather than in human readable format\n"), out);
814 fputs(_(" -J, --json use JSON output format\n"), out);
815 fputs(_(" -i, --noinaccessible ignore locks without read permissions\n"), out);
816 fputs(_(" -n, --noheadings don't print headings\n"), out);
817 fputs(_(" -o, --output <list> output columns (see --list-columns)\n"), out);
818 fputs(_(" --output-all output all columns\n"), out);
819 fputs(_(" -p, --pid <pid> display only locks held by this process\n"), out);
820 fputs(_(" -r, --raw use the raw output format\n"), out);
821 fputs(_(" -u, --notruncate don't truncate text in columns\n"), out);
822
823 fputs(USAGE_SEPARATOR, out);
824 fputs(_(" -H, --list-columns list the available columns\n"), out);
825 fprintf(out, USAGE_HELP_OPTIONS(24));
826 fprintf(out, USAGE_MAN_TAIL("lslocks(8)"));
827
828 exit(EXIT_SUCCESS);
829 }
830
831 static void __attribute__((__noreturn__)) list_colunms(void)
832 {
833 struct libscols_table *col_tb = xcolumn_list_table_new(
834 "lslocks-columns", stdout, raw, json);
835
836 for (size_t i = 0; i < ARRAY_SIZE(infos); i++) {
837 if (i != COL_SIZE) {
838 int json_type = get_json_type_for_column(i, bytes);
839 xcolumn_list_table_append_line(col_tb, infos[i].name,
840 json_type, NULL,
841 _(infos[i].help));
842 } else
843 xcolumn_list_table_append_line(col_tb, infos[i].name,
844 -1, "<string|number>",
845 _(infos[i].help));
846 }
847
848 scols_print_table(col_tb);
849 scols_unref_table(col_tb);
850
851 exit(EXIT_SUCCESS);
852 }
853
854 int main(int argc, char *argv[])
855 {
856 int c, rc = 0, collist = 0;
857 struct list_head proc_locks;
858 void *pid_locks = NULL;
859 char *outarg = NULL;
860 enum {
861 OPT_OUTPUT_ALL = CHAR_MAX + 1
862 };
863 static const struct option long_opts[] = {
864 { "bytes", no_argument, NULL, 'b' },
865 { "json", no_argument, NULL, 'J' },
866 { "pid", required_argument, NULL, 'p' },
867 { "help", no_argument, NULL, 'h' },
868 { "output", required_argument, NULL, 'o' },
869 { "output-all", no_argument, NULL, OPT_OUTPUT_ALL },
870 { "notruncate", no_argument, NULL, 'u' },
871 { "version", no_argument, NULL, 'V' },
872 { "noheadings", no_argument, NULL, 'n' },
873 { "raw", no_argument, NULL, 'r' },
874 { "noinaccessible", no_argument, NULL, 'i' },
875 { "list-columns", no_argument, NULL, 'H' },
876 { NULL, 0, NULL, 0 }
877 };
878
879 static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
880 { 'J','r' },
881 { 0 }
882 };
883 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
884 pid_t target_pid = 0;
885
886 setlocale(LC_ALL, "");
887 bindtextdomain(PACKAGE, LOCALEDIR);
888 textdomain(PACKAGE);
889 close_stdout_atexit();
890
891 while ((c = getopt_long(argc, argv,
892 "biJp:o:nruhVH", long_opts, NULL)) != -1) {
893
894 err_exclusive_options(c, long_opts, excl, excl_st);
895
896 switch(c) {
897 case 'b':
898 bytes = 1;
899 break;
900 case 'i':
901 no_inaccessible = 1;
902 break;
903 case 'J':
904 json = 1;
905 break;
906 case 'p':
907 target_pid = strtos32_or_err(optarg, _("invalid PID argument"));
908 break;
909 case 'o':
910 outarg = optarg;
911 break;
912 case OPT_OUTPUT_ALL:
913 for (ncolumns = 0; ncolumns < ARRAY_SIZE(infos); ncolumns++)
914 columns[ncolumns] = ncolumns;
915 break;
916 case 'n':
917 no_headings = 1;
918 break;
919 case 'r':
920 raw = 1;
921 break;
922 case 'u':
923 disable_columns_truncate();
924 break;
925
926 case 'H':
927 collist = 1;
928 break;
929 case 'V':
930 print_version(EXIT_SUCCESS);
931 case 'h':
932 usage();
933 default:
934 errtryhelp(EXIT_FAILURE);
935 }
936 }
937
938 if (collist)
939 list_colunms(); /* print end exit */
940
941 INIT_LIST_HEAD(&proc_locks);
942
943 if (!ncolumns) {
944 /* default columns */
945 columns[ncolumns++] = COL_SRC;
946 columns[ncolumns++] = COL_PID;
947 columns[ncolumns++] = COL_TYPE;
948 columns[ncolumns++] = COL_SIZE;
949 columns[ncolumns++] = COL_MODE;
950 columns[ncolumns++] = COL_M;
951 columns[ncolumns++] = COL_START;
952 columns[ncolumns++] = COL_END;
953 columns[ncolumns++] = COL_PATH;
954 }
955
956 if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
957 &ncolumns, column_name_to_id) < 0)
958 return EXIT_FAILURE;
959
960 scols_init_debug(0);
961
962 /* get_pids_locks() get locks related information from "lock:" fields
963 * of /proc/$pid/fdinfo/$fd as fallback information.
964 * get_proc_locks() used the fallback information if /proc/locks
965 * doesn't provides enough information or provides staled information. */
966 get_pids_locks(&pid_locks, add_to_tree);
967 rc = get_proc_locks(&proc_locks, add_to_list, &pid_locks);
968
969 if (!rc && !list_empty(&proc_locks))
970 rc = show_locks(&proc_locks, target_pid, &pid_locks);
971
972 tdestroy(pid_locks, rem_tnode);
973 rem_locks(&proc_locks);
974
975 mnt_unref_table(tab);
976 return rc;
977 }