summaryrefslogtreecommitdiff
path: root/misc-utils
diff options
context:
space:
mode:
Diffstat (limited to 'misc-utils')
-rw-r--r--misc-utils/Makemodule.am175
-rw-r--r--misc-utils/blkid.8299
-rw-r--r--misc-utils/blkid.c957
-rw-r--r--misc-utils/cal.197
-rw-r--r--misc-utils/cal.c800
-rw-r--r--misc-utils/chkdupexe120
-rw-r--r--misc-utils/chkdupexe.138
-rwxr-xr-xmisc-utils/chkdupexe.pl120
-rw-r--r--misc-utils/ddate.1114
-rw-r--r--misc-utils/ddate.c374
-rw-r--r--misc-utils/findfs.834
-rw-r--r--misc-utils/findfs.c69
-rw-r--r--misc-utils/findmnt.8217
-rw-r--r--misc-utils/findmnt.c1368
-rw-r--r--misc-utils/getopt-parse.bash47
-rw-r--r--misc-utils/getopt-parse.tcsh77
-rw-r--r--misc-utils/getopt.1448
-rw-r--r--misc-utils/getopt.c458
-rw-r--r--misc-utils/kill.1111
-rw-r--r--misc-utils/kill.c439
-rw-r--r--misc-utils/kill.h1
-rw-r--r--misc-utils/logger.1155
-rw-r--r--misc-utils/logger.c338
-rw-r--r--misc-utils/look.1116
-rw-r--r--misc-utils/look.c381
-rw-r--r--misc-utils/lsblk.8104
-rw-r--r--misc-utils/lsblk.c1421
-rw-r--r--misc-utils/lslocks.882
-rw-r--r--misc-utils/lslocks.c574
-rw-r--r--misc-utils/mcookie.165
-rw-r--r--misc-utils/mcookie.c190
-rw-r--r--misc-utils/namei.174
-rw-r--r--misc-utils/namei.c528
-rw-r--r--misc-utils/procs.c126
-rw-r--r--misc-utils/rename.160
-rw-r--r--misc-utils/rename.c126
-rw-r--r--misc-utils/sd-daemon.c530
-rw-r--r--misc-utils/sd-daemon.h282
-rw-r--r--misc-utils/uuidd.894
-rw-r--r--misc-utils/uuidd.8.in94
-rw-r--r--misc-utils/uuidd.c669
-rw-r--r--misc-utils/uuidd.rc.in71
-rw-r--r--misc-utils/uuidd.service.in12
-rw-r--r--misc-utils/uuidd.socket.in8
-rw-r--r--misc-utils/uuidgen.163
-rw-r--r--misc-utils/uuidgen.c104
-rw-r--r--misc-utils/whereis.1140
-rw-r--r--misc-utils/whereis.c485
-rw-r--r--misc-utils/wipefs.874
-rw-r--r--misc-utils/wipefs.c472
50 files changed, 13801 insertions, 0 deletions
diff --git a/misc-utils/Makemodule.am b/misc-utils/Makemodule.am
new file mode 100644
index 0000000..910e428
--- /dev/null
+++ b/misc-utils/Makemodule.am
@@ -0,0 +1,175 @@
+
+usrbin_exec_PROGRAMS += cal
+dist_man_MANS += misc-utils/cal.1
+cal_SOURCES = \
+ misc-utils/cal.c \
+ lib/mbsalign.c \
+ lib/strutils.c
+
+if !HAVE_LANGINFO
+cal_SOURCES += lib/langinfo.c
+endif
+
+cal_LDADD = $(LDADD)
+
+if HAVE_TINFO
+cal_LDADD += -ltinfo @NCURSES_LIBS@
+else
+if HAVE_NCURSES
+cal_LDADD += @NCURSES_LIBS@
+else
+if HAVE_TERMCAP
+cal_LDADD += -ltermcap
+endif
+endif # !HAVE_NCURSES
+endif # !HAVE_TINFO
+
+
+usrbin_exec_PROGRAMS += logger
+dist_man_MANS += misc-utils/logger.1
+logger_SOURCES = misc-utils/logger.c lib/strutils.c
+
+usrbin_exec_PROGRAMS += look
+dist_man_MANS += misc-utils/look.1
+look_SOURCES = misc-utils/look.c
+
+usrbin_exec_PROGRAMS += mcookie
+dist_man_MANS += misc-utils/mcookie.1
+mcookie_SOURCES = misc-utils/mcookie.c lib/md5.c
+
+usrbin_exec_PROGRAMS += namei
+dist_man_MANS += misc-utils/namei.1
+namei_SOURCES = misc-utils/namei.c lib/strutils.c
+
+usrbin_exec_PROGRAMS += whereis
+dist_man_MANS += misc-utils/whereis.1
+whereis_SOURCES = misc-utils/whereis.c
+
+
+if BUILD_CHKDUPEXE
+dist_usrbin_exec_SCRIPTS += misc-utils/chkdupexe
+CLEANFILES += misc-utils/chkdupexe
+dist_man_MANS += misc-utils/chkdupexe.1
+endif
+
+if BUILD_DDATE
+usrbin_exec_PROGRAMS += ddate
+dist_man_MANS += misc-utils/ddate.1
+ddate_SOURCES = misc-utils/ddate.c
+ruman1_DATA += man/ru/ddate.1
+endif
+
+if BUILD_LSLOCKS
+usrbin_exec_PROGRAMS += lslocks
+dist_man_MANS += misc-utils/lslocks.8
+lslocks_SOURCES = \
+ misc-utils/lslocks.c \
+ lib/at.c \
+ lib/strutils.c \
+ lib/tt.c \
+ lib/mbsalign.c
+endif
+
+if BUILD_LSBLK
+bin_PROGRAMS += lsblk
+dist_man_MANS += misc-utils/lsblk.8
+lsblk_SOURCES = misc-utils/lsblk.c
+lsblk_LDADD = $(LDADD) libblkid.la libmount.la libcommon.la
+lsblk_CFLAGS = $(AM_CFLAGS) -I$(ul_libblkid_incdir) -I$(ul_libmount_incdir)
+if HAVE_UDEV
+lsblk_LDADD += -ludev
+endif
+endif
+
+if BUILD_LIBUUID
+usrbin_exec_PROGRAMS += uuidgen
+dist_man_MANS += misc-utils/uuidgen.1
+uuidgen_SOURCES = misc-utils/uuidgen.c
+uuidgen_LDADD = $(LDADD) libuuid.la
+uuidgen_CFLAGS = $(AM_CFLAGS) -I$(ul_libuuid_incdir)
+endif
+
+if BUILD_UUIDD
+usrsbin_exec_PROGRAMS += uuidd
+dist_man_MANS += misc-utils/uuidd.8
+uuidd_LDADD = $(LDADD) libuuid.la
+uuidd_CFLAGS = $(AM_CFLAGS) -I$(ul_libuuid_incdir)
+uuidd_SOURCES = misc-utils/uuidd.c
+if USE_SOCKET_ACTIVATION
+uuidd_SOURCES += misc-utils/sd-daemon.c misc-utils/sd-daemon.h
+uuidd_LDADD += -lrt
+endif
+if HAVE_SYSTEMD
+systemdsystemunit_DATA += \
+ misc-utils/uuidd.service \
+ misc-utils/uuidd.socket
+endif
+endif # BUILD_UUIDD
+
+PATHFILES += \
+ misc-utils/uuidd.8 \
+ misc-utils/uuidd.rc \
+ misc-utils/uuidd.service \
+ misc-utils/uuidd.socket
+
+if BUILD_LIBBLKID
+sbin_PROGRAMS += blkid
+dist_man_MANS += misc-utils/blkid.8
+blkid_SOURCES = misc-utils/blkid.c
+blkid_LDADD = $(LDADD) libblkid.la libcommon.la
+blkid_CFLAGS = $(AM_CFLAGS) -I$(ul_libblkid_incdir)
+
+sbin_PROGRAMS += findfs
+dist_man_MANS += misc-utils/findfs.8
+findfs_LDADD = $(LDADD) libblkid.la
+findfs_SOURCES = misc-utils/findfs.c
+findfs_CFLAGS = $(AM_CFLAGS) -I$(ul_libblkid_incdir)
+
+sbin_PROGRAMS += wipefs
+dist_man_MANS += misc-utils/wipefs.8
+wipefs_SOURCES = misc-utils/wipefs.c
+wipefs_LDADD = $(LDADD) libblkid.la libcommon.la
+wipefs_CFLAGS = $(AM_CFLAGS) -I$(ul_libblkid_incdir)
+
+if HAVE_STATIC_BLKID
+sbin_PROGRAMS += blkid.static
+blkid_static_SOURCES = $(blkid_SOURCES)
+blkid_static_LDFLAGS = -all-static
+blkid_static_LDADD = $(LDADD) libblkid.la
+blkid_static_CFLAGS = $(AM_CFLAGS) -I$(ul_libblkid_incdir)
+endif
+endif # BUILD_LIBBLKID
+
+
+if BUILD_LIBMOUNT
+bin_PROGRAMS += findmnt
+dist_man_MANS += misc-utils/findmnt.8
+findmnt_LDADD = $(LDADD) libmount.la libcommon.la
+findmnt_CFLAGS = $(AM_CFLAGS) -I$(ul_libmount_incdir)
+findmnt_SOURCES = misc-utils/findmnt.c
+endif # BUILD_LIBMOUNT
+
+
+if BUILD_KILL
+bin_PROGRAMS += kill
+kill_SOURCES = \
+ misc-utils/kill.c \
+ misc-utils/kill.h \
+ misc-utils/procs.c
+kill_LDADD = $(LDADD) libcommon.la
+dist_man_MANS += misc-utils/kill.1
+endif
+
+if BUILD_RENAME
+usrbin_exec_PROGRAMS += rename
+dist_man_MANS += misc-utils/rename.1
+rename_SOURCES = misc-utils/rename.c
+endif
+
+usrbin_exec_PROGRAMS += getopt
+dist_man_MANS += misc-utils/getopt.1
+getopt_SOURCES = misc-utils/getopt.c
+getoptexampledir = $(datadir)/getopt/
+dist_getoptexample_SCRIPTS = \
+ misc-utils/getopt-parse.bash \
+ misc-utils/getopt-parse.tcsh
diff --git a/misc-utils/blkid.8 b/misc-utils/blkid.8
new file mode 100644
index 0000000..dc1e68f
--- /dev/null
+++ b/misc-utils/blkid.8
@@ -0,0 +1,299 @@
+.\" Copyright 2000 Andreas Dilger (adilger@turbolinux.com)
+.\"
+.\" This man page was created for blkid from e2fsprogs-1.25.
+.\"
+.\" This file may be copied under the terms of the GNU Public License.
+.\"
+.\" Based on uuidgen, Mon Sep 17 10:42:12 2000, Andreas Dilger
+.TH BLKID 8 "February 2011" "util-linux" "System Administration"
+.SH NAME
+blkid \- locate/print block device attributes
+.SH SYNOPSIS
+.B blkid
+.RB \-L
+.IR label " | "
+.RB \-U
+.IR uuid
+
+.B blkid
+.RB [ \-dghlv ]
+.RB [ \-c
+.IR file ]
+.RB [ \-o
+.IR format ]
+.in +6
+.RB [ \-s
+.IR tag ]
+.RB [ \-t
+.IR NAME=value ]
+[\fIdevice\fR ...]
+.in -6
+
+.B blkid
+.RB -p
+.RB [ \-O
+.IR offset ]
+.RB [ \-S
+.IR size ]
+.RB [ \-o
+.IR format ]
+.RB [ \-s
+.IR tag ]
+.in +9
+.RB [ \-n
+.IR list ]
+.RB [ \-u
+.IR list ]
+.IR device " ... "
+.in -9
+
+.B blkid
+.RB -i
+.RB [ \-o
+.IR format ]
+.RB [ \-s
+.IR tag ]
+.IR device " ... "
+
+.SH DESCRIPTION
+The
+.B blkid
+program is the command-line interface to working with the
+.BR libblkid (3)
+library. It can determine the type of content (e.g. filesystem or swap)
+that a block device holds, and also attributes (tokens, NAME=value pairs)
+from the content metadata (e.g. LABEL or UUID fields).
+.PP
+.B blkid
+has two main forms of operation: either searching for a device with a
+specific NAME=value pair, or displaying NAME=value pairs for one or
+more specified devices.
+.SH OPTIONS
+The \fIsize\fR and \fIoffset\fR arguments may be followed by the multiplicative
+suffixes KiB=1024, MiB=1024*1024, and so on for GiB, TiB, PiB, EiB, ZiB and YiB
+(the "iB" is optional, e.g. "K" has the same meaning as "KiB") or the suffixes
+KB=1000, MB=1000*1000, and so on for GB, PB, EB, ZB and YB.
+.TP
+.BI \-c " cachefile"
+Read from
+.I cachefile
+instead of reading from the default cache file (see CONFIGURATION FILE section
+for more details). If you want to start with a clean cache (i.e. don't report
+devices previously scanned but not necessarily available at this time), specify
+.IR /dev/null .
+.TP
+.B \-d
+Don't encode non-printing characters. The non-printing characters are encoded
+by ^ and M- notation by default. Note that \fB-o udev\fR output format uses
+a different encoding and this encoding cannot be disabled.
+.TP
+.B \-g
+Perform a garbage collection pass on the blkid cache to remove
+devices which no longer exist.
+.TP
+.B \-h
+Display a usage message and exit.
+.TP
+.B \-i
+Display I/O Limits (aka I/O topology) information. The 'export' output format is
+automatically enabled. This option can be used together with the \fB-p\fR option.
+.TP
+.B \-l
+Look up only one device that matches the search parameter specified with \fB-t\fR.
+.TP
+.B \-k
+List all known filesystems and RAIDs and exit.
+.TP
+.B \-t
+option. If there are multiple devices that match the specified search
+parameter, then the device with the highest priority is returned, and/or
+the first device found at a given priority. Device types in order of
+decreasing priority are Device Mapper, EVMS, LVM, MD, and finally regular
+block devices. If this option is not specified,
+.B blkid
+will print all of the devices that match the search parameter.
+.TP
+.BI \-L " label"
+Look up the device that uses this filesystem \fIlabel\fR (equal to: -l -o device -t
+LABEL=<label>). This lookup method is able to reliably use /dev/disk/by-label
+udev symlinks (dependent on a setting in /etc/blkid.conf). Avoid using the
+symlinks directly; it is not reliable to use the symlinks without verification.
+The \fB-L\fR option works on systems with and without udev.
+
+Unfortunately, the original
+.B blkid(8)
+from e2fsprogs use the \fB-L\fR option as a
+synonym for the \fB-o list\fR option. For better portability, use \fB-l -o device
+-t LABEL=<label>\fR and \fB-o list\fR in your scripts rather than the \fB-L\fR option.
+.TP
+.BI \-n " list "
+Restrict the probing functions to the specified (comma-separated) \fIlist\fR of
+superblock types (names).
+The list items may be prefixed with "no" to specify the types which should be ignored.
+For example:
+.sp
+ blkid -p -n vfat,ext3,ext4 /dev/sda1
+.sp
+probes for vfat, ext3 and ext4 filesystems, and
+.sp
+ blkid -p -n nominix /dev/sda1
+.sp
+probes for all supported formats except minix filesystems.
+This option is only useful together with \fB-p\fR.
+.TP
+.BI \-o " format"
+Display
+.BR blkid 's
+output using the specified format. Note that the variables order and
+devices order is variable. See also \fB-s\fR. The
+.I format
+parameter may be:
+.RS
+.TP
+.B full
+print all tags (the default)
+.TP
+.B value
+print the value of the tags
+.TP
+.B list
+print the devices in a user-friendly format; this output format is unsupported
+for low-level probing (\fB-p\fR or \fB-i\fR). This output format is \fBDEPRECATED\fR
+in favour of
+.BR lsblk (8)
+command.
+.TP
+.B device
+print the device name only; this output format is always enabled for \fB-L\fR
+and \fB-U\fR options
+.TP
+.B udev
+print key="value" pairs for easy import into the udev environment; the keys are
+prefixed by ID_FS_ or ID_PART_ prefixes
+
+The udev output returns the ID_FS_AMBIVALENT tag if more superblocks are detected,
+and ID_PART_ENTRY_* tags are always returned for all partitions including empty
+partitions. This output format is \fBDEPRECATED\fR.
+.TP
+.B export
+print key=value pairs for easy import into the environment; this output format
+is automatically enabled when I/O Limits (\fB-i\fR option) are requested
+.RE
+.TP
+.BI \-O " offset"
+Probe at the given \fIoffset\fR (only useful with \fB-p\fR). This option can be
+used together with the \fB-i\fR option.
+.TP
+.BI \-p
+Switch to low-level superblock probing mode (bypass cache).
+
+Note that low-level probing also returns information about partition table type
+(PTTYPE tag) and partitions (PART_ENTRY_* tags).
+.TP
+.BI \-s " tag"
+For each (specified) device, show only the tags that match
+.IR tag .
+It is possible to specify multiple
+.B \-s
+options. If no tag is specified, then all tokens are shown for all
+(specified) devices.
+In order to just refresh the cache without showing any tokens, use
+.B "-s none"
+with no other options.
+.TP
+.BI \-S " size"
+Overwrite device/file size (only useful with \fB-p\fR).
+.TP
+.BI \-t " NAME" = "value"
+Search for block devices with tokens named
+.I NAME
+that have the value
+.IR value ,
+and display any devices which are found.
+Common values for
+.I NAME
+include
+.BR TYPE ,
+.BR LABEL ,
+and
+.BR UUID .
+If there are no devices specified on the command line, all block devices
+will be searched; otherwise only the specified devices are searched.
+.TP
+.BI \-u " list "
+Restrict the probing functions to the specified (comma-separated) \fIlist\fR of "usage" types.
+Supported usage types are: filesystem, raid, crypto and other. The list items may be
+prefixed with "no" to specify the usage types which should be ignored. For example:
+.sp
+ blkid -p -u filesystem,other /dev/sda1
+.sp
+probes for all filesystem and other (e.g. swap) formats, and
+.sp
+ blkid -p -u noraid /dev/sda1
+.sp
+probes for all supported formats except RAIDs.
+This option is only useful together with \fB-p\fR.
+.TP
+.BI \-U " uuid "
+Look up the device that uses this filesystem \fIuuid\fR. For more details see the \fB-L\fR option.
+.TP
+.B \-v
+Display version number and exit.
+.TP
+.I device
+Display tokens from only the specified device. It is possible to
+give multiple
+.I device
+options on the command line. If none is given, all devices which
+appear in
+.I /proc/partitions
+are shown, if they are recognized.
+.SH "RETURN CODE"
+If the specified token was found, or if any tags were shown from (specified)
+devices, 0 is returned.
+
+If the specified token was not found, or no (specified) devices could be
+identified, an exit code of 2 is returned.
+
+For usage or other errors, an exit code of 4 is returned.
+
+If the ambivalent low-level probing result was detected, an exit code of 8 is
+returned.
+.SH CONFIGURATION FILE
+The standard location of the
+.I /etc/blkid.conf
+config file can be overridden by the environment variable BLKID_CONF.
+The following options control the libblkid library:
+.TP
+.I SEND_UEVENT=<yes|not>
+Sends uevent when
+.I /dev/disk/by-{label,uuid,partuuid,partlabel}/
+symlink does not match with LABEL, UUID, PARTUUID or PARTLABEL on the device. Default is "yes".
+.TP
+.I CACHE_FILE=<path>
+Overrides the standard location of the cache file. This setting can be
+overridden by the environment variable BLKID_FILE. Default is
+.I /run/blkid/blkid.tab
+or
+.I /etc/blkid.tab
+on systems without /run direcotry
+.TP
+.I EVALUATE=<methods>
+Defines LABEL and UUID evaluation method(s). Currently, the libblkid library
+supports "udev" and "scan" methods. More than one methods may be specified in
+a comma separated list. Default is "udev,scan". The "udev" method uses udev
+.I /dev/disk/by-*
+symlinks and the "scan" method scans all block devices from the
+.I /proc/partitions
+file.
+.SH AUTHOR
+.B blkid
+was written by Andreas Dilger for libblkid and improved by Theodore Ts'o
+and Karel Zak.
+.SH AVAILABILITY
+The blkid command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.SH "SEE ALSO"
+.BR libblkid (3)
+.BR findfs (8)
+.BR wipefs (8)
diff --git a/misc-utils/blkid.c b/misc-utils/blkid.c
new file mode 100644
index 0000000..fa38d82
--- /dev/null
+++ b/misc-utils/blkid.c
@@ -0,0 +1,957 @@
+/*
+ * blkid.c - User command-line interface for libblkid
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+extern int getopt(int argc, char * const argv[], const char *optstring);
+extern char *optarg;
+extern int optind;
+#endif
+
+#define OUTPUT_VALUE_ONLY (1 << 1)
+#define OUTPUT_DEVICE_ONLY (1 << 2)
+#define OUTPUT_PRETTY_LIST (1 << 3) /* deprecated */
+#define OUTPUT_UDEV_LIST (1 << 4) /* deprecated */
+#define OUTPUT_EXPORT_LIST (1 << 5)
+
+#define LOWPROBE_TOPOLOGY (1 << 1)
+#define LOWPROBE_SUPERBLOCKS (1 << 2)
+
+#define BLKID_EXIT_NOTFOUND 2 /* token or device not found */
+#define BLKID_EXIT_OTHER 4 /* bad usage or other error */
+#define BLKID_EXIT_AMBIVAL 8 /* ambivalent low-level probing detected */
+
+#include <blkid.h>
+
+#include "ismounted.h"
+
+#define STRTOXX_EXIT_CODE BLKID_EXIT_OTHER /* strtoxx_or_err() */
+#include "strutils.h"
+#define OPTUTILS_EXIT_CODE BLKID_EXIT_OTHER /* exclusive_option() */
+#include "optutils.h"
+
+#include "closestream.h"
+#include "ttyutils.h"
+
+const char *progname = "blkid";
+
+int raw_chars;
+
+static void print_version(FILE *out)
+{
+ fprintf(out, "%s from %s (libblkid %s, %s)\n",
+ progname, PACKAGE_STRING, LIBBLKID_VERSION, LIBBLKID_DATE);
+}
+
+static void usage(int error)
+{
+ FILE *out = error ? stderr : stdout;
+
+ print_version(out);
+ fprintf(out,
+ "Usage:\n"
+ " %1$s -L <label> | -U <uuid>\n\n"
+ " %1$s [-c <file>] [-ghlLv] [-o <format>] [-s <tag>] \n"
+ " [-t <token>] [<dev> ...]\n\n"
+ " %1$s -p [-s <tag>] [-O <offset>] [-S <size>] \n"
+ " [-o <format>] <dev> ...\n\n"
+ " %1$s -i [-s <tag>] [-o <format>] <dev> ...\n\n"
+ "Options:\n"
+ " -c <file> read from <file> instead of reading from the default\n"
+ " cache file (-c /dev/null means no cache)\n"
+ " -d don't encode non-printing characters\n"
+ " -h print this usage message and exit\n"
+ " -g garbage collect the blkid cache\n"
+ " -o <format> output format; can be one of:\n"
+ " value, device, export or full; (default: full)\n"
+ " -k list all known filesystems/RAIDs and exit\n"
+ " -s <tag> show specified tag(s) (default show all tags)\n"
+ " -t <token> find device with a specific token (NAME=value pair)\n"
+ " -l look up only first device with token specified by -t\n"
+ " -L <label> convert LABEL to device name\n"
+ " -U <uuid> convert UUID to device name\n"
+ " -v print version and exit\n"
+ " <dev> specify device(s) to probe (default: all devices)\n\n"
+ "Low-level probing options:\n"
+ " -p low-level superblocks probing (bypass cache)\n"
+ " -i gather information about I/O limits\n"
+ " -S <size> overwrite device size\n"
+ " -O <offset> probe at the given offset\n"
+ " -u <list> filter by \"usage\" (e.g. -u filesystem,raid)\n"
+ " -n <list> filter by filesystem type (e.g. -n vfat,ext3)\n"
+ "\n", progname);
+
+ exit(error);
+}
+
+/*
+ * This function does "safe" printing. It will convert non-printable
+ * ASCII characters using '^' and M- notation.
+ */
+static void safe_print(const char *cp, int len)
+{
+ unsigned char ch;
+
+ if (len < 0)
+ len = strlen(cp);
+
+ while (len--) {
+ ch = *cp++;
+ if (!raw_chars) {
+ if (ch >= 128) {
+ fputs("M-", stdout);
+ ch -= 128;
+ }
+ if ((ch < 32) || (ch == 0x7f)) {
+ fputc('^', stdout);
+ ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */
+ }
+ }
+ fputc(ch, stdout);
+ }
+}
+
+static int pretty_print_word(const char *str, int max_len,
+ int left_len, int overflow_nl)
+{
+ int len = strlen(str) + left_len;
+ int ret = 0;
+
+ fputs(str, stdout);
+ if (overflow_nl && len > max_len) {
+ fputc('\n', stdout);
+ len = 0;
+ } else if (len > max_len)
+ ret = len - max_len;
+ do
+ fputc(' ', stdout);
+ while (len++ < max_len);
+ return ret;
+}
+
+static void pretty_print_line(const char *device, const char *fs_type,
+ const char *label, const char *mtpt,
+ const char *uuid)
+{
+ static int device_len = 10, fs_type_len = 7;
+ static int label_len = 8, mtpt_len = 14;
+ static int term_width = -1;
+ int len, w;
+
+ if (term_width < 0) {
+ term_width = get_terminal_width();
+ if (term_width <= 0)
+ term_width = 80;
+ }
+ if (term_width > 80) {
+ term_width -= 80;
+ w = term_width / 10;
+ if (w > 8)
+ w = 8;
+ term_width -= 2*w;
+ label_len += w;
+ fs_type_len += w;
+ w = term_width/2;
+ device_len += w;
+ mtpt_len +=w;
+ }
+
+ len = pretty_print_word(device, device_len, 0, 1);
+ len = pretty_print_word(fs_type, fs_type_len, len, 0);
+ len = pretty_print_word(label, label_len, len, 0);
+ pretty_print_word(mtpt, mtpt_len, len, 0);
+
+ fputs(uuid, stdout);
+ fputc('\n', stdout);
+}
+
+static void pretty_print_dev(blkid_dev dev)
+{
+ blkid_tag_iterate iter;
+ const char *type, *value, *devname;
+ const char *uuid = "", *fs_type = "", *label = "";
+ int len, mount_flags;
+ char mtpt[80];
+ int retval;
+
+ if (dev == NULL) {
+ pretty_print_line("device", "fs_type", "label",
+ "mount point", "UUID");
+ for (len=get_terminal_width()-1; len > 0; len--)
+ fputc('-', stdout);
+ fputc('\n', stdout);
+ return;
+ }
+
+ devname = blkid_dev_devname(dev);
+ if (access(devname, F_OK))
+ return;
+
+ /* Get the uuid, label, type */
+ iter = blkid_tag_iterate_begin(dev);
+ while (blkid_tag_next(iter, &type, &value) == 0) {
+ if (!strcmp(type, "UUID"))
+ uuid = value;
+ if (!strcmp(type, "TYPE"))
+ fs_type = value;
+ if (!strcmp(type, "LABEL"))
+ label = value;
+ }
+ blkid_tag_iterate_end(iter);
+
+ /* Get the mount point */
+ mtpt[0] = 0;
+ retval = check_mount_point(devname, &mount_flags, mtpt, sizeof(mtpt));
+ if (retval == 0) {
+ if (mount_flags & MF_MOUNTED) {
+ if (!mtpt[0])
+ strcpy(mtpt, "(mounted, mtpt unknown)");
+ } else if (mount_flags & MF_BUSY)
+ strcpy(mtpt, "(in use)");
+ else
+ strcpy(mtpt, "(not mounted)");
+ }
+
+ pretty_print_line(devname, fs_type, label, mtpt, uuid);
+}
+
+static void print_udev_format(const char *name, const char *value)
+{
+ char enc[265], safe[256];
+ size_t namelen = strlen(name);
+
+ *safe = *enc = '\0';
+
+ if (!strcmp(name, "TYPE") || !strcmp(name, "VERSION")) {
+ blkid_encode_string(value, enc, sizeof(enc));
+ printf("ID_FS_%s=%s\n", name, enc);
+
+ } else if (!strcmp(name, "UUID") ||
+ !strcmp(name, "LABEL") ||
+ !strcmp(name, "UUID_SUB")) {
+
+ blkid_safe_string(value, safe, sizeof(safe));
+ printf("ID_FS_%s=%s\n", name, safe);
+
+ blkid_encode_string(value, enc, sizeof(enc));
+ printf("ID_FS_%s_ENC=%s\n", name, enc);
+
+ } else if (!strcmp(name, "PTTYPE")) {
+ printf("ID_PART_TABLE_TYPE=%s\n", value);
+
+ } else if (!strcmp(name, "PART_ENTRY_NAME") ||
+ !strcmp(name, "PART_ENTRY_TYPE")) {
+
+ blkid_encode_string(value, enc, sizeof(enc));
+ printf("ID_%s=%s\n", name, enc);
+
+ } else if (!strncmp(name, "PART_ENTRY_", 11))
+ printf("ID_%s=%s\n", name, value);
+
+ else if (namelen >= 15 && (
+ !strcmp(name + (namelen - 12), "_SECTOR_SIZE") ||
+ !strcmp(name + (namelen - 8), "_IO_SIZE") ||
+ !strcmp(name, "ALIGNMENT_OFFSET")))
+ printf("ID_IOLIMIT_%s=%s\n", name, value);
+ else
+ printf("ID_FS_%s=%s\n", name, value);
+}
+
+static int has_item(char *ary[], const char *item)
+{
+ char **p;
+
+ for (p = ary; *p != NULL; p++)
+ if (!strcmp(item, *p))
+ return 1;
+ return 0;
+}
+
+static void print_value(int output, int num, const char *devname,
+ const char *value, const char *name, size_t valsz)
+{
+ if (output & OUTPUT_VALUE_ONLY) {
+ fputs(value, stdout);
+ fputc('\n', stdout);
+
+ } else if (output & OUTPUT_UDEV_LIST) {
+ print_udev_format(name, value);
+
+ } else if (output & OUTPUT_EXPORT_LIST) {
+ if (num == 1 && devname)
+ printf("DEVNAME=%s\n", devname);
+ fputs(name, stdout);
+ fputs("=", stdout);
+ safe_print(value, valsz);
+ fputs("\n", stdout);
+
+ } else {
+ if (num == 1 && devname)
+ printf("%s: ", devname);
+ fputs(name, stdout);
+ fputs("=\"", stdout);
+ safe_print(value, valsz);
+ fputs("\" ", stdout);
+ }
+}
+
+static void print_tags(blkid_dev dev, char *show[], int output)
+{
+ blkid_tag_iterate iter;
+ const char *type, *value, *devname;
+ int num = 1;
+ static int first = 1;
+
+ if (!dev)
+ return;
+
+ if (output & OUTPUT_PRETTY_LIST) {
+ pretty_print_dev(dev);
+ return;
+ }
+
+ devname = blkid_dev_devname(dev);
+
+ if (output & OUTPUT_DEVICE_ONLY) {
+ printf("%s\n", devname);
+ return;
+ }
+
+ iter = blkid_tag_iterate_begin(dev);
+ while (blkid_tag_next(iter, &type, &value) == 0) {
+ if (show[0] && !has_item(show, type))
+ continue;
+
+ if (num == 1 && !first &&
+ (output & (OUTPUT_UDEV_LIST | OUTPUT_EXPORT_LIST)))
+ /* add extra line between output from more devices */
+ fputc('\n', stdout);
+
+ print_value(output, num++, devname, value, type, strlen(value));
+ }
+ blkid_tag_iterate_end(iter);
+
+ if (num > 1) {
+ if (!(output & (OUTPUT_VALUE_ONLY | OUTPUT_UDEV_LIST |
+ OUTPUT_EXPORT_LIST)))
+ printf("\n");
+ first = 0;
+ }
+}
+
+
+static int append_str(char **res, size_t *sz, const char *a, const char *b)
+{
+ char *str = *res;
+ size_t asz = a ? strlen(a) : 0;
+ size_t bsz = b ? strlen(b) : 0;
+ size_t len = *sz + asz + bsz;
+
+ if (!len)
+ return -1;
+
+ str = realloc(str, len + 1);
+ if (!str) {
+ free(*res);
+ return -1;
+ }
+
+ *res = str;
+ str += *sz;
+
+ if (a) {
+ memcpy(str, a, asz);
+ str += asz;
+ }
+ if (b) {
+ memcpy(str, b, bsz);
+ str += bsz;
+ }
+ *str = '\0';
+ *sz = len;
+ return 0;
+}
+
+/*
+ * Compose and print ID_FS_AMBIVALENT for udev
+ */
+static int print_udev_ambivalent(blkid_probe pr)
+{
+ char *val = NULL;
+ size_t valsz = 0;
+ int count = 0, rc = -1;
+
+ while (!blkid_do_probe(pr)) {
+ const char *usage_txt = NULL, *type = NULL, *version = NULL;
+ char enc[256];
+
+ blkid_probe_lookup_value(pr, "USAGE", &usage_txt, NULL);
+ blkid_probe_lookup_value(pr, "TYPE", &type, NULL);
+ blkid_probe_lookup_value(pr, "VERSION", &version, NULL);
+
+ if (!usage_txt || !type)
+ continue;
+
+ blkid_encode_string(usage_txt, enc, sizeof(enc));
+ if (append_str(&val, &valsz, enc, ":"))
+ goto done;
+
+ blkid_encode_string(type, enc, sizeof(enc));
+ if (append_str(&val, &valsz, enc, version ? ":" : " "))
+ goto done;
+
+ if (version) {
+ blkid_encode_string(version, enc, sizeof(enc));
+ if (append_str(&val, &valsz, enc, " "))
+ goto done;
+ }
+ count++;
+ }
+
+ if (count > 1) {
+ *(val + valsz - 1) = '\0'; /* rem tailing whitespace */
+ printf("ID_FS_AMBIVALEN=%s\n", val);
+ rc = 0;
+ }
+done:
+ free(val);
+ return rc;
+}
+
+static int lowprobe_superblocks(blkid_probe pr)
+{
+ struct stat st;
+ int rc, fd = blkid_probe_get_fd(pr);
+
+ if (fd < 0 || fstat(fd, &st))
+ return -1;
+
+ blkid_probe_enable_partitions(pr, 1);
+
+ if (!S_ISCHR(st.st_mode) && blkid_probe_get_size(pr) <= 1024 * 1440 &&
+ blkid_probe_is_wholedisk(pr)) {
+ /*
+ * check if the small disk is partitioned, if yes then
+ * don't probe for filesystems.
+ */
+ blkid_probe_enable_superblocks(pr, 0);
+
+ rc = blkid_do_fullprobe(pr);
+ if (rc < 0)
+ return rc; /* -1 = error, 1 = nothing, 0 = succes */
+
+ if (blkid_probe_lookup_value(pr, "PTTYPE", NULL, NULL) == 0)
+ return 0; /* partition table detected */
+ }
+
+ blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
+ blkid_probe_enable_superblocks(pr, 1);
+
+ return blkid_do_safeprobe(pr);
+}
+
+static int lowprobe_topology(blkid_probe pr)
+{
+ /* enable topology probing only */
+ blkid_probe_enable_topology(pr, 1);
+
+ blkid_probe_enable_superblocks(pr, 0);
+ blkid_probe_enable_partitions(pr, 0);
+
+ return blkid_do_fullprobe(pr);
+}
+
+static int lowprobe_device(blkid_probe pr, const char *devname,
+ int chain, char *show[], int output,
+ blkid_loff_t offset, blkid_loff_t size)
+{
+ const char *data;
+ const char *name;
+ int nvals = 0, n, num = 1;
+ size_t len;
+ int fd;
+ int rc = 0;
+ static int first = 1;
+
+ fd = open(devname, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "error: %s: %m\n", devname);
+ return BLKID_EXIT_NOTFOUND;
+ }
+ if (blkid_probe_set_device(pr, fd, offset, size))
+ goto done;
+
+ if (chain & LOWPROBE_TOPOLOGY)
+ rc = lowprobe_topology(pr);
+ if (rc >= 0 && (chain & LOWPROBE_SUPERBLOCKS))
+ rc = lowprobe_superblocks(pr);
+ if (rc < 0)
+ goto done;
+
+ if (!rc)
+ nvals = blkid_probe_numof_values(pr);
+
+ if (nvals &&
+ !(chain & LOWPROBE_TOPOLOGY) &&
+ !(output & OUTPUT_UDEV_LIST) &&
+ !blkid_probe_has_value(pr, "TYPE") &&
+ !blkid_probe_has_value(pr, "PTTYPE"))
+ /*
+ * Ignore probing result if there is not any filesystem or
+ * partition table on the device and udev output is not
+ * requested.
+ *
+ * The udev db stores information about partitions, so
+ * PART_ENTRY_* values are alway important.
+ */
+ nvals = 0;
+
+ if (nvals && !first && output & (OUTPUT_UDEV_LIST | OUTPUT_EXPORT_LIST))
+ /* add extra line between output from devices */
+ fputc('\n', stdout);
+
+ if (nvals && (output & OUTPUT_DEVICE_ONLY)) {
+ printf("%s\n", devname);
+ goto done;
+ }
+
+ for (n = 0; n < nvals; n++) {
+ if (blkid_probe_get_value(pr, n, &name, &data, &len))
+ continue;
+ if (show[0] && !has_item(show, name))
+ continue;
+ len = strnlen((char *) data, len);
+ print_value(output, num++, devname, (char *) data, name, len);
+ }
+
+ if (first)
+ first = 0;
+ if (nvals >= 1 && !(output & (OUTPUT_VALUE_ONLY |
+ OUTPUT_UDEV_LIST | OUTPUT_EXPORT_LIST)))
+ printf("\n");
+done:
+ if (rc == -2) {
+ if (output & OUTPUT_UDEV_LIST)
+ print_udev_ambivalent(pr);
+ else
+ fprintf(stderr,
+ "%s: ambivalent result (probably more "
+ "filesystems on the device, use wipefs(8) "
+ "to see more details)\n",
+ devname);
+ }
+ close(fd);
+
+ if (rc == -2)
+ return BLKID_EXIT_AMBIVAL; /* ambivalent probing result */
+ if (!nvals)
+ return BLKID_EXIT_NOTFOUND; /* nothing detected */
+
+ return 0; /* success */
+}
+
+/* converts comma separated list to BLKID_USAGE_* mask */
+static int list_to_usage(const char *list, int *flag)
+{
+ int mask = 0;
+ const char *word = NULL, *p = list;
+
+ if (p && strncmp(p, "no", 2) == 0) {
+ *flag = BLKID_FLTR_NOTIN;
+ p += 2;
+ }
+ if (!p || !*p)
+ goto err;
+ while(p) {
+ word = p;
+ p = strchr(p, ',');
+ if (p)
+ p++;
+ if (!strncmp(word, "filesystem", 10))
+ mask |= BLKID_USAGE_FILESYSTEM;
+ else if (!strncmp(word, "raid", 4))
+ mask |= BLKID_USAGE_RAID;
+ else if (!strncmp(word, "crypto", 6))
+ mask |= BLKID_USAGE_CRYPTO;
+ else if (!strncmp(word, "other", 5))
+ mask |= BLKID_USAGE_OTHER;
+ else
+ goto err;
+ }
+ return mask;
+err:
+ *flag = 0;
+ fprintf(stderr, "unknown kerword in -u <list> argument: '%s'\n",
+ word ? word : list);
+ exit(BLKID_EXIT_OTHER);
+}
+
+/* converts comma separated list to types[] */
+static char **list_to_types(const char *list, int *flag)
+{
+ int i;
+ const char *p = list;
+ char **res = NULL;
+
+ if (p && strncmp(p, "no", 2) == 0) {
+ *flag = BLKID_FLTR_NOTIN;
+ p += 2;
+ }
+ if (!p || !*p) {
+ fprintf(stderr, "error: -u <list> argument is empty\n");
+ goto err;
+ }
+ for (i = 1; p && (p = strchr(p, ',')); i++, p++);
+
+ res = calloc(i + 1, sizeof(char *));
+ if (!res)
+ goto err_mem;
+ p = *flag & BLKID_FLTR_NOTIN ? list + 2 : list;
+ i = 0;
+
+ while(p) {
+ const char *word = p;
+ p = strchr(p, ',');
+ res[i] = p ? strndup(word, p - word) : strdup(word);
+ if (!res[i++])
+ goto err_mem;
+ if (p)
+ p++;
+ }
+ res[i] = NULL;
+ return res;
+err_mem:
+ fprintf(stderr, "out of memory\n");
+err:
+ *flag = 0;
+ free(res);
+ exit(BLKID_EXIT_OTHER);
+}
+
+static void free_types_list(char *list[])
+{
+ char **n;
+
+ if (!list)
+ return;
+ for (n = list; *n; n++)
+ free(*n);
+ free(list);
+}
+
+int main(int argc, char **argv)
+{
+ blkid_cache cache = NULL;
+ char **devices = NULL;
+ char *show[128] = { NULL, };
+ char *search_type = NULL, *search_value = NULL;
+ char *read = NULL;
+ int fltr_usage = 0;
+ char **fltr_type = NULL;
+ int fltr_flag = BLKID_FLTR_ONLYIN;
+ unsigned int numdev = 0, numtag = 0;
+ int version = 0;
+ int err = BLKID_EXIT_OTHER;
+ unsigned int i;
+ int output_format = 0;
+ int lookup = 0, gc = 0, lowprobe = 0, eval = 0;
+ int c;
+ uintmax_t offset = 0, size = 0;
+
+ static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */
+ { 'n','u' },
+ { 0 }
+ };
+ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+
+ show[0] = NULL;
+ atexit(close_stdout);
+
+ while ((c = getopt (argc, argv,
+ "c:df:ghilL:n:ko:O:ps:S:t:u:U:w:v")) != EOF) {
+
+ err_exclusive_options(c, NULL, excl, excl_st);
+
+ switch (c) {
+ case 'c':
+ if (optarg && !*optarg)
+ read = NULL;
+ else
+ read = optarg;
+ break;
+ case 'd':
+ raw_chars = 1;
+ break;
+ case 'L':
+ eval++;
+ search_value = strdup(optarg);
+ search_type = strdup("LABEL");
+ break;
+ case 'n':
+ fltr_type = list_to_types(optarg, &fltr_flag);
+ break;
+ case 'u':
+ fltr_usage = list_to_usage(optarg, &fltr_flag);
+ break;
+ case 'U':
+ eval++;
+ search_value = strdup(optarg);
+ search_type = strdup("UUID");
+ break;
+ case 'i':
+ lowprobe |= LOWPROBE_TOPOLOGY;
+ break;
+ case 'l':
+ lookup++;
+ break;
+ case 'g':
+ gc = 1;
+ break;
+ case 'k':
+ {
+ size_t idx = 0;
+ const char *name = NULL;
+
+ while (blkid_superblocks_get_name(idx++, &name, NULL) == 0)
+ printf("%s\n", name);
+ exit(EXIT_SUCCESS);
+ }
+ case 'o':
+ if (!strcmp(optarg, "value"))
+ output_format = OUTPUT_VALUE_ONLY;
+ else if (!strcmp(optarg, "device"))
+ output_format = OUTPUT_DEVICE_ONLY;
+ else if (!strcmp(optarg, "list"))
+ output_format = OUTPUT_PRETTY_LIST; /* deprecated */
+ else if (!strcmp(optarg, "udev"))
+ output_format = OUTPUT_UDEV_LIST;
+ else if (!strcmp(optarg, "export"))
+ output_format = OUTPUT_EXPORT_LIST;
+ else if (!strcmp(optarg, "full"))
+ output_format = 0;
+ else {
+ fprintf(stderr, "Invalid output format %s. "
+ "Choose from value,\n\t"
+ "device, list, udev or full\n", optarg);
+ exit(BLKID_EXIT_OTHER);
+ }
+ break;
+ case 'O':
+ offset = strtosize_or_err(optarg, "invalid offset argument");
+ break;
+ case 'p':
+ lowprobe |= LOWPROBE_SUPERBLOCKS;
+ break;
+ case 's':
+ if (numtag + 1 >= sizeof(show) / sizeof(*show)) {
+ fprintf(stderr, "Too many tags specified\n");
+ usage(err);
+ }
+ show[numtag++] = optarg;
+ show[numtag] = NULL;
+ break;
+ case 'S':
+ size = strtosize_or_err(optarg, "invalid size argument");
+ break;
+ case 't':
+ if (search_type) {
+ fprintf(stderr, "Can only search for "
+ "one NAME=value pair\n");
+ usage(err);
+ }
+ if (blkid_parse_tag_string(optarg,
+ &search_type,
+ &search_value)) {
+ fprintf(stderr, "-t needs NAME=value pair\n");
+ usage(err);
+ }
+ break;
+ case 'v':
+ version = 1;
+ break;
+ case 'w':
+ /* ignore - backward compatibility */
+ break;
+ case 'h':
+ err = 0;
+ /* fallthrough */
+ default:
+ usage(err);
+ }
+ }
+
+
+ /* The rest of the args are device names */
+ if (optind < argc) {
+ devices = calloc(argc - optind, sizeof(char *));
+ if (!devices) {
+ fprintf(stderr, "Failed to allocate device name array\n");
+ goto exit;
+ }
+
+ while (optind < argc)
+ devices[numdev++] = argv[optind++];
+ }
+
+ if (version) {
+ print_version(stdout);
+ goto exit;
+ }
+
+ /* convert LABEL/UUID lookup to evaluate request */
+ if (lookup && output_format == OUTPUT_DEVICE_ONLY && search_type &&
+ (!strcmp(search_type, "LABEL") || !strcmp(search_type, "UUID"))) {
+ eval++;
+ lookup = 0;
+ }
+
+ if (!lowprobe && !eval && blkid_get_cache(&cache, read) < 0)
+ goto exit;
+
+ if (gc) {
+ blkid_gc_cache(cache);
+ err = 0;
+ goto exit;
+ }
+ err = BLKID_EXIT_NOTFOUND;
+
+ if (eval == 0 && (output_format & OUTPUT_PRETTY_LIST)) {
+ if (lowprobe) {
+ fprintf(stderr, "The low-level probing mode does not "
+ "support 'list' output format\n");
+ exit(BLKID_EXIT_OTHER);
+ }
+ pretty_print_dev(NULL);
+ }
+
+ if (lowprobe) {
+ /*
+ * Low-level API
+ */
+ blkid_probe pr;
+
+ if (!numdev) {
+ fprintf(stderr, "The low-level probing mode "
+ "requires a device\n");
+ exit(BLKID_EXIT_OTHER);
+ }
+
+ /* automatically enable 'export' format for I/O Limits */
+ if (!output_format && (lowprobe & LOWPROBE_TOPOLOGY))
+ output_format = OUTPUT_EXPORT_LIST;
+
+ pr = blkid_new_probe();
+ if (!pr)
+ goto exit;
+
+ if (lowprobe & LOWPROBE_SUPERBLOCKS) {
+ blkid_probe_set_superblocks_flags(pr,
+ BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
+ BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE |
+ BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION);
+
+ if (fltr_usage && blkid_probe_filter_superblocks_usage(
+ pr, fltr_flag, fltr_usage))
+ goto exit;
+
+ else if (fltr_type && blkid_probe_filter_superblocks_type(
+ pr, fltr_flag, fltr_type))
+ goto exit;
+ }
+
+ for (i = 0; i < numdev; i++) {
+ err = lowprobe_device(pr, devices[i], lowprobe, show,
+ output_format,
+ (blkid_loff_t) offset,
+ (blkid_loff_t) size);
+ if (err)
+ break;
+ }
+ blkid_free_probe(pr);
+ } else if (eval) {
+ /*
+ * Evaluate API
+ */
+ char *res = blkid_evaluate_tag(search_type, search_value, NULL);
+ if (res) {
+ err = 0;
+ printf("%s\n", res);
+ }
+ } else if (lookup) {
+ /*
+ * Classic (cache based) API
+ */
+ blkid_dev dev;
+
+ if (!search_type) {
+ fprintf(stderr, "The lookup option requires a "
+ "search type specified using -t\n");
+ exit(BLKID_EXIT_OTHER);
+ }
+ /* Load any additional devices not in the cache */
+ for (i = 0; i < numdev; i++)
+ blkid_get_dev(cache, devices[i], BLKID_DEV_NORMAL);
+
+ if ((dev = blkid_find_dev_with_tag(cache, search_type,
+ search_value))) {
+ print_tags(dev, show, output_format);
+ err = 0;
+ }
+ /* If we didn't specify a single device, show all available devices */
+ } else if (!numdev) {
+ blkid_dev_iterate iter;
+ blkid_dev dev;
+
+ blkid_probe_all(cache);
+
+ iter = blkid_dev_iterate_begin(cache);
+ blkid_dev_set_search(iter, search_type, search_value);
+ while (blkid_dev_next(iter, &dev) == 0) {
+ dev = blkid_verify(cache, dev);
+ if (!dev)
+ continue;
+ print_tags(dev, show, output_format);
+ err = 0;
+ }
+ blkid_dev_iterate_end(iter);
+ /* Add all specified devices to cache (optionally display tags) */
+ } else for (i = 0; i < numdev; i++) {
+ blkid_dev dev = blkid_get_dev(cache, devices[i],
+ BLKID_DEV_NORMAL);
+
+ if (dev) {
+ if (search_type &&
+ !blkid_dev_has_tag(dev, search_type,
+ search_value))
+ continue;
+ print_tags(dev, show, output_format);
+ err = 0;
+ }
+ }
+
+exit:
+ free(search_type);
+ free(search_value);
+ free_types_list(fltr_type);
+ if (!lowprobe && !eval)
+ blkid_put_cache(cache);
+ free(devices);
+ return err;
+}
diff --git a/misc-utils/cal.1 b/misc-utils/cal.1
new file mode 100644
index 0000000..4d9b3d1
--- /dev/null
+++ b/misc-utils/cal.1
@@ -0,0 +1,97 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Kim Letkeman.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)cal.1 8.1 (Berkeley) 6/6/93
+.\"
+.TH CAL 1 "June 2011" "util-linux" "User Commands"
+.SH NAME
+cal \- display a calendar
+.SH SYNOPSIS
+.B cal
+[\fIoptions\fR] [[[\fIday\fR] \fImonth\fR] \fIyear\fR]
+.SH DESCRIPTION
+.B cal
+displays a simple calendar. If no arguments are specified, the current
+month is displayed.
+.SH OPTIONS
+.TP
+\fB\-1\fR, \fB\-\-one\fR
+Display single month output.
+(This is the default.)
+.TP
+\fB\-3\fR, \fB\-\-three\fR
+Display prev/current/next month output.
+.TP
+\fB\-s\fR, \fB\-\-sunday\fR
+Display Sunday as the first day of the week.
+.TP
+\fB\-m\fR, \fB\-\-monday\fR
+Display Monday as the first day of the week.
+.TP
+\fB\-j\fR, \fB\-\-julian\fR
+Display Julian dates (days one-based, numbered from January 1).
+.TP
+\fB\-y\fR, \fB\-\-year\fR
+Display a calendar for the current year.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Display version information and exit.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display help screen and exit.
+.SH PARAMETERS
+A single parameter specifies the year (1 - 9999) to be displayed; note the
+year must be fully specified:
+.B "cal 89"
+will not display a calendar for 1989.
+.PP
+Two parameters denote the month (1 - 12) and year.
+.PP
+Three parameters denote the day (1-31), month and year, and the day will be
+highlighted if the calendar is displayed on a terminal. If no parameters are
+specified, the current month's calendar is displayed.
+.PP
+A year starts on Jan 1. The first day of the week is determined by the
+locale.
+.PP
+The Gregorian Reformation is assumed to have occurred in 1752 on the 3rd of
+September. By this time, most countries had recognized the reformation
+(although a few did not recognize it until the early 1900's). Ten days
+following that date were eliminated by the reformation, so the calendar for
+that month is a bit unusual.
+.SH HISTORY
+A cal command appeared in Version 6 AT&T UNIX.
+.SH AVAILABILITY
+The cal command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/misc-utils/cal.c b/misc-utils/cal.c
new file mode 100644
index 0000000..60e85df
--- /dev/null
+++ b/misc-utils/cal.c
@@ -0,0 +1,800 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kim Letkeman.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* 1999-02-01 Jean-Francois Bignolles: added option '-m' to display
+ * monday as the first day of the week.
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ *
+ * 2000-09-01 Michael Charles Pruznick <dummy@netwiz.net>
+ * Added "-3" option to print prev/next month with current.
+ * Added over-ridable default NUM_MONTHS and "-1" option to
+ * get traditional output when -3 is the default. I hope that
+ * enough people will like -3 as the default that one day the
+ * product can be shipped that way.
+ *
+ * 2001-05-07 Pablo Saratxaga <pablo@mandrakesoft.com>
+ * Fixed the bugs with multi-byte charset (zg: cjk, utf-8)
+ * displaying. made the 'month year' ("%s %d") header translatable
+ * so it can be adapted to conventions used by different languages
+ * added support to read "first_weekday" locale information
+ * still to do: support for 'cal_direction' (will require a major
+ * rewrite of the displaying) and proper handling of RTL scripts
+ */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "c.h"
+#include "closestream.h"
+#include "nls.h"
+#include "mbsalign.h"
+#include "strutils.h"
+
+#if defined(HAVE_LIBNCURSES) || defined(HAVE_LIBNCURSESW)
+
+#ifdef HAVE_NCURSES_H
+#include <ncurses.h>
+#elif defined(HAVE_NCURSES_NCURSES_H)
+#include <ncurses/ncurses.h>
+#endif
+
+#include <term.h> /* include after <curses.h> */
+
+static void
+my_setupterm(const char *term, int fildes, int *errret) {
+ setupterm((char*)term, fildes, errret);
+}
+
+static void
+my_putstring(char *s) {
+ putp(s);
+}
+
+static const char *
+my_tgetstr(char *s __attribute__ ((__unused__)), char *ss) {
+ const char* ret = tigetstr(ss);
+ if (!ret || ret==(char*)-1)
+ return "";
+ else
+ return ret;
+}
+
+#elif defined(HAVE_LIBTERMCAP)
+
+#include <termcap.h>
+
+char termbuffer[4096];
+char tcbuffer[4096];
+char *strbuf = termbuffer;
+
+static void
+my_setupterm(const char *term, int fildes, int *errret) {
+ *errret = tgetent(tcbuffer, term);
+}
+
+static void
+my_putstring(char *s) {
+ tputs (s, 1, putchar);
+}
+
+static const char *
+my_tgetstr(char *s, char *ss __attribute__ ((__unused__))) {
+ const char* ret = tgetstr(s, &strbuf);
+ if (!ret)
+ return "";
+ else
+ return ret;
+}
+
+#else /* ! (HAVE_LIBTERMCAP || HAVE_LIBNCURSES || HAVE_LIBNCURSESW) */
+
+static void
+my_putstring(char *s) {
+ fputs(s, stdout);
+}
+
+#endif
+
+
+const char *term="";
+const char *Senter="", *Sexit="";/* enter and exit standout mode */
+int Slen; /* strlen of Senter+Sexit */
+char *Hrow; /* pointer to highlighted row in month */
+
+#include "widechar.h"
+
+/* allow compile-time define to over-ride default */
+#ifndef NUM_MONTHS
+#define NUM_MONTHS 1
+#endif
+
+#if ( NUM_MONTHS != 1 && NUM_MONTHS !=3 )
+#error NUM_MONTHS must be 1 or 3
+#endif
+
+#define THURSDAY 4 /* for reformation */
+#define SATURDAY 6 /* 1 Jan 1 was a Saturday */
+
+#define FIRST_MISSING_DAY 639799 /* 3 Sep 1752 */
+#define NUMBER_MISSING_DAYS 11 /* 11 day correction */
+
+#define MAXDAYS 42 /* slots in a month array */
+#define SPACE -1 /* used in day array */
+
+static int days_in_month[2][13] = {
+ {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+};
+
+#define SEP1752_OFS 4 /* sep1752[4] is a Sunday */
+
+/* 1 Sep 1752 is represented by sep1752[6] and j_sep1752[6] */
+int sep1752[MAXDAYS+6] = {
+ SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, 1, 2, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE
+}, j_sep1752[MAXDAYS+6] = {
+ SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, 245, 246, 258, 259, 260,
+ 261, 262, 263, 264, 265, 266, 267,
+ 268, 269, 270, 271, 272, 273, 274,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE
+}, empty[MAXDAYS] = {
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE
+};
+
+#define DAY_LEN 3 /* 3 spaces per day */
+#define J_DAY_LEN 4 /* 4 spaces per day */
+#define WEEK_LEN 21 /* 7 days * 3 characters */
+#define J_WEEK_LEN 28 /* 7 days * 4 characters */
+#define HEAD_SEP 2 /* spaces between day headings */
+#define J_HEAD_SEP 2
+
+/* utf-8 can have up to 6 bytes per char; and an extra byte for ending \0 */
+char day_headings[WEEK_LEN*6+1];
+/* weekstart = 1 => " M Tu W Th F S S " */
+char j_day_headings[J_WEEK_LEN*6+1];
+/* weekstart = 1 => " M Tu W Th F S S " */
+const char *full_month[12];
+
+/* leap year -- account for gregorian reformation in 1752 */
+#define leap_year(yr) \
+ ((yr) <= 1752 ? !((yr) % 4) : \
+ (!((yr) % 4) && ((yr) % 100)) || !((yr) % 400))
+
+/* number of centuries since 1700, not inclusive */
+#define centuries_since_1700(yr) \
+ ((yr) > 1700 ? (yr) / 100 - 17 : 0)
+
+/* number of centuries since 1700 whose modulo of 400 is 0 */
+#define quad_centuries_since_1700(yr) \
+ ((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
+
+/* number of leap years between year 1 and this year, not inclusive */
+#define leap_years_since_year_1(yr) \
+ ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
+
+/* 0 => sunday, 1 => monday */
+int weekstart=0;
+int julian;
+
+#define TODAY_FLAG 0x400 /* flag day for highlighting */
+
+#define FMT_ST_LINES 8
+#define FMT_ST_CHARS 300 /* 90 suffices in most locales */
+struct fmt_st
+{
+ char s[FMT_ST_LINES][FMT_ST_CHARS];
+};
+
+char * ascii_day(char *, int);
+int center_str(const char* src, char* dest, size_t dest_size, size_t width);
+void center(const char *, size_t, int);
+void day_array(int, int, int, int *);
+int day_in_week(int, int, int);
+int day_in_year(int, int, int);
+void yearly(int, int);
+void j_yearly(int, int);
+void do_monthly(int, int, int, struct fmt_st*);
+void monthly(int, int, int);
+void monthly3(int, int, int);
+void trim_trailing_spaces(char *);
+static void __attribute__ ((__noreturn__)) usage(FILE * out);
+void headers_init(void);
+
+int
+main(int argc, char **argv) {
+ struct tm *local_time;
+ time_t now;
+ int ch, day, month, year, yflag;
+ int num_months = NUM_MONTHS;
+
+ static const struct option longopts[] = {
+ {"one", no_argument, NULL, '1'},
+ {"three", no_argument, NULL, '3'},
+ {"sunday", no_argument, NULL, 's'},
+ {"monday", no_argument, NULL, 'm'},
+ {"julian", no_argument, NULL, 'j'},
+ {"year", no_argument, NULL, 'y'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+#if defined(HAVE_LIBNCURSES) || defined(HAVE_LIBNCURSESW) || defined(HAVE_LIBTERMCAP)
+ if ((term = getenv("TERM"))) {
+ int ret;
+ my_setupterm(term, 1, &ret);
+ if (ret > 0) {
+ Senter = my_tgetstr("so","smso");
+ Sexit = my_tgetstr("se","rmso");
+ Slen = strlen(Senter) + strlen(Sexit);
+ }
+ }
+#endif
+
+/*
+ * The traditional Unix cal utility starts the week at Sunday,
+ * while ISO 8601 starts at Monday. We read the start day from
+ * the locale database, which can be overridden with the
+ * -s (Sunday) or -m (Monday) options.
+ */
+#if HAVE_DECL__NL_TIME_WEEK_1STDAY
+ /*
+ * You need to use 2 locale variables to get the first day of the week.
+ * This is needed to support first_weekday=2 and first_workday=1 for
+ * the rare case where working days span across 2 weeks.
+ * This shell script shows the combinations and calculations involved:
+ *
+ * for LANG in en_US ru_RU fr_FR csb_PL POSIX; do
+ * printf "%s:\t%s + %s -1 = " $LANG $(locale week-1stday first_weekday)
+ * date -d"$(locale week-1stday) +$(($(locale first_weekday)-1))day" +%w
+ * done
+ *
+ * en_US: 19971130 + 1 -1 = 0 #0 = sunday
+ * ru_RU: 19971130 + 2 -1 = 1
+ * fr_FR: 19971201 + 1 -1 = 1
+ * csb_PL: 19971201 + 2 -1 = 2
+ * POSIX: 19971201 + 7 -1 = 0
+ */
+ {
+ int wfd;
+ union { unsigned int word; char *string; } val;
+ val.string = nl_langinfo(_NL_TIME_WEEK_1STDAY);
+
+ wfd = val.word;
+ wfd = day_in_week(wfd % 100, (wfd / 100) % 100, wfd / (100 * 100));
+ weekstart = (wfd + *nl_langinfo(_NL_TIME_FIRST_WEEKDAY) - 1) % 7;
+ }
+#endif
+
+ yflag = 0;
+ while ((ch = getopt_long(argc, argv, "13mjsyVh", longopts, NULL)) != -1)
+ switch(ch) {
+ case '1':
+ num_months = 1; /* default */
+ break;
+ case '3':
+ num_months = 3;
+ break;
+ case 's':
+ weekstart = 0; /* default */
+ break;
+ case 'm':
+ weekstart = 1;
+ break;
+ case 'j':
+ julian = 1;
+ break;
+ case 'y':
+ yflag = 1;
+ break;
+ case 'V':
+ printf(_("%s from %s\n"), program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ case '?':
+ default:
+ usage(stderr);
+ }
+ argc -= optind;
+ argv += optind;
+
+ time(&now);
+ local_time = localtime(&now);
+
+ day = month = year = 0;
+ switch(argc) {
+ case 3:
+ day = strtos32_or_err(*argv++, _("illegal day value"));
+ if (day < 1 || 31 < day)
+ errx(EXIT_FAILURE, _("illegal day value: use 1-%d"), 31);
+ /* FALLTHROUGH */
+ case 2:
+ month = strtos32_or_err(*argv++, _("illegal month value: use 1-12"));
+ if (month < 1 || 12 < month)
+ errx(EXIT_FAILURE, _("illegal month value: use 1-12"));
+ /* FALLTHROUGH */
+ case 1:
+ year = strtos32_or_err(*argv++, _("illegal year value: use 1-9999"));
+ if (year < 1 || 9999 < year)
+ errx(EXIT_FAILURE, _("illegal year value: use 1-9999"));
+ if (day) {
+ int dm = days_in_month[leap_year(year)][month];
+ if (day > dm)
+ errx(EXIT_FAILURE, _("illegal day value: use 1-%d"), dm);
+ day = day_in_year(day, month, year);
+ } else if ((local_time->tm_year + 1900) == year) {
+ day = local_time->tm_yday + 1;
+ }
+ if (!month)
+ yflag=1;
+ break;
+ case 0:
+ day = local_time->tm_yday + 1;
+ year = local_time->tm_year + 1900;
+ month = local_time->tm_mon + 1;
+ break;
+ default:
+ usage(stderr);
+ }
+ headers_init();
+
+ if (!isatty(1))
+ day = 0; /* don't highlight */
+
+ if (yflag && julian)
+ j_yearly(day, year);
+ else if (yflag)
+ yearly(day, year);
+ else if (num_months == 1)
+ monthly(day, month, year);
+ else if (num_months == 3)
+ monthly3(day, month, year);
+
+ return EXIT_SUCCESS;
+}
+
+void headers_init(void)
+{
+ int i, wd;
+ char *cur_dh = day_headings, *cur_j_dh = j_day_headings;
+
+ strcpy(day_headings, "");
+ strcpy(j_day_headings, "");
+
+ for (i = 0; i < 7; i++) {
+ ssize_t space_left;
+ wd = (i + weekstart) % 7;
+
+ if (i)
+ strcat(cur_dh++, " ");
+ space_left =
+ sizeof(day_headings) - (cur_dh - day_headings);
+ if (space_left <= 2)
+ break;
+ cur_dh +=
+ center_str(nl_langinfo(ABDAY_1 + wd), cur_dh,
+ space_left, 2);
+
+ if (i)
+ strcat(cur_j_dh++, " ");
+ space_left =
+ sizeof(j_day_headings) - (cur_j_dh - j_day_headings);
+ if (space_left <= 3)
+ break;
+ cur_j_dh +=
+ center_str(nl_langinfo(ABDAY_1 + wd), cur_j_dh,
+ space_left, 3);
+ }
+
+ for (i = 0; i < 12; i++)
+ full_month[i] = nl_langinfo(MON_1 + i);
+}
+
+void
+do_monthly(int day, int month, int year, struct fmt_st *out) {
+ int col, row, days[MAXDAYS];
+ char *p, lineout[FMT_ST_CHARS];
+ int width = (julian ? J_WEEK_LEN : WEEK_LEN) - 1;
+
+ day_array(day, month, year, days);
+
+ /*
+ * %s is the month name, %d the year number.
+ * you can change the order and/or add something here; eg for
+ * Basque the translation should be: "%2$dko %1$s", and
+ * the Vietnamese should be "%s na(m %d", etc.
+ */
+ snprintf(lineout, sizeof(lineout), _("%s %d"),
+ full_month[month - 1], year);
+ center_str(lineout, out->s[0], ARRAY_SIZE(out->s[0]), width);
+
+ snprintf(out->s[1], FMT_ST_CHARS, "%s",
+ julian ? j_day_headings : day_headings);
+ for (row = 0; row < 6; row++) {
+ int has_hl = 0;
+ for (col = 0, p = lineout; col < 7; col++) {
+ int xd = days[row * 7 + col];
+ if (xd != SPACE && (xd & TODAY_FLAG))
+ has_hl = 1;
+ p = ascii_day(p, xd);
+ }
+ *p = '\0';
+ trim_trailing_spaces(lineout);
+ snprintf(out->s[row+2], FMT_ST_CHARS, "%s", lineout);
+ if (has_hl)
+ Hrow = out->s[row+2];
+ }
+}
+
+void
+monthly(int day, int month, int year) {
+ int i;
+ struct fmt_st out;
+
+ do_monthly(day, month, year, &out);
+ for (i = 0; i < FMT_ST_LINES; i++) {
+ my_putstring(out.s[i]);
+ putchar('\n');
+ }
+}
+
+void
+monthly3(int day, int month, int year) {
+ char lineout[FMT_ST_CHARS];
+ int i;
+ int width;
+ struct fmt_st out_prev;
+ struct fmt_st out_curm;
+ struct fmt_st out_next;
+ int prev_month, prev_year;
+ int next_month, next_year;
+
+ if (month == 1) {
+ prev_month = 12;
+ prev_year = year - 1;
+ } else {
+ prev_month = month - 1;
+ prev_year = year;
+ }
+ if (month == 12) {
+ next_month = 1;
+ next_year = year + 1;
+ } else {
+ next_month = month + 1;
+ next_year = year;
+ }
+
+ do_monthly(day, prev_month, prev_year, &out_prev);
+ do_monthly(day, month, year, &out_curm);
+ do_monthly(day, next_month, next_year, &out_next);
+
+ width = (julian ? J_WEEK_LEN : WEEK_LEN) -1;
+ for (i = 0; i < 2; i++)
+ printf("%s %s %s\n", out_prev.s[i], out_curm.s[i], out_next.s[i]);
+ for (i = 2; i < FMT_ST_LINES; i++) {
+ int w1, w2, w3;
+ w1 = w2 = w3 = width;
+
+#if defined(HAVE_LIBNCURSES) || defined(HAVE_LIBNCURSESW) || defined(HAVE_LIBTERMCAP)
+ /* adjust width to allow for non printable characters */
+ w1 += (out_prev.s[i] == Hrow ? Slen : 0);
+ w2 += (out_curm.s[i] == Hrow ? Slen : 0);
+ w3 += (out_next.s[i] == Hrow ? Slen : 0);
+#endif
+ snprintf(lineout, sizeof(lineout), "%-*s %-*s %-*s\n",
+ w1, out_prev.s[i],
+ w2, out_curm.s[i],
+ w3, out_next.s[i]);
+
+ my_putstring(lineout);
+ }
+}
+
+void
+j_yearly(int day, int year) {
+ int col, *dp, i, month, row, which_cal;
+ int days[12][MAXDAYS];
+ char *p, lineout[80];
+
+ snprintf(lineout, sizeof(lineout), "%d", year);
+ center(lineout, J_WEEK_LEN*2 + J_HEAD_SEP - 1, 0);
+ printf("\n\n");
+
+ for (i = 0; i < 12; i++)
+ day_array(day, i + 1, year, days[i]);
+ memset(lineout, ' ', sizeof(lineout) - 1);
+ lineout[sizeof(lineout) - 1] = '\0';
+ for (month = 0; month < 12; month += 2) {
+ center(full_month[month], J_WEEK_LEN-1, J_HEAD_SEP+1);
+ center(full_month[month + 1], J_WEEK_LEN-1, 0);
+ printf("\n%s%*s %s\n", j_day_headings, J_HEAD_SEP, "",
+ j_day_headings);
+ for (row = 0; row < 6; row++) {
+ p = lineout;
+ for (which_cal = 0; which_cal < 2; which_cal++) {
+ dp = &days[month + which_cal][row * 7];
+ for (col = 0; col < 7; col++)
+ p = ascii_day(p, *dp++);
+ p += sprintf(p, " ");
+ }
+ *p = '\0';
+ trim_trailing_spaces(lineout);
+ my_putstring(lineout);
+ putchar('\n');
+ }
+ }
+ printf("\n");
+}
+
+void
+yearly(int day, int year) {
+ int col, *dp, i, month, row, which_cal;
+ int days[12][MAXDAYS];
+ char *p, lineout[100];
+
+ snprintf(lineout, sizeof(lineout), "%d", year);
+ center(lineout, WEEK_LEN*3 + HEAD_SEP*2 - 1, 0);
+ printf("\n\n");
+
+ for (i = 0; i < 12; i++)
+ day_array(day, i + 1, year, days[i]);
+ memset(lineout, ' ', sizeof(lineout) - 1);
+ lineout[sizeof(lineout) - 1] = '\0';
+ for (month = 0; month < 12; month += 3) {
+ center(full_month[month], WEEK_LEN-1, HEAD_SEP+1);
+ center(full_month[month + 1], WEEK_LEN-1, HEAD_SEP+1);
+ center(full_month[month + 2], WEEK_LEN-1, 0);
+ printf("\n%s%*s %s%*s %s\n", day_headings, HEAD_SEP,
+ "", day_headings, HEAD_SEP, "", day_headings);
+ for (row = 0; row < 6; row++) {
+ p = lineout;
+ for (which_cal = 0; which_cal < 3; which_cal++) {
+ dp = &days[month + which_cal][row * 7];
+ for (col = 0; col < 7; col++)
+ p = ascii_day(p, *dp++);
+ p += sprintf(p, " ");
+ }
+ *p = '\0';
+ trim_trailing_spaces(lineout);
+ my_putstring(lineout);
+ putchar('\n');
+ }
+ }
+ putchar('\n');
+}
+
+/*
+ * day_array --
+ * Fill in an array of 42 integers with a calendar. Assume for a moment
+ * that you took the (maximum) 6 rows in a calendar and stretched them
+ * out end to end. You would have 42 numbers or spaces. This routine
+ * builds that array for any month from Jan. 1 through Dec. 9999.
+ */
+void
+day_array(int day, int month, int year, int *days) {
+ int julday, daynum, dw, dm;
+ int *d_sep1752;
+
+ if (month == 9 && year == 1752) {
+ int sep1752_ofs = (weekstart + SEP1752_OFS) % 7;
+ d_sep1752 = julian ? j_sep1752 : sep1752;
+ memcpy(days, d_sep1752 + sep1752_ofs, MAXDAYS * sizeof(int));
+ for (dm=0; dm<MAXDAYS; dm++)
+ if (j_sep1752[dm + sep1752_ofs] == day)
+ days[dm] |= TODAY_FLAG;
+ return;
+ }
+ memcpy(days, empty, MAXDAYS * sizeof(int));
+ dm = days_in_month[leap_year(year)][month];
+ dw = (day_in_week(1, month, year) - weekstart + 7) % 7;
+ julday = day_in_year(1, month, year);
+ daynum = julian ? julday : 1;
+ while (dm--) {
+ days[dw] = daynum++;
+ if (julday++ == day)
+ days[dw] |= TODAY_FLAG;
+ dw++;
+ }
+}
+
+/*
+ * day_in_year --
+ * return the 1 based day number within the year
+ */
+int
+day_in_year(int day, int month, int year) {
+ int i, leap;
+
+ leap = leap_year(year);
+ for (i = 1; i < month; i++)
+ day += days_in_month[leap][i];
+ return day;
+}
+
+/*
+ * day_in_week
+ * return the 0 based day number for any date from 1 Jan. 1 to
+ * 31 Dec. 9999. Assumes the Gregorian reformation eliminates
+ * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all
+ * missing days.
+ */
+int
+day_in_week(int day, int month, int year) {
+ long temp;
+
+ temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1)
+ + day_in_year(day, month, year);
+ if (temp < FIRST_MISSING_DAY)
+ return ((temp - 1 + SATURDAY) % 7);
+ if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS))
+ return (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
+ return (THURSDAY);
+}
+
+char *
+ascii_day(char *p, int day) {
+ int display, val;
+ int highlight = 0;
+ static char *aday[] = {
+ "",
+ " 1", " 2", " 3", " 4", " 5", " 6", " 7",
+ " 8", " 9", "10", "11", "12", "13", "14",
+ "15", "16", "17", "18", "19", "20", "21",
+ "22", "23", "24", "25", "26", "27", "28",
+ "29", "30", "31",
+ };
+
+ if (day == SPACE) {
+ int len = julian ? J_DAY_LEN : DAY_LEN;
+ memset(p, ' ', len);
+ return p+len;
+ }
+ if (day & TODAY_FLAG) {
+ day &= ~TODAY_FLAG;
+ p += sprintf(p, "%s", Senter);
+ highlight = 1;
+ }
+ if (julian) {
+ if ((val = day / 100)) {
+ day %= 100;
+ *p++ = val + '0';
+ display = 1;
+ } else {
+ *p++ = ' ';
+ display = 0;
+ }
+ val = day / 10;
+ if (val || display)
+ *p++ = val + '0';
+ else
+ *p++ = ' ';
+ *p++ = day % 10 + '0';
+ } else {
+ *p++ = aday[day][0];
+ *p++ = aday[day][1];
+ }
+ if (highlight)
+ p += sprintf(p, "%s", Sexit);
+ *p++ = ' ';
+ return p;
+}
+
+void
+trim_trailing_spaces(char *s)
+{
+ char *p;
+
+ for (p = s; *p; ++p)
+ continue;
+ while (p > s && isspace(*--p))
+ continue;
+ if (p > s)
+ ++p;
+ *p = '\0';
+}
+
+/*
+ * Center string, handling multibyte characters appropriately.
+ * In addition if the string is too large for the width it's truncated.
+ * The number of trailing spaces may be 1 less than the number of leading spaces.
+ */
+int
+center_str(const char* src, char* dest, size_t dest_size, size_t width)
+{
+ return mbsalign(src, dest, dest_size, &width,
+ MBS_ALIGN_CENTER, MBA_UNIBYTE_FALLBACK);
+}
+
+void
+center(const char *str, size_t len, int separate)
+{
+ char lineout[FMT_ST_CHARS];
+ center_str(str, lineout, ARRAY_SIZE(lineout), len);
+ fputs(lineout, stdout);
+ if (separate)
+ printf("%*s", separate, "");
+}
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fputs(_("\nUsage:\n"), out);
+ fprintf(out,
+ _(" %s [options] [[[day] month] year]\n"),
+ program_invocation_short_name);
+
+ fputs(_("\nOptions:\n"), out);
+ fputs(_(" -1, --one show only current month (default)\n"
+ " -3, --three show previous, current and next month\n"
+ " -s, --sunday Sunday as first day of week\n"
+ " -m, --monday Monday as first day of week\n"
+ " -j, --julian output Julian dates\n"
+ " -y, --year show whole current year\n"
+ " -V, --version display version information and exit\n"
+ " -h, --help display this help text and exit\n\n"), out);
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
diff --git a/misc-utils/chkdupexe b/misc-utils/chkdupexe
new file mode 100644
index 0000000..b82b102
--- /dev/null
+++ b/misc-utils/chkdupexe
@@ -0,0 +1,120 @@
+#! -w
+#
+# chkdupexe version 2.1.1
+#
+# Simple script to look for and list duplicate executables and dangling
+# symlinks in the system executable directories.
+#
+# Copyright 1993 Nicolai Langfeldt. janl@math.uio.no
+# Distribute under gnu copyleft (included in perl package)
+#
+# Modified 1995-07-04 Michael Shields <shields@tembel.org>
+# Don't depend on GNU ls.
+# Cleanups.
+# Merge together $ENV{'PATH'} and $execdirs.
+# Don't break if there are duplicates in $PATH.
+#
+# Modified 1996-02-16 Nicolai Langfeldt (janl@math.uio.no).
+# I was thinking admins would edit the $execdirs list to suit their
+# machine(s) when I wrote this. This is ofcourse not the case, thus
+# Michaels fixes. And my fixes to his :-)
+# - Working duplicate dirs detection.
+# - Added more checks
+# - Took out $PATH from the list of checked directories and added a
+# check for $execdirs and $PATH consistency instead
+# - Made it possible to run with perl -w
+
+$execdirs='/bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin '.
+ '/usr/X11/bin /usr/bin/X11 /usr/local/X11/bin '.
+ '/usr/TeX/bin /usr/tex/bin /usr/games '.
+ '/usr/local/games';
+
+# Turn off buffering for the output channel.
+$|=1;
+
+# Values from /usr/include/linux/errno.h. Existence of linux/errno.ph is not
+# something to count on... :-(
+$ENOENT=2;
+
+%didthis=();
+
+foreach $dir (split(/\s+/, "$execdirs"), "\0", split(/:/, $ENV{PATH})) {
+
+ if ($dir eq "\0") { $checkingpath = 1; next; }
+
+ # It's like this: One directory corresponds to one $device,$inode tuple
+ # If a symlink points to a directory we already checked that directory
+ # will have the same $device,$inode tuple.
+
+ # Does this directory have any real exstence outside the ravings of
+ # symlinks pointing hither and dither?
+ ($device,$inode)=stat($dir);
+ if (!defined($device)) {
+ # Nonexistant directory, or dangling symlink?
+ ($dum)=lstat($dir);
+ next if $! == $ENOENT;
+ if (!$dum) {
+ print "Dangling symlink: $dir\n";
+ next;
+ }
+ warn "Nonexistent directory: $dir\n" if ($checkingpath);
+ next;
+ }
+
+ if (!-d _) {
+ print "Not a directory: $dir\n";
+ next;
+ }
+
+ next if defined($didthis{$device,$inode});
+
+ $didthis{$device,$inode}=1;
+
+ chdir($dir) || die "Could not chdir $dir: $!\n";
+# This would give us the true directory name, do we want that?
+# chop($dir=`pwd`);
+ opendir(DIR,".") ||
+ die "NUTS! Personaly I think your perl or filesystem is broken.\n".
+ "I've done all sorts of checks on $dir, and now I can't open it!\n";
+ foreach $_ (readdir(DIR)) {
+ lstat($_);
+ if (-l _) {
+ ($dum)=stat($_);
+ print "Dangling symlink: $dir/$_\n" unless defined($dum);
+ next;
+ }
+ next unless -f _ && -x _; # Only handle regular executable files
+ if (defined($count{$_})) {
+ $progs{$_}.=" $dir/$_";
+ $count{$_}++;
+ } else {
+ $progs{$_}="$dir/$_";
+ $count{$_}=1;
+ }
+ }
+ closedir(DIR);
+}
+
+open(LS,"| xargs -r ls -ldU");
+while (($prog,$paths)=each %progs) {
+ print LS "$paths\n" if ($count{$prog}>1);
+}
+close(LS);
+
+exit 0;
+
+@unchecked=();
+# Check if the users PATH contains something I've not checked. The site admin
+# might want to know about inconsistencies in user PATHs and chkdupexec
+# configuration
+foreach $dir (split(/:/,$ENV{'PATH'})) {
+ ($device,$inode)=stat($dir);
+ next unless defined($device);
+ next if defined($didthis{$device,$inode});
+ push(@unchecked,$dir);
+ $didthis{$device,$inode}=1;
+}
+
+print "Warning: Your path contains these directories which chkdupexe has not checked:\n",join(',',@unchecked),
+ ".\nPlease review the execdirs list in chkdupexe.\n"
+ if ($#unchecked>=$[);
diff --git a/misc-utils/chkdupexe.1 b/misc-utils/chkdupexe.1
new file mode 100644
index 0000000..dd3664a
--- /dev/null
+++ b/misc-utils/chkdupexe.1
@@ -0,0 +1,38 @@
+.\" chkdupexe.1 --
+.\" Created: Sat Mar 11 18:19:44 1995 by faith@cs.unc.edu
+.\" Revised: Sat Mar 11 19:07:05 1995 by faith@cs.unc.edu
+.\" Revised: Wed Jul 5 01:56:26 1995 by shields@tembel.org
+.\" Copyright 1995 Rickard E. Faith (faith@cs.unc.edu)
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of this
+.\" manual under the conditions for verbatim copying, provided that the
+.\" entire resulting derived work is distributed under the terms of a
+.\" permission notice identical to this one
+.\"
+.\" Since the Linux kernel and libraries are constantly changing, this
+.\" manual page may be incorrect or out-of-date. The author(s) assume no
+.\" responsibility for errors or omissions, or for damages resulting from
+.\" the use of the information contained herein. The author(s) may not
+.\" have taken the same level of care in the production of this manual,
+.\" which is licensed free of charge, as they might when working
+.\" professionally.
+.\"
+.\" Formatted or processed versions of this manual, if unaccompanied by
+.\" the source, must acknowledge the copyright and authors of this work.
+.\"
+.TH CHKDUPEXE 1 "March 1995" "util-linux" "User Commands"
+.SH NAME
+chkdupexe \- find duplicate executables
+.SH SYNOPSIS
+.B chkdupexe
+.SH DESCRIPTION
+.B chkdupexe
+will scan the union of $PATH and a hardcoded list of common locations
+for binaries. It will report dangling symlinks and duplicately-named
+binaries.
+.SH AUTHOR
+Nicolai Langfeldt, Michael Shields.
diff --git a/misc-utils/chkdupexe.pl b/misc-utils/chkdupexe.pl
new file mode 100755
index 0000000..c2c2384
--- /dev/null
+++ b/misc-utils/chkdupexe.pl
@@ -0,0 +1,120 @@
+#!@PERL@ -w
+#
+# chkdupexe version 2.1.1
+#
+# Simple script to look for and list duplicate executables and dangling
+# symlinks in the system executable directories.
+#
+# Copyright 1993 Nicolai Langfeldt. janl@math.uio.no
+# Distribute under gnu copyleft (included in perl package)
+#
+# Modified 1995-07-04 Michael Shields <shields@tembel.org>
+# Don't depend on GNU ls.
+# Cleanups.
+# Merge together $ENV{'PATH'} and $execdirs.
+# Don't break if there are duplicates in $PATH.
+#
+# Modified 1996-02-16 Nicolai Langfeldt (janl@math.uio.no).
+# I was thinking admins would edit the $execdirs list to suit their
+# machine(s) when I wrote this. This is ofcourse not the case, thus
+# Michaels fixes. And my fixes to his :-)
+# - Working duplicate dirs detection.
+# - Added more checks
+# - Took out $PATH from the list of checked directories and added a
+# check for $execdirs and $PATH consistency instead
+# - Made it possible to run with perl -w
+
+$execdirs='/bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin '.
+ '/usr/X11/bin /usr/bin/X11 /usr/local/X11/bin '.
+ '/usr/TeX/bin /usr/tex/bin /usr/games '.
+ '/usr/local/games';
+
+# Turn off buffering for the output channel.
+$|=1;
+
+# Values from /usr/include/linux/errno.h. Existence of linux/errno.ph is not
+# something to count on... :-(
+$ENOENT=2;
+
+%didthis=();
+
+foreach $dir (split(/\s+/, "$execdirs"), "\0", split(/:/, $ENV{PATH})) {
+
+ if ($dir eq "\0") { $checkingpath = 1; next; }
+
+ # It's like this: One directory corresponds to one $device,$inode tuple
+ # If a symlink points to a directory we already checked that directory
+ # will have the same $device,$inode tuple.
+
+ # Does this directory have any real exstence outside the ravings of
+ # symlinks pointing hither and dither?
+ ($device,$inode)=stat($dir);
+ if (!defined($device)) {
+ # Nonexistant directory, or dangling symlink?
+ ($dum)=lstat($dir);
+ next if $! == $ENOENT;
+ if (!$dum) {
+ print "Dangling symlink: $dir\n";
+ next;
+ }
+ warn "Nonexistent directory: $dir\n" if ($checkingpath);
+ next;
+ }
+
+ if (!-d _) {
+ print "Not a directory: $dir\n";
+ next;
+ }
+
+ next if defined($didthis{$device,$inode});
+
+ $didthis{$device,$inode}=1;
+
+ chdir($dir) || die "Could not chdir $dir: $!\n";
+# This would give us the true directory name, do we want that?
+# chop($dir=`pwd`);
+ opendir(DIR,".") ||
+ die "NUTS! Personaly I think your perl or filesystem is broken.\n".
+ "I've done all sorts of checks on $dir, and now I can't open it!\n";
+ foreach $_ (readdir(DIR)) {
+ lstat($_);
+ if (-l _) {
+ ($dum)=stat($_);
+ print "Dangling symlink: $dir/$_\n" unless defined($dum);
+ next;
+ }
+ next unless -f _ && -x _; # Only handle regular executable files
+ if (defined($count{$_})) {
+ $progs{$_}.=" $dir/$_";
+ $count{$_}++;
+ } else {
+ $progs{$_}="$dir/$_";
+ $count{$_}=1;
+ }
+ }
+ closedir(DIR);
+}
+
+open(LS,"| xargs -r ls -ldU");
+while (($prog,$paths)=each %progs) {
+ print LS "$paths\n" if ($count{$prog}>1);
+}
+close(LS);
+
+exit 0;
+
+@unchecked=();
+# Check if the users PATH contains something I've not checked. The site admin
+# might want to know about inconsistencies in user PATHs and chkdupexec
+# configuration
+foreach $dir (split(/:/,$ENV{'PATH'})) {
+ ($device,$inode)=stat($dir);
+ next unless defined($device);
+ next if defined($didthis{$device,$inode});
+ push(@unchecked,$dir);
+ $didthis{$device,$inode}=1;
+}
+
+print "Warning: Your path contains these directories which chkdupexe has not checked:\n",join(',',@unchecked),
+ ".\nPlease review the execdirs list in chkdupexe.\n"
+ if ($#unchecked>=$[);
diff --git a/misc-utils/ddate.1 b/misc-utils/ddate.1
new file mode 100644
index 0000000..0b01e09
--- /dev/null
+++ b/misc-utils/ddate.1
@@ -0,0 +1,114 @@
+.\" All Rites Reversed. This file is in the PUBLIC DOMAIN.
+.\" Kallisti.
+.TH DDATE 1 "Bureaucracy 3161" "util-linux" "Emperor Norton User Command"
+.SH NAME
+ddate \- convert Gregorian dates to Discordian dates
+.SH SYNOPSIS
+.B ddate
+.RI [ \fB+\fPformat]
+.RI [ date ]
+.SH DESCRIPTION
+.B ddate
+prints the date in Discordian date format.
+.PP
+If called with no arguments,
+.B ddate
+will get the current system date, convert this to the Discordian
+date format and print this on the standard output. Alternatively, a
+Gregorian date may be specified on the command line, in the form of a numerical
+day, month and year.
+.PP
+If a format string is specified, the Discordian date will be printed in
+a format specified by the string. This mechanism works similarly to the
+format string mechanism of
+.B date(1),
+only almost completely differently. The fields are:
+.IP %A
+Full name of the day of the week (i.e., Sweetmorn)
+.IP %a
+Abbreviated name of the day of the week (i.e., SM)
+.IP %B
+Full name of the season (i.e., Chaos)
+.IP %b
+Abbreviated name of the season (i.e., Chs)
+.IP %d
+Ordinal number of day in season (i.e., 23)
+.IP %e
+Cardinal number of day in season (i.e., 23rd)
+.IP %H
+Name of current Holyday, if any
+.IP %N
+Magic code to prevent rest of format from being printed unless today is
+a Holyday.
+.IP %n
+Newline
+.IP %t
+Tab
+.IP %X
+Number of days remaining until X-Day. (Not valid if the SubGenius options
+are not compiled in.)
+.IP %{
+.IP %}
+Used to enclose the part of the string which is to be replaced with the
+words "St. Tib's Day" if the current day is St. Tib's Day.
+.IP %\.
+Try it and see.
+.bp
+.SH EXAMPLES
+.nf
+% ddate
+.br
+Sweetmorn, Bureaucracy 42, 3161 YOLD
+.PP
+% ddate +'Today is %{%A, the %e of %B%}, %Y. %N%nCelebrate %H'
+.br
+Today is Sweetmorn, the 42nd of Bureaucracy, 3161.
+.PP
+% ddate +"It's %{%A, the %e of %B%}, %Y. %N%nCelebrate %H" 26 9 1995
+.br
+It's Prickle-Prickle, the 50th of Bureaucracy, 3161.
+.br
+Celebrate Bureflux
+.PP
+% ddate +"Today's %{%A, the %e of %B%}, %Y. %N%nCelebrate %H" 29 2 1996
+.br
+Today's St. Tib's Day, 3162.
+.br
+
+.SH BUGS
+
+.B ddate(1)
+will produce undefined behavior if asked to produce the date for St. Tib's
+day and its format string does not contain the St. Tib's Day delimiters
+%{ and %}.
+
+.SH NOTE
+
+After `X-Day' passed without incident, the Church of the SubGenius
+declared that it had got the year upside down - X-Day is actually in 8661 AD
+rather than 1998 AD. Thus, the True X-Day is Cfn 40, 9827.
+
+.SH AUTHOR
+.nh
+Original program by Druel the Chaotic aka Jeremy Johnson (mpython@gnu.ai.mit.edu)
+.br
+Major rewrite by Lee H:. O:. Smith, KYTP, aka Andrew Bulhak (acb@dev.null.org)
+.br
+Five tons of flax.
+
+.SH DISTRIBUTION POLICY
+
+Public domain. All rites reversed.
+
+.SH SEE ALSO
+
+date(1),
+.br
+http://www.subgenius.com/
+.br
+Malaclypse the Younger,
+.I "Principia Discordia, Or How I Found Goddess And What I Did To Her When I Found Her"
+
+.SH AVAILABILITY
+The ddate command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/misc-utils/ddate.c b/misc-utils/ddate.c
new file mode 100644
index 0000000..c0b4ce4
--- /dev/null
+++ b/misc-utils/ddate.c
@@ -0,0 +1,374 @@
+/* $ DVCS ID: $jer|,523/lhos,KYTP!41023161\b"?" <<= DO NOT DELETE! */
+
+/* ddate.c .. converts boring normal dates to fun Discordian Date -><-
+ written the 65th day of The Aftermath in the Year of Our Lady of
+ Discord 3157 by Druel the Chaotic aka Jeremy Johnson aka
+ mpython@gnu.ai.mit.edu
+ 28 Sever St Apt #3
+ Worcester MA 01609
+
+ and I'm not responsible if this program messes anything up (except your
+ mind, I'm responsible for that)
+
+ (k) YOLD 3161 and all time before and after.
+ Reprint, reuse, and recycle what you wish.
+ This program is in the public domain. Distribute freely. Or not.
+
+ Majorly hacked, extended and bogotified/debogotified on
+ Sweetmorn, Bureaucracy 42, 3161 YOLD, by Lee H:. O:. Smith, KYTP,
+ aka Andrew Bulhak, aka acb@dev.null.org
+
+ and I'm not responsible if this program messes anything up (except your
+ mind, I'm responsible for that) (and that goes for me as well --lhos)
+
+ Version history:
+ Bureflux 3161: First release of enhanced ddate with format strings
+ 59 Bcy, 3161: PRAISE_BOB and KILL_BOB options split, other minor
+ changes.
+
+ 1999-02-22 Arkadiusz Miskiewicz <misiek@pld.ORG.PL>
+ - added Native Language Support
+
+ 2000-03-17 Burt Holzman <bnh@iname.com>
+ - added range checks for dates
+*/
+
+/* configuration options VVVVV READ THIS!!! */
+
+/* If you wish ddate(1) to print the date in the same format as Druel's
+ * original ddate when called in immediate mode, define OLD_IMMEDIATE_FMT
+ */
+
+#define OLD_IMMEDIATE_FMT
+
+/* If you wish to use the US format for aneristic dates (m-d-y), as opposed to
+ * the Commonwealth format, define US_FORMAT.
+ */
+
+/* #define US_FORMAT */
+
+/* If you are ideologically, theologically or otherwise opposed to the
+ * Church of the SubGenius and do not wish your copy of ddate(1) to contain
+ * code for counting down to X-Day, undefine KILL_BOB */
+
+#define KILL_BOB 13013
+
+/* If you wish ddate(1) to contain SubGenius slogans, define PRAISE_BOB */
+
+/*#define PRAISE_BOB 13013*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <stdio.h>
+
+#include "nls.h"
+#include "closestream.h"
+#include "c.h"
+
+#ifndef __GNUC__
+#define inline /* foo */
+#endif
+
+#ifdef KILL_BOB
+int xday_countdown(int yday, int year);
+#endif
+
+
+/* string constants */
+
+char *day_long[5] = {
+ "Sweetmorn", "Boomtime", "Pungenday", "Prickle-Prickle", "Setting Orange"
+};
+
+char *day_short[5] = {"SM","BT","PD","PP","SO"};
+
+char *season_long[5] = {
+ "Chaos", "Discord", "Confusion", "Bureaucracy", "The Aftermath"
+};
+
+char *season_short[5] = {"Chs", "Dsc", "Cfn", "Bcy", "Afm"};
+
+char *holyday[5][2] = {
+ { "Mungday", "Chaoflux" },
+ { "Mojoday", "Discoflux" },
+ { "Syaday", "Confuflux" },
+ { "Zaraday", "Bureflux" },
+ { "Maladay", "Afflux" }
+};
+
+struct disc_time {
+ int season; /* 0-4 */
+ int day; /* 0-72 */
+ int yday; /* 0-365 */
+ int year; /* 3066- */
+};
+
+char *excl[] = {
+ "Hail Eris!", "All Hail Discordia!", "Kallisti!", "Fnord.", "Or not.",
+ "Wibble.", "Pzat!", "P'tang!", "Frink!",
+#ifdef PRAISE_BOB
+ "Slack!", "Praise \"Bob\"!", "Or kill me.",
+#endif /* PRAISE_BOB */
+ /* randomness, from the Net and other places. Feel free to add (after
+ checking with the relevant authorities, of course). */
+ "Grudnuk demand sustenance!", "Keep the Lasagna flying!",
+ "You are what you see.",
+ "Or is it?", "This statement is false.",
+#if defined(linux) || defined (__linux__) || defined (__linux)
+ "Hail Eris, Hack Linux!",
+#endif
+ ""
+};
+
+char default_fmt[] = "%{%A, %B %d%}, %Y YOLD";
+char *default_immediate_fmt=
+#ifdef OLD_IMMEDIATE_FMT
+"Today is %{%A, the %e day of %B%} in the YOLD %Y%N%nCelebrate %H"
+#else
+default_fmt
+#endif
+;
+
+#define DY(y) (y+1166)
+
+static inline char *ending(int i) {
+ return i/10==1?"th":(i%10==1?"st":(i%10==2?"nd":(i%10==3?"rd":"th")));
+}
+
+static inline int leapp(int i) {
+ return (!(DY(i)%4))&&((DY(i)%100)||(!(DY(i)%400)));
+}
+
+/* select a random string */
+static inline char *sel(char **strings, int num) {
+ return(strings[random()%num]);
+}
+
+void print(struct disc_time,char **); /* old */
+void format(char *buf, const char* fmt, struct disc_time dt);
+/* read a fortune file */
+int load_fortunes(char *fn, char *delim, char** result);
+
+struct disc_time convert(int,int);
+struct disc_time makeday(int,int,int);
+
+int
+main (int argc, char *argv[]) {
+ long t;
+ struct tm *eris;
+ int bob,raw;
+ struct disc_time hastur;
+ char schwa[23*17], *fnord=0;
+ int pi;
+ char *progname, *p;
+
+ progname = argv[0];
+ if ((p = strrchr(progname, '/')) != NULL)
+ progname = p+1;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ srandom(time(NULL));
+ /* do args here */
+ for(pi=1; pi<argc; pi++) {
+ switch(argv[pi][0]) {
+ case '+': fnord=argv[pi]+1; break;
+ case '-':
+ switch(argv[pi][1]) {
+ case 'V':
+ printf(_("%s (%s)\n"), progname, PACKAGE_STRING);
+ default: goto usage;
+ }
+ default: goto thud;
+ }
+ }
+
+ thud:
+ if (argc-pi==3){
+ int moe=atoi(argv[pi]), larry=atoi(argv[pi+1]), curly=atoi(argv[pi+2]);
+ hastur=makeday(
+#ifdef US_FORMAT
+ moe,larry,
+#else
+ larry,moe,
+#endif
+ curly);
+ if (hastur.season == -1) {
+ printf("Invalid date -- out of range\n");
+ return -1;
+ }
+ fnord=fnord?fnord:default_fmt;
+ } else if (argc!=pi) {
+ usage:
+ fprintf(stderr,_("usage: %s [+format] [day month year]\n"), argv[0]);
+ exit(1);
+ } else {
+ t= time(NULL);
+ eris=localtime(&t);
+ bob=eris->tm_yday; /* days since Jan 1. */
+ raw=eris->tm_year; /* years since 1980 */
+ hastur=convert(bob,raw);
+ fnord=fnord?fnord:default_immediate_fmt;
+ }
+ format(schwa, fnord, hastur);
+ printf("%s\n", schwa);
+
+ return 0;
+}
+
+void format(char *buf, const char* fmt, struct disc_time dt)
+{
+ int tib_start=-1, tib_end=0;
+ int i, fmtlen=strlen(fmt);
+ char *bufptr=buf;
+
+/* fprintf(stderr, "format(%p, \"%s\", dt)\n", buf, fmt);*/
+
+ /* first, find extents of St. Tib's Day area, if defined */
+ for(i=0; i<fmtlen; i++) {
+ if(fmt[i]=='%') {
+ switch(fmt[i+1]) {
+ case 'A':
+ case 'a':
+ case 'd':
+ case 'e':
+ if(tib_start>0) tib_end=i+1;
+ else tib_start=i;
+ break;
+ case '{': tib_start=i; break;
+ case '}': tib_end=i+1; break;
+ }
+ }
+ }
+
+ /* now do the formatting */
+ buf[0]=0;
+
+ for(i=0; i<fmtlen; i++) {
+ if((i==tib_start) && (dt.day==-1)) {
+ /* handle St. Tib's Day */
+ strcpy(bufptr, _("St. Tib's Day"));
+ bufptr += strlen(bufptr);
+ i=tib_end;
+ } else {
+ if(fmt[i]=='%') {
+ char *wibble=0, snarf[23];
+ switch(fmt[++i]) {
+ case 'A': wibble=day_long[dt.yday%5]; break;
+ case 'a': wibble=day_short[dt.yday%5]; break;
+ case 'B': wibble=season_long[dt.season]; break;
+ case 'b': wibble=season_short[dt.season]; break;
+ case 'd': sprintf(snarf, "%d", dt.day+1); wibble=snarf; break;
+ case 'e': sprintf(snarf, "%d%s", dt.day+1, ending(dt.day+1));
+ wibble=snarf; break;
+ case 'H': if(dt.day==4||dt.day==49)
+ wibble=holyday[dt.season][dt.day==49]; break;
+ case 'N': if(dt.day!=4&&dt.day!=49) goto eschaton; break;
+ case 'n': *(bufptr++)='\n'; break;
+ case 't': *(bufptr++)='\t'; break;
+
+ case 'Y': sprintf(snarf, "%d", dt.year); wibble=snarf; break;
+ case '.': wibble=sel(excl, ARRAY_SIZE(excl));
+ break;
+#ifdef KILL_BOB
+ case 'X': sprintf(snarf, "%d",
+ xday_countdown(dt.yday, dt.year));
+ wibble = snarf; break;
+#endif /* KILL_BOB */
+ }
+ if(wibble) {
+/* fprintf(stderr, "wibble = (%s)\n", wibble);*/
+ strcpy(bufptr, wibble); bufptr+=strlen(wibble);
+ }
+ } else {
+ *(bufptr++) = fmt[i];
+ }
+ }
+ }
+ eschaton:
+ *(bufptr)=0;
+}
+
+struct disc_time makeday(int imonth,int iday,int iyear) /*i for input */
+{
+ struct disc_time funkychickens;
+
+ int cal[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
+ int dayspast=0;
+
+ memset(&funkychickens,0,sizeof(funkychickens));
+ /* basic range checks */
+ if (imonth < 1 || imonth > 12) {
+ funkychickens.season = -1;
+ return funkychickens;
+ }
+ if (iday < 1 || iday > cal[imonth-1]) {
+ if (!(imonth == 2 && iday == 29 && iyear%4 == 0 &&
+ (iyear%100 != 0 || iyear%400 == 0))) {
+ funkychickens.season = -1;
+ return funkychickens;
+ }
+ }
+
+ imonth--;
+ funkychickens.year= iyear+1166;
+ while(imonth>0) { dayspast+=cal[--imonth]; }
+ funkychickens.day=dayspast+iday-1;
+ funkychickens.season=0;
+ if((funkychickens.year%4)==2) {
+ if (funkychickens.day==59 && iday==29) funkychickens.day=-1;
+ }
+ funkychickens.yday=funkychickens.day;
+/* note: EQUAL SIGN...hopefully that fixes it */
+ while(funkychickens.day>=73) {
+ funkychickens.season++;
+ funkychickens.day-=73;
+ }
+ return funkychickens;
+}
+
+struct disc_time convert(int nday, int nyear)
+{ struct disc_time funkychickens;
+
+ funkychickens.year = nyear+3066;
+ funkychickens.day=nday;
+ funkychickens.season=0;
+ if ((funkychickens.year%4)==2)
+ {if (funkychickens.day==59)
+ funkychickens.day=-1;
+ else if (funkychickens.day >59)
+ funkychickens.day-=1;
+ }
+ funkychickens.yday=funkychickens.day;
+ while (funkychickens.day>=73)
+ { funkychickens.season++;
+ funkychickens.day-=73;
+ }
+ return funkychickens;
+
+ }
+
+#ifdef KILL_BOB
+
+/* Code for counting down to X-Day, X-Day being Cfn 40, 3164
+ *
+ * After `X-Day' passed without incident, the CoSG declared that it had
+ * got the year upside down --- X-Day is actually in 8661 AD rather than
+ * 1998 AD.
+ *
+ * Thus, the True X-Day is Cfn 40, 9827.
+ *
+ */
+
+int xday_countdown(int yday, int year) {
+ int r=(185-yday)+(((yday<59)&&(leapp(year)))?1:0);
+ while(year<9827) r+=(leapp(++year)?366:365);
+ while(year>9827) r-=(leapp(year--)?366:365);
+ return r;
+}
+
+#endif
diff --git a/misc-utils/findfs.8 b/misc-utils/findfs.8
new file mode 100644
index 0000000..b754903
--- /dev/null
+++ b/misc-utils/findfs.8
@@ -0,0 +1,34 @@
+.\" -*- nroff -*-
+.\" Copyright 1993, 1994, 1995 by Theodore Ts'o. All Rights Reserved.
+.\" This file may be copied under the terms of the GNU Public License.
+.\"
+.TH FINDFS 8 "February 2009" "util-linux" "System Administration"
+.SH NAME
+findfs \- find a filesystem by label or UUID
+.SH SYNOPSIS
+.B findfs
+.BI LABEL= label
+.sp
+.B findfs
+.BI UUID= uuid
+.SH DESCRIPTION
+.B findfs
+will search the disks in the system looking for a filesystem which has
+a label matching
+.I label
+or a UUID equal to
+.IR uuid .
+If the filesystem is found, the device name for the filesystem will
+be printed on stdout.
+.PP
+.SH AUTHOR
+.B findfs
+was originally written by Theodore Ts'o (tytso@mit.edu) and re-written for
+the util-linux package by Karel Zak (kzak@redhat.com).
+.SH AVAILABILITY
+The findfs command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.SH SEE ALSO
+.BR blkid (8)
+.BR fsck (8)
+
diff --git a/misc-utils/findfs.c b/misc-utils/findfs.c
new file mode 100644
index 0000000..bc4a843
--- /dev/null
+++ b/misc-utils/findfs.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <blkid.h>
+
+#include "nls.h"
+#include "closestream.h"
+#include "c.h"
+
+static void __attribute__((__noreturn__)) usage(int rc)
+{
+ FILE *out = rc ? stderr : stdout;
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(" %1$s [options] LABEL=<label>\n"
+ " %1$s [options] UUID=<uuid>\n"),
+ program_invocation_short_name);
+ fputs(USAGE_OPTIONS, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ fprintf(out, USAGE_MAN_TAIL("findfs(8)"));
+ exit(rc);
+}
+
+int main(int argc, char **argv)
+{
+ char *dev, *tk, *vl;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ if (argc != 2)
+ /* we return '2' for backward compatibility
+ * with version from e2fsprogs */
+ usage(2);
+
+ if (!strncmp(argv[1], "LABEL=", 6)) {
+ tk = "LABEL";
+ vl = argv[1] + 6;
+ } else if (!strncmp(argv[1], "UUID=", 5)) {
+ tk = "UUID";
+ vl = argv[1] + 5;
+ } else if (strcmp(argv[1], "-V") == 0 ||
+ strcmp(argv[1], "--version") == 0) {
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ } else if (strcmp(argv[1], "-h") == 0 ||
+ strcmp(argv[1], "--help") == 0) {
+ usage(EXIT_SUCCESS);
+ } else
+ usage(2);
+
+ dev = blkid_evaluate_tag(tk, vl, NULL);
+ if (!dev)
+ errx(EXIT_FAILURE, _("unable to resolve '%s'"), argv[1]);
+
+ puts(dev);
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/misc-utils/findmnt.8 b/misc-utils/findmnt.8
new file mode 100644
index 0000000..2072c52
--- /dev/null
+++ b/misc-utils/findmnt.8
@@ -0,0 +1,217 @@
+.\" -*- nroff -*-
+.TH FINDMNT 8 "April 2010" "util-linux" "System Administration"
+.SH NAME
+findmnt \- find a filesystem
+.SH SYNOPSIS
+.B findmnt
+.RB [ options ]
+.sp
+.B findmnt
+.RB [ options ]
+.IR device | mountpoint
+.sp
+.B findmnt
+.RB [ options ]
+.RB [ \--source ]
+.IR device
+.RB [ \--target ]
+.IR mountpoint
+.SH DESCRIPTION
+.B findmnt
+will list all mounted filesytems or search for a filesystem. The
+.B findmnt
+command is able to search in
+.IR /etc/fstab ,
+.IR /etc/mtab
+or
+.IR /proc/self/mountinfo .
+If
+.IR device
+or
+.IR mountpoint
+is not given, all filesystems are shown.
+.PP
+The device may be specified by device name, maj:min, filesystem LABEL or UUID
+or partition PARTUUID or PARTLABEL. Note that device name may be interpreted
+as mountpoint (and vice versa) if --target or --source options are not
+specified.
+.PP
+The command prints all mounted filesystems in the tree-like format by default.
+.SH OPTIONS
+.IP "\fB\-h, \-\-help\fP"
+Print help and exit.
+.IP "\fB\-s, \-\-fstab\fP"
+Search in
+.IR /etc/fstab .
+The output is in the list format (see --list).
+.IP "\fB\-m, \-\-mtab\fP"
+Search in
+.IR /etc/mtab .
+The output is in the list format (see \fB\-\-list\fP).
+.IP "\fB\-k, \-\-kernel\fP"
+Search in
+.IR /proc/self/mountinfo .
+The output is in the tree-like format. This is the default.
+
+.IP "\fB\-A, \-\-all\fP"
+Disable all built-in filters and print all filesystems.
+.IP "\fB\-a, \-\-ascii\fP"
+Use ascii characters for tree formatting.
+.IP "\fB\-c, \-\-canonicalize\fP"
+Canonicalize all printed paths.
+.IP "\fB\-D, \-\-df\fP"
+Imitate the output of df(1). This option is equivalent to
+"-o SOURCE,FSTYPE,SIZE,USED,AVAIL,USE%,TARGET", but excludes all
+pseudo filesystem. Use \fB\-\-all\fP to print all filesystems.
+.IP "\fB\-d, \-\-direction \fIword\fP"
+The search direction -
+.IR forward
+or
+.IR backward .
+.IP "\fB\-e, \-\-evaluate\fP"
+Convert all tags (LABEL, UUID, PARTUUID or PARTLABEL) to the device names.
+.IP "\fB\-F, \-\-tab\-file \fIpath\fP"
+Search in an alternative file, if used with \fB\-\-fstab\fP, \fB\-\-mtab\fP
+or \fB\-\-kernel\fP then overwrites the default paths, if specified more than
+once, then tree-like output is disabled (see the \fB\-\-list\fP option).
+.IP "\fB\-f, \-\-first\-only\fP"
+Print the first matching filesystem only.
+.IP "\fB\-i, \-\-invert\fP"
+Invert the sense of matching.
+.IP "\fB\-l, \-\-list\fP"
+Use the list output format. This output format is automatically enabled if the
+output is restricted by \fB\-t\fP, \fB\-O\fP, \fB\-S\fP or \fB\-T\fP
+option and the option \fB\-\-submounts\fP is not used or if more that one
+source file (the option \fB\-F\fP) is specified.
+.IP "\fB\-v, \-\-nofsroot\fP"
+Do not print a [/dir] in the SOURCE column for bind-mounts or btrfs subvolumes.
+.IP "\fB\-N, \-\-task \fItid\fP"
+Use alternative namespace /proc/<tid>/mountinfo rather than the default
+/proc/self/mountinfo. If the option is specified more than once, then
+tree-like output is disabled (see the \fB\-\-list\fP option). See also
+.BR unshare (1)
+command.
+.IP "\fB\-n, \-\-noheadings\fP"
+Do not print a header line.
+.IP "\fB\-u, \-\-notruncate\fP"
+Do not truncate text in columns. The default is to not truncate the
+.BR TARGET ,
+.BR SOURCE ,
+.BR UUID ,
+.BR LABEL ,
+.BR PARTUUID ,
+.BR PARTLABEL
+columns. This option disables text truncation also in all other columns.
+.IP "\fB\-O, \-\-options \fIlist\fP"
+Limit the set of printed filesystems. More than one option
+may be specified in a comma-separated list. The
+.B \-t
+and
+.B \-O
+options are cumulative in effect. It is different from
+.B \-t
+in that each option is matched exactly; a leading
+.I no
+at the beginning does not have global meaning. The "no" could used for
+individual items in the list. The "no" prefix interpratation could be disabled
+by "+" prefix.
+.IP "\fB\-o, \-\-output \fIlist\fP"
+Define output columns. See the \fB\-\-help\fP output to get list of the
+currently supported columns. The
+.BR TARGET
+column contains tree formatting if the
+.B \-\-list
+or
+.B \-\-raw
+options are not specified.
+.IP "\fB\-p, \-\-poll\fR[\fI=list\fR]\fP"
+Monitor changes in the /proc/self/mountinfo file. Supported actions are: mount,
+umount, remount and move. More than one action may be specified in a
+comma-separated list. All actions are monitored by default.
+
+The time for which \fB--poll\fR will block can be restricted with the \fB\-\-timeout\fP
+or \fB\-\-first-only\fP options.
+
+The standard columns always use the new version of the information from the
+mountinfo file, except the umount action which is based on the original
+information cached by
+.BR findmnt (8) .
+The poll mode allows to use extra columns:
+.RS
+.TP
+.B ACTION
+mount, umount, move or remount action name; this column is enabled by default
+.TP
+.B OLD-TARGET
+available for umount and move actions
+.TP
+.B OLD-OPTIONS
+available for umount and remount actions
+.RE
+.IP "\fB\-P, \-\-pairs\fP"
+Use key="value" output format. All potentially unsafe characters are hex-escaped (\\x<code>).
+.IP "\fB\-r, \-\-raw\fP"
+Use raw output format. All potentially unsafe characters are hex-escaped (\\x<code>).
+.IP "\fB\-t, \-\-types \fIlist\fP"
+Limit the set of printed filesystems. More than one type may be
+specified in a comma-separated list. The list of filesystem types can be
+prefixed with
+.I no
+to specify the filesystem types on which no action should be taken. For
+more details see
+.BR mount (8).
+.IP "\fB\-R, \-\-submounts\fP"
+Print recursively all submounts for the selected filesystems. The restrictions
+defined by options \fB\-t\fP, \fB\-O\fP, \fB\-S\fP, \fB\-T\fP and
+\fB\--direction\fP are not applied to submounts. All submounts are always
+printed in tree-like order. The option enables the tree-like output format by
+default. This option has no effect for \fB\-\-mtab\fP or \fB\-\-fstab\fP.
+.IP "\fB\-S, \-\-source \fIspec\fP"
+Explicitly define the mount source. Supported are \fIdevice\fR, \fImaj:min\fR,
+\fILABEL=\fR, \fIUUID=\fR, \fIPARTLABEL=\fR or \fIPARTUUID=\fR.
+.IP "\fB\-T, \-\-target \fIdir\fP"
+Explicitly define the mount target (mountpoint directory).
+.IP "\fB\-w, \-\-timeout \fImilliseconds\fP"
+Specify an upper limit on the time for which \fB--poll\fR will block, in milliseconds.
+.SH EXAMPLES
+.IP "\fBfindmnt \-\-fstab \-t nfs\fP"
+Prints all nfs filesystems defined in
+.IR /etc/fstab .
+.IP "\fBfindmnt \-\-fstab /mnt/foo\fP"
+Prints all
+.IR /etc/fstab
+filesystems where the mountpoint directory is /mnt/foo. It also prints bind mounts where /mnt/foo
+is a source.
+.IP "\fBfindmnt \-\-fstab --target /mnt/foo\fP"
+Prints all
+.IR /etc/fstab
+filesystems where the mountpoint directory is /mnt/foo.
+.IP "\fBfindmnt --fstab --evaluate\fP"
+Prints all
+.IR /etc/fstab
+filesystems and converts LABEL= and UUID= tags to the real device names.
+.IP "\fBfindmnt -n --raw --evaluate --output=target LABEL=/boot\fP"
+Prints only the mountpoint where the filesystem with label "/boot" is mounted.
+.IP "\fBfindmnt --poll --target /mnt/foo\fP"
+Monitors mount, umount, remount and move on /mnt/foo.
+.IP "\fBfindmnt --poll=umount --first-only --target /mnt/foo\fP"
+Waits for /mnt/foo umount.
+.IP "\fBfindmnt --poll=remount -t ext3 -O ro\fP"
+Monitors remounts to read-only mode on all ext3 filesystems.
+.SH ENVIRONMENT
+.IP LIBMOUNT_FSTAB=<path>
+overrides the default location of the fstab file
+.IP LIBMOUNT_MTAB=<path>
+overrides the default location of the mtab file
+.IP LIBMOUNT_DEBUG=0xffff
+enables debug output
+.SH AUTHORS
+.nf
+Karel Zak <kzak@redhat.com>
+.fi
+.SH SEE ALSO
+.BR mount (8),
+.BR fstab (5)
+.SH AVAILABILITY
+The findmnt command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/misc-utils/findmnt.c b/misc-utils/findmnt.c
new file mode 100644
index 0000000..853fae0
--- /dev/null
+++ b/misc-utils/findmnt.c
@@ -0,0 +1,1368 @@
+/*
+ * findmnt(8)
+ *
+ * Copyright (C) 2010,2011 Red Hat, Inc. All rights reserved.
+ * Written by Karel Zak <kzak@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include <termios.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#include <assert.h>
+#include <poll.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+
+#include <libmount.h>
+
+#include "pathnames.h"
+#include "nls.h"
+#include "closestream.h"
+#include "c.h"
+#include "tt.h"
+#include "strutils.h"
+#include "xalloc.h"
+#include "optutils.h"
+
+/* flags */
+enum {
+ FL_EVALUATE = (1 << 1),
+ FL_CANONICALIZE = (1 << 2),
+ FL_FIRSTONLY = (1 << 3),
+ FL_INVERT = (1 << 4),
+ FL_NOSWAPMATCH = (1 << 6),
+ FL_NOFSROOT = (1 << 7),
+ FL_SUBMOUNTS = (1 << 8),
+ FL_POLL = (1 << 9),
+ FL_DF = (1 << 10),
+ FL_ALL = (1 << 11)
+};
+
+/* column IDs */
+enum {
+ COL_SOURCE,
+ COL_TARGET,
+ COL_FSTYPE,
+ COL_OPTIONS,
+ COL_VFS_OPTIONS,
+ COL_FS_OPTIONS,
+ COL_LABEL,
+ COL_UUID,
+ COL_PARTLABEL,
+ COL_PARTUUID,
+ COL_MAJMIN,
+ COL_ACTION,
+ COL_OLD_TARGET,
+ COL_OLD_OPTIONS,
+ COL_SIZE,
+ COL_AVAIL,
+ COL_USED,
+ COL_USEPERC,
+ COL_FSROOT,
+ COL_TID,
+
+ FINDMNT_NCOLUMNS
+};
+
+enum {
+ TABTYPE_FSTAB = 1,
+ TABTYPE_MTAB,
+ TABTYPE_KERNEL
+};
+
+/* column names */
+struct colinfo {
+ const char *name; /* header */
+ double whint; /* width hint (N < 1 is in percent of termwidth) */
+ int flags; /* tt flags */
+ const char *help; /* column description */
+ const char *match; /* pattern for match_func() */
+ void *match_data; /* match specific data */
+};
+
+/* columns descriptions (don't use const, this is writable) */
+static struct colinfo infos[FINDMNT_NCOLUMNS] = {
+ [COL_SOURCE] = { "SOURCE", 0.25, TT_FL_NOEXTREMES, N_("source device") },
+ [COL_TARGET] = { "TARGET", 0.30, TT_FL_TREE | TT_FL_NOEXTREMES, N_("mountpoint") },
+ [COL_FSTYPE] = { "FSTYPE", 0.10, TT_FL_TRUNC, N_("filesystem type") },
+ [COL_OPTIONS] = { "OPTIONS", 0.10, TT_FL_TRUNC, N_("all mount options") },
+ [COL_VFS_OPTIONS] = { "VFS-OPTIONS", 0.20, TT_FL_TRUNC, N_("VFS specific mount options") },
+ [COL_FS_OPTIONS] = { "FS-OPTIONS", 0.10, TT_FL_TRUNC, N_("FS specific mount options") },
+ [COL_LABEL] = { "LABEL", 0.10, 0, N_("filesystem label") },
+ [COL_UUID] = { "UUID", 36, 0, N_("filesystem UUID") },
+ [COL_PARTLABEL] = { "PARTLABEL", 0.10, 0, N_("partition label") },
+ [COL_PARTUUID] = { "PARTUUID", 36, 0, N_("partition UUID") },
+ [COL_MAJMIN] = { "MAJ:MIN", 6, 0, N_("major:minor device number") },
+ [COL_ACTION] = { "ACTION", 10, TT_FL_STRICTWIDTH, N_("action detected by --poll") },
+ [COL_OLD_OPTIONS] = { "OLD-OPTIONS", 0.10, TT_FL_TRUNC, N_("old mount options saved by --poll") },
+ [COL_OLD_TARGET] = { "OLD-TARGET", 0.30, 0, N_("old mountpoint saved by --poll") },
+ [COL_SIZE] = { "SIZE", 5, TT_FL_RIGHT, N_("filesystem size") },
+ [COL_AVAIL] = { "AVAIL", 5, TT_FL_RIGHT, N_("filesystem size available") },
+ [COL_USED] = { "USED", 5, TT_FL_RIGHT, N_("filesystem size used") },
+ [COL_USEPERC] = { "USE%", 3, TT_FL_RIGHT, N_("filesystem use percentage") },
+ [COL_FSROOT] = { "FSROOT", 0.25, TT_FL_NOEXTREMES, N_("filesystem root") },
+ [COL_TID] = { "TID", 4, TT_FL_RIGHT, N_("task ID") },
+};
+
+/* global flags */
+static int flags;
+static int tt_flags;
+
+/* array with IDs of enabled columns */
+static int columns[FINDMNT_NCOLUMNS];
+static int ncolumns;
+
+/* poll actions (parsed --poll=<list> */
+#define FINDMNT_NACTIONS 4 /* mount, umount, move, remount */
+static int actions[FINDMNT_NACTIONS];
+static int nactions;
+
+/* libmount cache */
+static struct libmnt_cache *cache;
+
+static int get_column_id(int num)
+{
+ assert(num < ncolumns);
+ assert(columns[num] < FINDMNT_NCOLUMNS);
+ return columns[num];
+}
+
+static struct colinfo *get_column_info(int num)
+{
+ return &infos[ get_column_id(num) ];
+}
+
+static const char *column_id_to_name(int id)
+{
+ assert(id < FINDMNT_NCOLUMNS);
+ return infos[id].name;
+}
+
+static const char *get_column_name(int num)
+{
+ return get_column_info(num)->name;
+}
+
+static float get_column_whint(int num)
+{
+ return get_column_info(num)->whint;
+}
+
+static int get_column_flags(int num)
+{
+ return get_column_info(num)->flags;
+}
+
+static const char *get_match(int id)
+{
+ assert(id < FINDMNT_NCOLUMNS);
+ return infos[id].match;
+}
+
+static void *get_match_data(int id)
+{
+ assert(id < FINDMNT_NCOLUMNS);
+ return infos[id].match_data;
+}
+
+static void set_match(int id, const char *match)
+{
+ assert(id < FINDMNT_NCOLUMNS);
+ infos[id].match = match;
+}
+
+static void set_match_data(int id, void *data)
+{
+ assert(id < FINDMNT_NCOLUMNS);
+ infos[id].match_data = data;
+}
+
+/*
+ * source match means COL_SOURCE *or* COL_MAJMIN, depends on
+ * data format.
+ */
+static void set_source_match(const char *data)
+{
+ int maj, min;
+
+ if (sscanf(data, "%d:%d", &maj, &min) == 2) {
+ dev_t *devno = xmalloc(sizeof(dev_t));
+
+ *devno = makedev(maj, min);
+ set_match(COL_MAJMIN, data);
+ set_match_data(COL_MAJMIN, (void *) devno);
+ flags |= FL_NOSWAPMATCH;
+ } else
+ set_match(COL_SOURCE, data);
+}
+
+static void enable_extra_target_match(void)
+{
+ char *cn = NULL, *mnt = NULL;
+
+ /*
+ * Check if match pattern is mountpoint, if not use the
+ * real mountpoint.
+ */
+ cn = mnt_resolve_path(get_match(COL_TARGET), cache);
+ if (!cn)
+ return;
+
+ mnt = mnt_get_mountpoint(cn);
+ if (!mnt || strcmp(mnt, cn) == 0)
+ return;
+
+ /* replace the current setting with the real mountpoint */
+ set_match(COL_TARGET, mnt);
+}
+
+
+static int is_tabdiff_column(int id)
+{
+ assert(id < FINDMNT_NCOLUMNS);
+
+ switch(id) {
+ case COL_ACTION:
+ case COL_OLD_TARGET:
+ case COL_OLD_OPTIONS:
+ return 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/*
+ * "findmnt" without any filter
+ */
+static int is_listall_mode(void)
+{
+ if ((flags & FL_DF) && !(flags & FL_ALL))
+ return 0;
+
+ return (!get_match(COL_SOURCE) &&
+ !get_match(COL_TARGET) &&
+ !get_match(COL_FSTYPE) &&
+ !get_match(COL_OPTIONS) &&
+ !get_match(COL_MAJMIN));
+}
+
+/*
+ * Returns 1 if the @act is in the --poll=<list>
+ */
+static int has_poll_action(int act)
+{
+ int i;
+
+ if (!nactions)
+ return 1; /* all actions enabled */
+ for (i = 0; i < nactions; i++)
+ if (actions[i] == act)
+ return 1;
+ return 0;
+}
+
+static int poll_action_name_to_id(const char *name, size_t namesz)
+{
+ int id = -1;
+
+ if (strncasecmp(name, "move", namesz) == 0 && namesz == 4)
+ id = MNT_TABDIFF_MOVE;
+ else if (strncasecmp(name, "mount", namesz) == 0 && namesz == 5)
+ id = MNT_TABDIFF_MOUNT;
+ else if (strncasecmp(name, "umount", namesz) == 0 && namesz == 6)
+ id = MNT_TABDIFF_UMOUNT;
+ else if (strncasecmp(name, "remount", namesz) == 0 && namesz == 7)
+ id = MNT_TABDIFF_REMOUNT;
+ else
+ warnx(_("unknown action: %s"), name);
+
+ return id;
+}
+
+/*
+ * findmnt --first-only <devname|TAG=|mountpoint>
+ *
+ * ... it works like "mount <devname|TAG=|mountpoint>"
+ */
+static int is_mount_compatible_mode(void)
+{
+ if (!get_match(COL_SOURCE))
+ return 0; /* <devname|TAG=|mountpoint> is required */
+ if (get_match(COL_FSTYPE) || get_match(COL_OPTIONS))
+ return 0; /* cannot be restricted by -t or -O */
+ if (!(flags & FL_FIRSTONLY))
+ return 0; /* we have to return the first entry only */
+
+ return 1; /* ok */
+}
+
+static void disable_columns_truncate(void)
+{
+ int i;
+
+ for (i = 0; i < FINDMNT_NCOLUMNS; i++)
+ infos[i].flags &= ~TT_FL_TRUNC;
+}
+
+/*
+ * converts @name to column ID
+ */
+static int column_name_to_id(const char *name, size_t namesz)
+{
+ int i;
+
+ for (i = 0; i < FINDMNT_NCOLUMNS; i++) {
+ const char *cn = column_id_to_name(i);
+
+ if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
+ return i;
+ }
+ warnx(_("unknown column: %s"), name);
+ return -1;
+}
+
+/* Returns LABEL or UUID */
+static const char *get_tag(struct libmnt_fs *fs, const char *tagname)
+{
+ const char *t, *v, *res;
+
+ if (!mnt_fs_get_tag(fs, &t, &v) && !strcmp(t, tagname))
+ res = v;
+ else {
+ res = mnt_fs_get_source(fs);
+ if (res)
+ res = mnt_resolve_spec(res, cache);
+ if (res)
+ res = mnt_cache_find_tag_value(cache, res, tagname);
+ }
+
+ return res;
+}
+
+static const char *get_vfs_attr(struct libmnt_fs *fs, int sizetype)
+{
+ struct statvfs buf;
+ uint64_t vfs_attr = 0;
+ char *sizestr;
+
+ if (statvfs(mnt_fs_get_target(fs), &buf) != 0)
+ return NULL;
+
+ switch(sizetype) {
+ case COL_SIZE:
+ vfs_attr = buf.f_frsize * buf.f_blocks;
+ break;
+ case COL_AVAIL:
+ vfs_attr = buf.f_frsize * buf.f_bavail;
+ break;
+ case COL_USED:
+ vfs_attr = buf.f_frsize * (buf.f_blocks - buf.f_bfree);
+ break;
+ case COL_USEPERC:
+ if (buf.f_blocks == 0)
+ return "-";
+
+ xasprintf(&sizestr, "%.0f%%",
+ (double)(buf.f_blocks - buf.f_bfree) /
+ buf.f_blocks * 100);
+ return sizestr;
+ }
+
+ return vfs_attr == 0 ? "0" :
+ size_to_human_string(SIZE_SUFFIX_1LETTER, vfs_attr);
+}
+
+/* reads FS data from libmount
+ */
+static const char *get_data(struct libmnt_fs *fs, int num)
+{
+ const char *str = NULL;
+ int col_id = get_column_id(num);
+
+ switch (col_id) {
+ case COL_SOURCE:
+ {
+ const char *root = mnt_fs_get_root(fs);
+
+ str = mnt_fs_get_srcpath(fs);
+
+ if (str && (flags & FL_CANONICALIZE))
+ str = mnt_resolve_path(str, cache);
+ if (!str) {
+ str = mnt_fs_get_source(fs);
+
+ if (str && (flags & FL_EVALUATE))
+ str = mnt_resolve_spec(str, cache);
+ }
+ if (root && str && !(flags & FL_NOFSROOT) && strcmp(root, "/")) {
+ char *tmp;
+
+ if (xasprintf(&tmp, "%s[%s]", str, root) > 0)
+ str = tmp;
+ }
+ break;
+ }
+ case COL_TARGET:
+ str = mnt_fs_get_target(fs);
+ break;
+ case COL_FSTYPE:
+ str = mnt_fs_get_fstype(fs);
+ break;
+ case COL_OPTIONS:
+ str = mnt_fs_get_options(fs);
+ break;
+ case COL_VFS_OPTIONS:
+ str = mnt_fs_get_vfs_options(fs);
+ break;
+ case COL_FS_OPTIONS:
+ str = mnt_fs_get_fs_options(fs);
+ break;
+ case COL_UUID:
+ str = get_tag(fs, "UUID");
+ break;
+ case COL_PARTUUID:
+ str = get_tag(fs, "PARTUUID");
+ break;
+ case COL_LABEL:
+ str = get_tag(fs, "LABEL");
+ break;
+ case COL_PARTLABEL:
+ str = get_tag(fs, "PARTLABEL");
+ break;
+
+ case COL_MAJMIN:
+ {
+ dev_t devno = mnt_fs_get_devno(fs);
+ if (devno) {
+ char *tmp;
+ int rc = 0;
+ if ((tt_flags & TT_FL_RAW) || (tt_flags & TT_FL_EXPORT))
+ rc = xasprintf(&tmp, "%u:%u",
+ major(devno), minor(devno));
+ else
+ rc = xasprintf(&tmp, "%3u:%-3u",
+ major(devno), minor(devno));
+ if (rc)
+ str = tmp;
+ }
+ break;
+ }
+ case COL_SIZE:
+ case COL_AVAIL:
+ case COL_USED:
+ case COL_USEPERC:
+ str = get_vfs_attr(fs, col_id);
+ break;
+ case COL_FSROOT:
+ str = mnt_fs_get_root(fs);
+ break;
+ case COL_TID:
+ if (mnt_fs_get_tid(fs)) {
+ char *tmp;
+ if (xasprintf(&tmp, "%d", mnt_fs_get_tid(fs)) > 0)
+ str = tmp;
+ }
+ break;
+ default:
+ break;
+ }
+ return str;
+}
+
+static const char *get_tabdiff_data(struct libmnt_fs *old_fs,
+ struct libmnt_fs *new_fs,
+ int change,
+ int num)
+{
+ const char *str = NULL;
+
+ switch (get_column_id(num)) {
+ case COL_ACTION:
+ switch (change) {
+ case MNT_TABDIFF_MOUNT:
+ str = _("mount");
+ break;
+ case MNT_TABDIFF_UMOUNT:
+ str = _("umount");
+ break;
+ case MNT_TABDIFF_REMOUNT:
+ str = _("remount");
+ break;
+ case MNT_TABDIFF_MOVE:
+ str = _("move");
+ break;
+ default:
+ str = _("unknown");
+ break;
+ }
+ break;
+ case COL_OLD_OPTIONS:
+ if (old_fs && (change == MNT_TABDIFF_REMOUNT ||
+ change == MNT_TABDIFF_UMOUNT))
+ str = mnt_fs_get_options(old_fs);
+ break;
+ case COL_OLD_TARGET:
+ if (old_fs && (change == MNT_TABDIFF_MOVE ||
+ change == MNT_TABDIFF_UMOUNT))
+ str = mnt_fs_get_target(old_fs);
+ break;
+ default:
+ if (new_fs)
+ str = get_data(new_fs, num);
+ else
+ str = get_data(old_fs, num);
+ break;
+ }
+ return str;
+}
+
+/* adds one line to the output @tab */
+static struct tt_line *add_line(struct tt *tt, struct libmnt_fs *fs,
+ struct tt_line *parent)
+{
+ int i;
+ struct tt_line *line = tt_add_line(tt, parent);
+
+ if (!line) {
+ warn(_("failed to add line to output"));
+ return NULL;
+ }
+ for (i = 0; i < ncolumns; i++)
+ tt_line_set_data(line, i, get_data(fs, i));
+
+ tt_line_set_userdata(line, fs);
+ return line;
+}
+
+static struct tt_line *add_tabdiff_line(struct tt *tt, struct libmnt_fs *new_fs,
+ struct libmnt_fs *old_fs, int change)
+{
+ int i;
+ struct tt_line *line = tt_add_line(tt, NULL);
+
+ if (!line) {
+ warn(_("failed to add line to output"));
+ return NULL;
+ }
+ for (i = 0; i < ncolumns; i++)
+ tt_line_set_data(line, i,
+ get_tabdiff_data(old_fs, new_fs, change, i));
+
+ return line;
+}
+
+static int has_line(struct tt *tt, struct libmnt_fs *fs)
+{
+ struct list_head *p;
+
+ list_for_each(p, &tt->tb_lines) {
+ struct tt_line *ln = list_entry(p, struct tt_line, ln_lines);
+ if ((struct libmnt_fs *) ln->userdata == fs)
+ return 1;
+ }
+ return 0;
+}
+
+/* reads filesystems from @tb (libmount) and fillin @tt (output table) */
+static int create_treenode(struct tt *tt, struct libmnt_table *tb,
+ struct libmnt_fs *fs, struct tt_line *parent_line)
+{
+ struct libmnt_fs *chld = NULL;
+ struct libmnt_iter *itr = NULL;
+ struct tt_line *line;
+ int rc = -1;
+
+ if (!fs) {
+ /* first call, get root FS */
+ if (mnt_table_get_root_fs(tb, &fs))
+ goto leave;
+ parent_line = NULL;
+
+ } else if ((flags & FL_SUBMOUNTS) && has_line(tt, fs))
+ return 0;
+
+ itr = mnt_new_iter(MNT_ITER_FORWARD);
+ if (!itr)
+ goto leave;
+
+ line = add_line(tt, fs, parent_line);
+ if (!line)
+ goto leave;
+
+ /*
+ * add all children to the output table
+ */
+ while(mnt_table_next_child_fs(tb, itr, fs, &chld) == 0) {
+ if (create_treenode(tt, tb, chld, line))
+ goto leave;
+ }
+ rc = 0;
+leave:
+ mnt_free_iter(itr);
+ return rc;
+}
+
+/* error callback */
+static int parser_errcb(struct libmnt_table *tb __attribute__ ((__unused__)),
+ const char *filename, int line)
+{
+ warnx(_("%s: parse error at line %d"), filename, line);
+ return 0;
+}
+
+static char **append_tabfile(char **files, int *nfiles, char *filename)
+{
+ files = xrealloc(files, sizeof(char *) * (*nfiles + 1));
+ files[(*nfiles)++] = filename;
+ return files;
+}
+
+static char **append_pid_tabfile(char **files, int *nfiles, pid_t pid)
+{
+ char *path = NULL;
+
+ xasprintf(&path, "/proc/%d/mountinfo", (int) pid);
+ return append_tabfile(files, nfiles, path);
+}
+
+/* calls libmount fstab/mtab/mountinfo parser */
+static struct libmnt_table *parse_tabfiles(char **files,
+ int nfiles,
+ int tabtype)
+{
+ struct libmnt_table *tb;
+ int rc = 0;
+
+ tb = mnt_new_table();
+ if (!tb) {
+ warn(_("failed to initialize libmount table"));
+ return NULL;
+ }
+ mnt_table_set_parser_errcb(tb, parser_errcb);
+
+ do {
+ /* NULL means that libmount will use default paths */
+ const char *path = nfiles ? *files++ : NULL;
+
+ switch (tabtype) {
+ case TABTYPE_FSTAB:
+ rc = mnt_table_parse_fstab(tb, path);
+ break;
+ case TABTYPE_MTAB:
+ rc = mnt_table_parse_mtab(tb, path);
+ break;
+ case TABTYPE_KERNEL:
+ if (!path)
+ path = access(_PATH_PROC_MOUNTINFO, R_OK) == 0 ?
+ _PATH_PROC_MOUNTINFO :
+ _PATH_PROC_MOUNTS;
+
+ rc = mnt_table_parse_file(tb, path);
+ break;
+ }
+ if (rc) {
+ mnt_free_table(tb);
+ warn(_("can't read %s"), path);
+ return NULL;
+ }
+ } while (--nfiles > 0);
+
+ return tb;
+}
+
+/* checks if @tb contains parent->child relations */
+static int tab_is_tree(struct libmnt_table *tb)
+{
+ struct libmnt_fs *fs = NULL;
+ struct libmnt_iter *itr = NULL;
+ int rc = 0;
+
+ itr = mnt_new_iter(MNT_ITER_BACKWARD);
+ if (!itr)
+ return 0;
+
+ if (mnt_table_next_fs(tb, itr, &fs) == 0)
+ rc = mnt_fs_get_id(fs) > 0 && mnt_fs_get_parent_id(fs) > 0;
+
+ mnt_free_iter(itr);
+ return rc;
+}
+
+
+/* filter function for libmount (mnt_table_find_next_fs()) */
+static int match_func(struct libmnt_fs *fs,
+ void *data __attribute__ ((__unused__)))
+{
+ int rc = flags & FL_INVERT ? 1 : 0;
+ const char *m;
+ void *md;
+
+ m = get_match(COL_TARGET);
+ if (m && !mnt_fs_match_target(fs, m, cache))
+ return rc;
+
+ m = get_match(COL_SOURCE);
+ if (m && !mnt_fs_match_source(fs, m, cache))
+ return rc;
+
+ m = get_match(COL_FSTYPE);
+ if (m && !mnt_fs_match_fstype(fs, m))
+ return rc;
+
+ m = get_match(COL_OPTIONS);
+ if (m && !mnt_fs_match_options(fs, m))
+ return rc;
+
+ md = get_match_data(COL_MAJMIN);
+ if (md && mnt_fs_get_devno(fs) != *((dev_t *) md))
+ return rc;
+
+ if ((flags & FL_DF) && !(flags & FL_ALL)) {
+ const char *type = mnt_fs_get_fstype(fs);
+
+ if (type && strstr(type, "tmpfs")) /* tmpfs is wanted */
+ return !rc;
+
+ if (mnt_fs_is_pseudofs(fs))
+ return rc;
+ }
+
+ return !rc;
+}
+
+/* iterate over filesystems in @tb */
+static struct libmnt_fs *get_next_fs(struct libmnt_table *tb,
+ struct libmnt_iter *itr)
+{
+ struct libmnt_fs *fs = NULL;
+
+ if (is_listall_mode()) {
+ /*
+ * Print whole file
+ */
+ if (mnt_table_next_fs(tb, itr, &fs) != 0)
+ return NULL;
+
+ } else if (is_mount_compatible_mode()) {
+ /*
+ * Look up for FS in the same way how mount(8) searchs in fstab
+ *
+ * findmnt -f <spec>
+ */
+ fs = mnt_table_find_source(tb, get_match(COL_SOURCE),
+ mnt_iter_get_direction(itr));
+
+ if (!fs && !(flags & FL_NOSWAPMATCH))
+ fs = mnt_table_find_target(tb, get_match(COL_SOURCE),
+ mnt_iter_get_direction(itr));
+ } else {
+ /*
+ * Look up for all matching entries
+ *
+ * findmnt [-l] <source> <target> [-O <options>] [-t <types>]
+ * findmnt [-l] <spec> [-O <options>] [-t <types>]
+ */
+again:
+ mnt_table_find_next_fs(tb, itr, match_func, NULL, &fs);
+
+ if (!fs &&
+ !(flags & FL_NOSWAPMATCH) &&
+ !get_match(COL_TARGET) && get_match(COL_SOURCE)) {
+
+ /* swap 'spec' and target. */
+ set_match(COL_TARGET, get_match(COL_SOURCE));
+ set_match(COL_SOURCE, NULL);
+ mnt_reset_iter(itr, -1);
+
+ goto again;
+ }
+ }
+
+ return fs;
+}
+
+static int add_matching_lines(struct libmnt_table *tb,
+ struct tt *tt, int direction)
+{
+ struct libmnt_iter *itr = NULL;
+ struct libmnt_fs *fs;
+ int nlines = 0, rc = -1;
+
+ itr = mnt_new_iter(direction);
+ if (!itr) {
+ warn(_("failed to initialize libmount iterator"));
+ goto done;
+ }
+
+ while((fs = get_next_fs(tb, itr))) {
+ if ((tt_flags & TT_FL_TREE) || (flags & FL_SUBMOUNTS))
+ rc = create_treenode(tt, tb, fs, NULL);
+ else
+ rc = !add_line(tt, fs, NULL);
+ if (rc)
+ goto done;
+ nlines++;
+ if (flags & FL_FIRSTONLY)
+ break;
+ flags |= FL_NOSWAPMATCH;
+ }
+
+ if (nlines)
+ rc = 0;
+done:
+ mnt_free_iter(itr);
+ return rc;
+}
+
+static int poll_match(struct libmnt_fs *fs)
+{
+ int rc = match_func(fs, NULL);
+
+ if (rc == 0 && !(flags & FL_NOSWAPMATCH) &&
+ get_match(COL_SOURCE) && !get_match(COL_TARGET)) {
+ /*
+ * findmnt --poll /foo
+ * The '/foo' maybe source as well as target.
+ */
+ const char *str = get_match(COL_SOURCE);
+
+ set_match(COL_TARGET, str); /* swap */
+ set_match(COL_SOURCE, NULL);
+
+ rc = match_func(fs, NULL);
+
+ set_match(COL_TARGET, NULL); /* restore */
+ set_match(COL_SOURCE, str);
+
+ }
+ return rc;
+}
+
+static int poll_table(struct libmnt_table *tb, const char *tabfile,
+ int timeout, struct tt *tt, int direction)
+{
+ FILE *f = NULL;
+ int rc = -1;
+ struct libmnt_iter *itr = NULL;
+ struct libmnt_table *tb_new = NULL;
+ struct libmnt_tabdiff *diff = NULL;
+ struct pollfd fds[1];
+
+ tb_new = mnt_new_table();
+ if (!tb_new) {
+ warn(_("failed to initialize libmount table"));
+ goto done;
+ }
+
+ itr = mnt_new_iter(direction);
+ if (!itr) {
+ warn(_("failed to initialize libmount iterator"));
+ goto done;
+ }
+
+ diff = mnt_new_tabdiff();
+ if (!diff) {
+ warn(_("failed to initialize libmount tabdiff"));
+ goto done;
+ }
+
+ /* cache is unnecessary to detect changes */
+ mnt_table_set_cache(tb, NULL);
+ mnt_table_set_cache(tb_new, NULL);
+
+ f = fopen(tabfile, "r");
+ if (!f) {
+ warn(_("cannot open %s"), tabfile);
+ goto done;
+ }
+
+ mnt_table_set_parser_errcb(tb_new, parser_errcb);
+
+ fds[0].fd = fileno(f);
+ fds[0].events = POLLPRI;
+
+ while (1) {
+ struct libmnt_table *tmp;
+ struct libmnt_fs *old, *new;
+ int change, count;
+
+ count = poll(fds, 1, timeout);
+ if (count == 0)
+ break; /* timeout */
+ if (count < 0) {
+ warn(_("poll() failed"));
+ goto done;
+ }
+
+ rewind(f);
+ rc = mnt_table_parse_stream(tb_new, f, tabfile);
+ if (!rc)
+ rc = mnt_diff_tables(diff, tb, tb_new);
+ if (rc < 0)
+ goto done;
+
+ count = 0;
+ mnt_reset_iter(itr, direction);
+ while(mnt_tabdiff_next_change(
+ diff, itr, &old, &new, &change) == 0) {
+
+ if (!has_poll_action(change))
+ continue;
+ if (!poll_match(new ? new : old))
+ continue;
+ count++;
+ rc = !add_tabdiff_line(tt, new, old, change);
+ if (rc)
+ goto done;
+ if (flags & FL_FIRSTONLY)
+ break;
+ }
+
+ if (count) {
+ rc = tt_print_table(tt);
+ if (rc)
+ goto done;
+ }
+
+ /* swap tables */
+ tmp = tb;
+ tb = tb_new;
+ tb_new = tmp;
+
+ tt_remove_lines(tt);
+ mnt_reset_table(tb_new);
+
+ if (count && (flags & FL_FIRSTONLY))
+ break;
+ }
+
+ rc = 0;
+done:
+ mnt_free_table(tb_new);
+ mnt_free_tabdiff(diff);
+ mnt_free_iter(itr);
+ if (f)
+ fclose(f);
+ return rc;
+}
+
+static void __attribute__((__noreturn__)) usage(FILE *out)
+{
+ int i;
+
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(
+ " %1$s [options]\n"
+ " %1$s [options] <device> | <mountpoint>\n"
+ " %1$s [options] <device> <mountpoint>\n"
+ " %1$s [options] [--source <device>] [--target <mountpoint>]\n"),
+ program_invocation_short_name);
+
+ fprintf(out, _(
+ "\nOptions:\n"
+ " -s, --fstab search in static table of filesystems\n"
+ " -m, --mtab search in table of mounted filesystems\n"
+ " -k, --kernel search in kernel table of mounted\n"
+ " filesystems (default)\n\n"));
+
+ fprintf(out, _(
+ " -p, --poll[=<list>] monitor changes in table of mounted filesystems\n"
+ " -w, --timeout <num> upper limit in milliseconds that --poll will block\n\n"));
+
+ fprintf(out, _(
+ " -A, --all disable all built-in filters, print all filesystems\n"
+ " -a, --ascii use ASCII chars for tree formatting\n"
+ " -c, --canonicalize canonicalize printed paths\n"
+ " -D, --df imitate the output of df(1)\n"
+ " -d, --direction <word> direction of search, 'forward' or 'backward'\n"
+ " -e, --evaluate convert tags (LABEL,UUID,PARTUUID,PARTLABEL) \n"
+ " to device names\n"
+ " -F, --tab-file <path> alternative file for --fstab, --mtab or --kernel options\n"
+ " -f, --first-only print the first found filesystem only\n"));
+
+ fprintf(out, _(
+ " -i, --invert invert the sense of matching\n"
+ " -l, --list use list format output\n"
+ " -N, --task <tid> use alternative namespace (/proc/<tid>/mountinfo file)\n"
+ " -n, --noheadings don't print column headings\n"
+ " -u, --notruncate don't truncate text in columns\n"));
+ fprintf(out, _(
+ " -O, --options <list> limit the set of filesystems by mount options\n"
+ " -o, --output <list> the output columns to be shown\n"
+ " -P, --pairs use key=\"value\" output format\n"
+ " -r, --raw use raw output format\n"
+ " -t, --types <list> limit the set of filesystems by FS types\n"));
+ fprintf(out, _(
+ " -v, --nofsroot don't print [/dir] for bind or btrfs mounts\n"
+ " -R, --submounts print all submounts for the matching filesystems\n"
+ " -S, --source <string> the device to mount (by name, maj:min, \n"
+ " LABEL=, UUID=, PARTUUID=, PARTLABEL=)\n"
+ " -T, --target <string> the mountpoint to use\n"));
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+
+ fprintf(out, _("\nAvailable columns:\n"));
+
+ for (i = 0; i < FINDMNT_NCOLUMNS; i++)
+ fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help));
+
+ fprintf(out, USAGE_MAN_TAIL("findmnt(8)"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+ struct libmnt_table *tb = NULL;
+ char **tabfiles = NULL;
+ int direction = MNT_ITER_FORWARD;
+ int i, c, rc = -1, timeout = -1;
+ int ntabfiles = 0, tabtype = 0;
+ char *outarg = NULL;
+
+ struct tt *tt = NULL;
+
+ static const struct option longopts[] = {
+ { "all", 0, 0, 'A' },
+ { "ascii", 0, 0, 'a' },
+ { "canonicalize", 0, 0, 'c' },
+ { "direction", 1, 0, 'd' },
+ { "df", 0, 0, 'D' },
+ { "evaluate", 0, 0, 'e' },
+ { "first-only", 0, 0, 'f' },
+ { "fstab", 0, 0, 's' },
+ { "help", 0, 0, 'h' },
+ { "invert", 0, 0, 'i' },
+ { "kernel", 0, 0, 'k' },
+ { "list", 0, 0, 'l' },
+ { "mtab", 0, 0, 'm' },
+ { "noheadings", 0, 0, 'n' },
+ { "notruncate", 0, 0, 'u' },
+ { "options", 1, 0, 'O' },
+ { "output", 1, 0, 'o' },
+ { "poll", 2, 0, 'p' },
+ { "pairs", 0, 0, 'P' },
+ { "raw", 0, 0, 'r' },
+ { "types", 1, 0, 't' },
+ { "fsroot", 0, 0, 'v' },
+ { "submounts", 0, 0, 'R' },
+ { "source", 1, 0, 'S' },
+ { "tab-file", 1, 0, 'F' },
+ { "task", 1, 0, 'N' },
+ { "target", 1, 0, 'T' },
+ { "timeout", 1, 0, 'w' },
+ { "version", 0, 0, 'V' },
+
+ { NULL, 0, 0, 0 }
+ };
+
+ static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */
+ { 'N','k','m','s' }, /* task,kernel,mtab,fstab */
+ { 'P','l','r' }, /* pairs,list,raw */
+ { 'm','p','s' }, /* mtab,poll,fstab */
+ { 0 }
+ };
+ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+
+ assert(ARRAY_SIZE(columns) == FINDMNT_NCOLUMNS);
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ /* default output format */
+ tt_flags |= TT_FL_TREE;
+
+ while ((c = getopt_long(argc, argv,
+ "AacDd:ehifF:o:O:p::PklmnN:rst:uvRS:T:w:V",
+ longopts, NULL)) != -1) {
+
+ err_exclusive_options(c, longopts, excl, excl_st);
+
+ switch(c) {
+ case 'A':
+ flags |= FL_ALL;
+ break;
+ case 'a':
+ tt_flags |= TT_FL_ASCII;
+ break;
+ case 'c':
+ flags |= FL_CANONICALIZE;
+ break;
+ case 'D':
+ tt_flags &= ~TT_FL_TREE;
+ flags |= FL_DF;
+ break;
+ case 'd':
+ if (!strcmp(optarg, "forward"))
+ direction = MNT_ITER_FORWARD;
+ else if (!strcmp(optarg, "backward"))
+ direction = MNT_ITER_BACKWARD;
+ else
+ errx(EXIT_FAILURE,
+ _("unknown direction '%s'"), optarg);
+ break;
+ case 'e':
+ flags |= FL_EVALUATE;
+ break;
+ case 'h':
+ usage(stdout);
+ break;
+ case 'i':
+ flags |= FL_INVERT;
+ break;
+ case 'f':
+ flags |= FL_FIRSTONLY;
+ break;
+ case 'F':
+ tabfiles = append_tabfile(tabfiles, &ntabfiles, optarg);
+ break;
+ case 'u':
+ disable_columns_truncate();
+ break;
+ case 'o':
+ outarg = optarg;
+ break;
+ case 'O':
+ set_match(COL_OPTIONS, optarg);
+ break;
+ case 'p':
+ if (optarg) {
+ nactions = string_to_idarray(optarg,
+ actions, ARRAY_SIZE(actions),
+ poll_action_name_to_id);
+ if (nactions < 0)
+ exit(EXIT_FAILURE);
+ }
+ flags |= FL_POLL;
+ tt_flags &= ~TT_FL_TREE;
+ break;
+ case 'P':
+ tt_flags |= TT_FL_EXPORT;
+ tt_flags &= ~TT_FL_TREE;
+ break;
+ case 'm': /* mtab */
+ tabtype = TABTYPE_MTAB;
+ tt_flags &= ~TT_FL_TREE;
+ break;
+ case 's': /* fstab */
+ tabtype = TABTYPE_FSTAB;
+ tt_flags &= ~TT_FL_TREE;
+ break;
+ case 'k': /* kernel (mountinfo) */
+ tabtype = TABTYPE_KERNEL;
+ break;
+ case 't':
+ set_match(COL_FSTYPE, optarg);
+ break;
+ case 'r':
+ tt_flags &= ~TT_FL_TREE; /* disable the default */
+ tt_flags |= TT_FL_RAW; /* enable raw */
+ break;
+ case 'l':
+ tt_flags &= ~TT_FL_TREE; /* disable the default */
+ break;
+ case 'n':
+ tt_flags |= TT_FL_NOHEADINGS;
+ break;
+ case 'N':
+ tabtype = TABTYPE_KERNEL;
+ tabfiles = append_pid_tabfile(tabfiles, &ntabfiles,
+ strtou32_or_err(optarg,
+ _("invalid TID argument")));
+ break;
+ case 'v':
+ flags |= FL_NOFSROOT;
+ break;
+ case 'R':
+ flags |= FL_SUBMOUNTS;
+ break;
+ case 'S':
+ set_source_match(optarg);
+ flags |= FL_NOSWAPMATCH;
+ break;
+ case 'T':
+ set_match(COL_TARGET, optarg);
+ flags |= FL_NOSWAPMATCH;
+ break;
+ case 'w':
+ timeout = strtos32_or_err(optarg, _("invalid timeout argument"));
+ break;
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ default:
+ usage(stderr);
+ break;
+ }
+ }
+
+ if (!ncolumns && (flags & FL_DF)) {
+ columns[ncolumns++] = COL_SOURCE;
+ columns[ncolumns++] = COL_FSTYPE;
+ columns[ncolumns++] = COL_SIZE;
+ columns[ncolumns++] = COL_USED;
+ columns[ncolumns++] = COL_AVAIL;
+ columns[ncolumns++] = COL_USEPERC;
+ columns[ncolumns++] = COL_TARGET;
+ }
+
+ /* default columns */
+ if (!ncolumns) {
+ if (flags & FL_POLL)
+ columns[ncolumns++] = COL_ACTION;
+
+ columns[ncolumns++] = COL_TARGET;
+ columns[ncolumns++] = COL_SOURCE;
+ columns[ncolumns++] = COL_FSTYPE;
+ columns[ncolumns++] = COL_OPTIONS;
+ }
+
+ if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
+ &ncolumns, column_name_to_id) < 0)
+ return EXIT_FAILURE;
+
+ if (!tabtype)
+ tabtype = TABTYPE_KERNEL;
+
+ if ((flags & FL_POLL) && ntabfiles > 1)
+ errx(EXIT_FAILURE, _("--poll accepts only one file, but more specified by --tab-file"));
+
+ if (optind < argc && (get_match(COL_SOURCE) || get_match(COL_TARGET)))
+ errx(EXIT_FAILURE, _(
+ "options --target and --source can't be used together "
+ "with command line element that is not an option"));
+
+ if (optind < argc)
+ set_source_match(argv[optind++]); /* dev/tag/mountpoint/maj:min */
+ if (optind < argc)
+ set_match(COL_TARGET, argv[optind++]); /* mountpoint */
+
+ if ((flags & FL_SUBMOUNTS) && is_listall_mode())
+ /* don't care about submounts if list all mounts */
+ flags &= ~FL_SUBMOUNTS;
+
+ if (!(flags & FL_SUBMOUNTS) &&
+ (!is_listall_mode() || (flags & FL_FIRSTONLY)))
+ tt_flags &= ~TT_FL_TREE;
+
+ if (!(flags & FL_NOSWAPMATCH) &&
+ !get_match(COL_TARGET) && get_match(COL_SOURCE)) {
+ /*
+ * Check if we can swap source and target, it's
+ * not possible if the source is LABEL=/UUID=
+ */
+ const char *x = get_match(COL_SOURCE);
+
+ if (!strncmp(x, "LABEL=", 6) || !strncmp(x, "UUID=", 5) ||
+ !strncmp(x, "PARTLABEL=", 10) || !strncmp(x, "PARTUUID=", 9))
+ flags |= FL_NOSWAPMATCH;
+ }
+
+ /*
+ * initialize libmount
+ */
+ mnt_init_debug(0);
+
+ tb = parse_tabfiles(tabfiles, ntabfiles, tabtype);
+ if (!tb)
+ goto leave;
+
+ if ((tt_flags & TT_FL_TREE) && (ntabfiles > 1 || !tab_is_tree(tb)))
+ tt_flags &= ~TT_FL_TREE;
+
+ cache = mnt_new_cache();
+ if (!cache) {
+ warn(_("failed to initialize libmount cache"));
+ goto leave;
+ }
+ mnt_table_set_cache(tb, cache);
+
+ if (tabtype == TABTYPE_KERNEL
+ && (flags & FL_NOSWAPMATCH)
+ && get_match(COL_TARGET))
+ /*
+ * enable extra functionality for target match
+ */
+ enable_extra_target_match();
+
+ /*
+ * initialize output formatting (tt.h)
+ */
+ tt = tt_new_table(tt_flags);
+ if (!tt) {
+ warn(_("failed to initialize output table"));
+ goto leave;
+ }
+
+ for (i = 0; i < ncolumns; i++) {
+ int fl = get_column_flags(i);
+ int id = get_column_id(i);
+
+ if (!(tt_flags & TT_FL_TREE))
+ fl &= ~TT_FL_TREE;
+
+ if (!(flags & FL_POLL) && is_tabdiff_column(id)) {
+ warnx(_("%s column is requested, but --poll "
+ "is not enabled"), get_column_name(i));
+ goto leave;
+ }
+ if (!tt_define_column(tt, get_column_name(i),
+ get_column_whint(i), fl)) {
+ warn(_("failed to initialize output column"));
+ goto leave;
+ }
+ }
+
+ /*
+ * Fill in data to the output table
+ */
+ if (flags & FL_POLL) {
+ /* poll mode (accept the first tabfile only) */
+ rc = poll_table(tb, tabfiles ? *tabfiles : _PATH_PROC_MOUNTINFO, timeout, tt, direction);
+
+ } else if ((tt_flags & TT_FL_TREE) && is_listall_mode())
+ /* whole tree */
+ rc = create_treenode(tt, tb, NULL, NULL);
+ else
+ /* whole lits of sub-tree */
+ rc = add_matching_lines(tb, tt, direction);
+
+ /*
+ * Print the output table for non-poll modes
+ */
+ if (!rc && !(flags & FL_POLL))
+ tt_print_table(tt);
+leave:
+ tt_free_table(tt);
+
+ mnt_free_table(tb);
+ mnt_free_cache(cache);
+ free(tabfiles);
+
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/misc-utils/getopt-parse.bash b/misc-utils/getopt-parse.bash
new file mode 100644
index 0000000..29e7f6f
--- /dev/null
+++ b/misc-utils/getopt-parse.bash
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+# A small example program for using the new getopt(1) program.
+# This program will only work with bash(1)
+# An similar program using the tcsh(1) script language can be found
+# as parse.tcsh
+
+# Example input and output (from the bash prompt):
+# ./parse.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long "
+# Option a
+# Option c, no argument
+# Option c, argument `more'
+# Option b, argument ` very long '
+# Remaining arguments:
+# --> `par1'
+# --> `another arg'
+# --> `wow!*\?'
+
+# Note that we use `"$@"' to let each command-line parameter expand to a
+# separate word. The quotes around `$@' are essential!
+# We need TEMP as the `eval set --' would nuke the return value of getopt.
+TEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \
+ -n 'example.bash' -- "$@"`
+
+if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
+
+# Note the quotes around `$TEMP': they are essential!
+eval set -- "$TEMP"
+
+while true ; do
+ case "$1" in
+ -a|--a-long) echo "Option a" ; shift ;;
+ -b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;;
+ -c|--c-long)
+ # c has an optional argument. As we are in quoted mode,
+ # an empty parameter will be generated if its optional
+ # argument is not found.
+ case "$2" in
+ "") echo "Option c, no argument"; shift 2 ;;
+ *) echo "Option c, argument \`$2'" ; shift 2 ;;
+ esac ;;
+ --) shift ; break ;;
+ *) echo "Internal error!" ; exit 1 ;;
+ esac
+done
+echo "Remaining arguments:"
+for arg do echo '--> '"\`$arg'" ; done
diff --git a/misc-utils/getopt-parse.tcsh b/misc-utils/getopt-parse.tcsh
new file mode 100644
index 0000000..408c470
--- /dev/null
+++ b/misc-utils/getopt-parse.tcsh
@@ -0,0 +1,77 @@
+#!/bin/tcsh
+
+# A small example program for using the new getopt(1) program.
+# This program will only work with tcsh(1)
+# An similar program using the bash(1) script language can be found
+# as parse.bash
+
+# Example input and output (from the tcsh prompt):
+# ./parse.tcsh -a par1 'another arg' --c-long 'wow\!*\?' -cmore -b " very long "
+# Option a
+# Option c, no argument
+# Option c, argument `more'
+# Option b, argument ` very long '
+# Remaining arguments:
+# --> `par1'
+# --> `another arg'
+# --> `wow!*\?'
+
+# Note that we had to escape the exclamation mark in the wow-argument. This
+# is _not_ a problem with getopt, but with the tcsh command parsing. If you
+# would give the same line from the bash prompt (ie. call ./parse.tcsh),
+# you could remove the exclamation mark.
+
+# This is a bit tricky. We use a temp variable, to be able to check the
+# return value of getopt (eval nukes it). argv contains the command arguments
+# as a list. The ':q` copies that list without doing any substitutions:
+# each element of argv becomes a separate argument for getopt. The braces
+# are needed because the result is also a list.
+set temp=(`getopt -s tcsh -o ab:c:: --long a-long,b-long:,c-long:: -- $argv:q`)
+if ($? != 0) then
+ echo "Terminating..." >/dev/stderr
+ exit 1
+endif
+
+# Now we do the eval part. As the result is a list, we need braces. But they
+# must be quoted, because they must be evaluated when the eval is called.
+# The 'q` stops doing any silly substitutions.
+eval set argv=\($temp:q\)
+
+while (1)
+ switch($1:q)
+ case -a:
+ case --a-long:
+ echo "Option a" ; shift
+ breaksw;
+ case -b:
+ case --b-long:
+ echo "Option b, argument "\`$2:q\' ; shift ; shift
+ breaksw
+ case -c:
+ case --c-long:
+ # c has an optional argument. As we are in quoted mode,
+ # an empty parameter will be generated if its optional
+ # argument is not found.
+
+ if ($2:q == "") then
+ echo "Option c, no argument"
+ else
+ echo "Option c, argument "\`$2:q\'
+ endif
+ shift; shift
+ breaksw
+ case --:
+ shift
+ break
+ default:
+ echo "Internal error!" ; exit 1
+ endsw
+end
+
+echo "Remaining arguments:"
+# foreach el ($argv:q) created problems for some tcsh-versions (at least
+# 6.02). So we use another shift-loop here:
+while ($#argv > 0)
+ echo '--> '\`$1:q\'
+ shift
+end
diff --git a/misc-utils/getopt.1 b/misc-utils/getopt.1
new file mode 100644
index 0000000..6558e12
--- /dev/null
+++ b/misc-utils/getopt.1
@@ -0,0 +1,448 @@
+.TH GETOPT "1" "June 2012" "util-linux" "User Commands"
+.SH NAME
+getopt \- parse command options (enhanced)
+.SH SYNOPSIS
+getopt optstring parameters
+.br
+getopt [options] [\-\-] optstring parameters
+.br
+getopt [options] \-o|\-\-options optstring [options] [\-\-]
+.I parameters
+.SH DESCRIPTION
+.B getopt
+is used to break up
+.RI ( parse )
+options in command lines for easy parsing by shell procedures, and to
+check for legal options. It uses the
+.SM GNU
+.BR getopt (3)
+routines to do this.
+.PP
+The parameters
+.B getopt
+is called with can be divided into two parts: options which modify
+the way getopt will parse
+.RI ( options
+and
+.BR \-o | \-\-options
+.I optstring
+in the
+.BR SYNOPSIS ),
+and the parameters which are to be parsed
+.RI ( parameters
+in the
+.BR SYNOPSIS ).
+The second part will start at the first non\-option parameter that is
+not an option argument, or after the first occurrence of
+.RB ' \-\- '.
+If no
+.RB ' \-o '
+or
+.RB ' \-\-options '
+option is found in the first part, the first parameter of the second
+part is used as the short options string.
+.PP
+If the environment variable
+.B GETOPT_COMPATIBLE
+is set, or if its first parameter is not an option (does not start
+with a
+.RB ' \- ',
+this is the first format in the
+.BR SYNOPSIS ),
+.B getopt
+will generate output that is compatible with that of other versions of
+.BR getopt (1).
+It will still do parameter shuffling and recognize optional arguments
+(see section
+.B COMPATIBILITY
+for more information).
+.PP
+Traditional implementations of
+.BR getopt (1)
+are unable to cope with whitespace and other (shell\-specific)
+special characters in arguments and non\-option parameters. To solve
+this problem, this implementation can generate quoted output which
+must once again be interpreted by the shell (usually by using the
+.B eval
+command). This has the effect of preserving those characters, but
+you must call
+.B getopt
+in a way that is no longer compatible with other versions (the second
+or third format in the
+.BR SYNOPSIS ).
+To determine whether this enhanced version of
+.BR getopt (1)
+is installed, a special test option
+.RB ( \-T )
+can be used.
+.SH OPTIONS
+.TP
+.BR \-a , " \-\-alternative"
+Allow long options to start with a single
+.RB ' \- '.
+.TP
+.BR \-h , " \-\-help"
+Output a small usage guide and exit successfully. No other output is
+generated.
+.TP
+.BR \-l , " \-\-longoptions \fIlongopts\fP"
+The long (multi\-character) options to be recognized. More than one
+option name may be specified at once, by separating the names with
+commas. This option may be given more than once, the
+.I longopts
+are cumulative. Each long option name in
+.I longopts
+may be followed by one colon to indicate it has a required argument,
+and by two colons to indicate it has an optional argument.
+.TP
+.BR \-n , " \-\-name \fIprogname\fP"
+The name that will be used by the
+.BR getopt (3)
+routines when it reports errors. Note that errors of
+.BR getopt (1)
+are still reported as coming from getopt.
+.TP
+.BR \-o , " \-\-options \fIshortopts\fP"
+The short (one\-character) options to be recognized. If this option
+is not found, the first parameter of
+.B getopt
+that does not start with a
+.RB ' \- '
+(and is not an option argument) is used as the short options string.
+Each short option character in
+.I shortopts
+may be followed by one colon to indicate it has a required argument,
+and by two colons to indicate it has an optional argument. The first
+character of shortopts may be
+.RB ' + '
+or
+.RB ' \- '
+to influence the way options are parsed and output is generated (see
+section
+.B SCANNING MODES
+for details).
+.TP
+.BR \-q , " \-\-quiet"
+Disable error reporting by getopt(3).
+.TP
+.BR \-Q , " \-\-quiet\-output"
+Do not generate normal output. Errors are still reported by
+.BR getopt (3),
+unless you also use
+.IR \-q .
+.TP
+.BR \-s , " \-\-shell \fIshell\fP"
+Set quoting conventions to those of shell. If no \-s argument is
+found, the
+.SM BASH
+conventions are used. Valid arguments are currently
+.RB ' sh '
+.RB ' bash ',
+.RB ' csh ',
+and
+.RB ' tcsh '.
+.TP
+.BR \-u , " \-\-unquoted"
+Do not quote the output. Note that whitespace and special
+(shell\-dependent) characters can cause havoc in this mode (like they
+do with other
+.BR getopt (1)
+implementations).
+.TP
+.BR \-T , " \-\-test"
+Test if your
+.BR getopt (1)
+is this enhanced version or an old version. This generates no
+output, and sets the error status to 4. Other implementations of
+.BR getopt (1),
+and this version if the environment variable
+.B GETOPT_COMPATIBLE
+is set, will return
+.RB ' \-\- '
+and error status 0.
+.TP
+.BR \-V , " \-\-version"
+Output version information and exit successfully. No other output is
+generated.
+.SH PARSING
+This section specifies the format of the second part of the
+parameters of
+.B getopt
+(the
+.I parameters
+in the
+.BR SYNOPSIS ).
+The next section
+.RB ( OUTPUT )
+describes the output that is generated. These parameters were
+typically the parameters a shell function was called with. Care must
+be taken that each parameter the shell function was called with
+corresponds to exactly one parameter in the parameter list of
+.B getopt
+(see the
+.BR EXAMPLES ).
+All parsing is done by the GNU
+.BR getopt (3)
+routines.
+.PP
+The parameters are parsed from left to right. Each parameter is
+classified as a short option, a long option, an argument to an
+option, or a non\-option parameter.
+.PP
+A simple short option is a
+.RB ' \- '
+followed by a short option character. If the option has a required
+argument, it may be written directly after the option character or as
+the next parameter (ie. separated by whitespace on the command
+line). If the option has an optional argument, it must be written
+directly after the option character if present.
+.PP
+It is possible to specify several short options after one
+.RB ' \- ',
+as long as all (except possibly the last) do not have required or
+optional arguments.
+.PP
+A long option normally begins with
+.RB ' \-\- '
+followed by the long option name. If the option has a required
+argument, it may be written directly after the long option name,
+separated by
+.RB ' = ',
+or as the next argument (i.e. separated by whitespace on the command
+line). If the option has an optional argument, it must be written
+directly after the long option name, separated by
+.RB ' = ',
+if present (if you add the
+.RB ' = '
+but nothing behind it, it is interpreted as if no argument was
+present; this is a slight bug, see the
+.BR BUGS ).
+Long options may be abbreviated, as long as the abbreviation is not
+ambiguous.
+.PP
+Each parameter not starting with a
+.RB ' \- ',
+and not a required argument of a previous option, is a non\-option
+parameter. Each parameter after a
+.RB ' \-\- '
+parameter is always interpreted as a non\-option parameter. If the
+environment variable
+.B POSIXLY_CORRECT
+is set, or if the short option string started with a
+.RB ' + ',
+all remaining parameters are interpreted as non\-option parameters as
+soon as the first non\-option parameter is found.
+.SH OUTPUT
+Output is generated for each element described in the previous
+section. Output is done in the same order as the elements are
+specified in the input, except for non\-option parameters. Output
+can be done in
+.I compatible
+.RI ( unquoted )
+mode, or in such way that whitespace and other special characters
+within arguments and non\-option parameters are preserved (see
+.BR QUOTING ).
+When the output is processed in the shell script, it will seem to be
+composed of distinct elements that can be processed one by one (by
+using the shift command in most shell languages). This is imperfect
+in unquoted mode, as elements can be split at unexpected places if
+they contain whitespace or special characters.
+.PP
+If there are problems parsing the parameters, for example because a
+required argument is not found or an option is not recognized, an
+error will be reported on stderr, there will be no output for the
+offending element, and a non\-zero error status is returned.
+.PP
+For a short option, a single
+.RB ' \- '
+and the option character are generated as one parameter. If the
+option has an argument, the next parameter will be the argument. If
+the option takes an optional argument, but none was found, the next
+parameter will be generated but be empty in quoting mode, but no
+second parameter will be generated in unquoted (compatible) mode.
+Note that many other
+.BR getopt (1)
+implementations do not support optional arguments.
+.PP
+If several short options were specified after a single
+.RB ' \- ',
+each will be present in the output as a separate parameter.
+.PP
+For a long option,
+.RB ' \-\- '
+and the full option name are generated as one parameter. This is
+done regardless whether the option was abbreviated or specified with
+a single
+.RB ' \- '
+in the input. Arguments are handled as with short options.
+.PP
+Normally, no non\-option parameters output is generated until all
+options and their arguments have been generated. Then
+.RB ' \-\- '
+is generated as a single parameter, and after it the non\-option
+parameters in the order they were found, each as a separate
+parameter. Only if the first character of the short options string
+was a
+.RB ' \- ',
+non\-option parameter output is generated at the place they are found
+in the input (this is not supported if the first format of the
+.B SYNOPSIS
+is used; in that case all preceding occurrences of
+.RB ' \- '
+and
+.RB ' + '
+are ignored).
+.SH QUOTING
+In compatible mode, whitespace or 'special' characters in arguments
+or non\-option parameters are not handled correctly. As the output
+is fed to the shell script, the script does not know how it is
+supposed to break the output into separate parameters. To circumvent
+this problem, this implementation offers quoting. The idea is that
+output is generated with quotes around each parameter. When this
+output is once again fed to the shell (usually by a shell
+.B eval
+command), it is split correctly into separate parameters.
+.PP
+Quoting is not enabled if the environment variable
+.B GETOPT_COMPATIBLE
+is set, if the first form of the
+.B SYNOPSIS
+is used, or if the option
+.RB ' \-u '
+is found.
+.PP
+Different shells use different quoting conventions. You can use the
+.RB ' \-s '
+option to select the shell you are using. The following shells are
+currently supported:
+.RB ' sh ',
+.RB ' bash ',
+.RB ' csh '
+and
+.RB ' tcsh '.
+Actually, only two 'flavors' are distinguished: sh\-like quoting
+conventions and csh\-like quoting conventions. Chances are that if
+you use another shell script language, one of these flavors can still
+be used.
+.SH "SCANNING MODES"
+The first character of the short options string may be a
+.RB ' \- '
+or a
+.RB ' + '
+to indicate a special scanning mode. If the first calling form in
+the
+.B SYNOPSIS
+is used they are ignored; the environment variable
+.B POSIXLY_CORRECT
+is still examined, though.
+.PP
+If the first character is
+.RB ' + ',
+or if the environment variable
+.B POSIXLY_CORRECT
+is set, parsing stops as soon as the first non\-option parameter (ie.
+a parameter that does not start with a
+.RB ' \- ')
+is found that is not an option argument. The remaining parameters
+are all interpreted as non\-option parameters.
+.PP
+If the first character is a
+.RB ' \- ',
+non\-option parameters are outputted at the place where they are
+found; in normal operation, they are all collected at the end of
+output after a
+.RB ' \-\- '
+parameter has been generated. Note that this
+.RB ' \-\- '
+parameter is still generated, but it will always be the last
+parameter in this mode.
+.SH COMPATIBILITY
+This version of
+.BR getopt (1)
+is written to be as compatible as possible to other versions.
+Usually you can just replace them with this version without any
+modifications, and with some advantages.
+.PP
+If the first character of the first parameter of getopt is not a
+.RB ' \- ',
+getopt goes into compatibility mode. It will interpret its first
+parameter as the string of short options, and all other arguments
+will be parsed. It will still do parameter shuffling (ie. all
+non\-option parameters are outputted at the end), unless the
+environment variable
+.B POSIXLY_CORRECT
+is set.
+.PP
+The environment variable
+.B GETOPT_COMPATIBLE
+forces
+.B getopt
+into compatibility mode. Setting both this environment variable and
+.B POSIXLY_CORRECT
+offers 100% compatibility for 'difficult' programs. Usually, though,
+neither is needed.
+.PP
+In compatibility mode, leading
+.RB ' \- '
+and
+.RB ' + '
+characters in the short options string are ignored.
+.SH RETURN CODES
+.B getopt
+returns error code
+.B 0
+for successful parsing,
+.B 1
+if
+.BR getopt (3)
+returns errors,
+.B 2
+if it does not understand its own parameters,
+.B 3
+if an internal error occurs like out\-of\-memory, and
+.B 4
+if it is called with
+.BR \-T .
+.SH EXAMPLES
+Example scripts for (ba)sh and (t)csh are provided with the
+.BR getopt (1)
+distribution, and are optionally installed in
+.BR /usr/share/getopt/ .
+.SH ENVIRONMENT
+.IP POSIXLY_CORRECT
+This environment variable is examined by the
+.BR getopt (3)
+routines. If it is set, parsing stops as soon as a parameter is
+found that is not an option or an option argument. All remaining
+parameters are also interpreted as non\-option parameters, regardless
+whether they start with a
+.RB ' \- '.
+.IP GETOPT_COMPATIBLE
+Forces
+.B getopt
+to use the first calling format as specified in the
+.BR SYNOPSIS .
+.SH BUGS
+.BR getopt (3)
+can parse long options with optional arguments that are given an
+empty optional argument (but can not do this for short options).
+This
+.BR getopt (1)
+treats optional arguments that are empty as if they were not present.
+.PP
+The syntax if you do not want any short option variables at all is
+not very intuitive (you have to set them explicitly to the empty
+string).
+.SH AUTHOR
+.MT frodo@frodo.looijaard.name
+Frodo Looijaard
+.ME
+.SH "SEE ALSO"
+.BR getopt (3),
+.BR bash (1),
+.BR tcsh (1).
+.SH AVAILABILITY
+The getopt command is part of the util-linux package and is available from
+.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
diff --git a/misc-utils/getopt.c b/misc-utils/getopt.c
new file mode 100644
index 0000000..0efe5d8
--- /dev/null
+++ b/misc-utils/getopt.c
@@ -0,0 +1,458 @@
+/*
+ * getopt.c - Enhanced implementation of BSD getopt(1)
+ * Copyright (c) 1997-2005 Frodo Looijaard <frodo@frodo.looijaard.name>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * Version 1.0-b4: Tue Sep 23 1997. First public release.
+ * Version 1.0: Wed Nov 19 1997.
+ * Bumped up the version number to 1.0
+ * Fixed minor typo (CSH instead of TCSH)
+ * Version 1.0.1: Tue Jun 3 1998
+ * Fixed sizeof instead of strlen bug
+ * Bumped up the version number to 1.0.1
+ * Version 1.0.2: Thu Jun 11 1998 (not present)
+ * Fixed gcc-2.8.1 warnings
+ * Fixed --version/-V option (not present)
+ * Version 1.0.5: Tue Jun 22 1999
+ * Make -u option work (not present)
+ * Version 1.0.6: Tue Jun 27 2000
+ * No important changes
+ * Version 1.1.0: Tue Jun 30 2000
+ * Added NLS support (partly written by Arkadiusz Mi<B6>kiewicz
+ * <misiek@pld.org.pl>)
+ * Version 1.1.4: Mon Nov 7 2005
+ * Fixed a few type's in the manpage
+ */
+
+/* Exit codes:
+ * 0) No errors, successful operation.
+ * 1) getopt(3) returned an error.
+ * 2) A problem with parameter parsing for getopt(1).
+ * 3) Internal error, out of memory
+ * 4) Returned for -T
+ */
+#define GETOPT_EXIT_CODE 1
+#define PARAMETER_EXIT_CODE 2
+#define XALLOC_EXIT_CODE 3
+#define TEST_EXIT_CODE 4
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <getopt.h>
+
+#include "closestream.h"
+#include "nls.h"
+#include "xalloc.h"
+
+/* NON_OPT is the code that is returned when a non-option is found in '+'
+ * mode */
+#define NON_OPT 1
+/* LONG_OPT is the code that is returned when a long option is found. */
+#define LONG_OPT 2
+
+/* The shells recognized. */
+typedef enum { BASH, TCSH } shell_t;
+
+
+/* Some global variables that tells us how to parse. */
+static shell_t shell = BASH; /* The shell we generate output for. */
+static int quiet_errors = 0; /* 0 is not quiet. */
+static int quiet_output = 0; /* 0 is not quiet. */
+static int quote = 1; /* 1 is do quote. */
+
+/* Allow changing which getopt is in use with function pointer */
+int (*getopt_long_fp) (int argc, char *const *argv, const char *optstr,
+ const struct option * longopts, int *longindex);
+
+/* Function prototypes */
+static const char *normalize(const char *arg);
+static int generate_output(char *argv[], int argc, const char *optstr,
+ const struct option *longopts);
+static void parse_error(const char *message);
+static void add_long_options(char *options);
+static void add_longopt(const char *name, int has_arg);
+static void print_help(void);
+static void set_shell(const char *new_shell);
+
+/*
+ * This function 'normalizes' a single argument: it puts single quotes
+ * around it and escapes other special characters. If quote is false, it
+ * just returns its argument.
+ *
+ * Bash only needs special treatment for single quotes; tcsh also recognizes
+ * exclamation marks within single quotes, and nukes whitespace. This
+ * function returns a pointer to a buffer that is overwritten by each call.
+ */
+static const char *normalize(const char *arg)
+{
+ static char *BUFFER = NULL;
+ const char *argptr = arg;
+ char *bufptr;
+
+ if (!quote) {
+ /* Just copy arg */
+ BUFFER = xmalloc(strlen(arg) + 1);
+ strcpy(BUFFER, arg);
+ return BUFFER;
+ }
+
+ /*
+ * Each character in arg may take up to four characters in the
+ * result: For a quote we need a closing quote, a backslash, a quote
+ * and an opening quote! We need also the global opening and closing
+ * quote, and one extra character for '\0'.
+ */
+ BUFFER = xmalloc(strlen(arg) * 4 + 3);
+
+ bufptr = BUFFER;
+ *bufptr++ = '\'';
+
+ while (*argptr) {
+ if (*argptr == '\'') {
+ /* Quote: replace it with: '\'' */
+ *bufptr++ = '\'';
+ *bufptr++ = '\\';
+ *bufptr++ = '\'';
+ *bufptr++ = '\'';
+ } else if (shell == TCSH && *argptr == '!') {
+ /* Exclamation mark: replace it with: \! */
+ *bufptr++ = '\'';
+ *bufptr++ = '\\';
+ *bufptr++ = '!';
+ *bufptr++ = '\'';
+ } else if (shell == TCSH && *argptr == '\n') {
+ /* Newline: replace it with: \n */
+ *bufptr++ = '\\';
+ *bufptr++ = 'n';
+ } else if (shell == TCSH && isspace(*argptr)) {
+ /* Non-newline whitespace: replace it with \<ws> */
+ *bufptr++ = '\'';
+ *bufptr++ = '\\';
+ *bufptr++ = *argptr;
+ *bufptr++ = '\'';
+ } else
+ /* Just copy */
+ *bufptr++ = *argptr;
+ argptr++;
+ }
+ *bufptr++ = '\'';
+ *bufptr++ = '\0';
+ return BUFFER;
+}
+
+/*
+ * Generate the output. argv[0] is the program name (used for reporting errors).
+ * argv[1..] contains the options to be parsed. argc must be the number of
+ * elements in argv (ie. 1 if there are no options, only the program name),
+ * optstr must contain the short options, and longopts the long options.
+ * Other settings are found in global variables.
+ */
+static int generate_output(char *argv[], int argc, const char *optstr,
+ const struct option *longopts)
+{
+ int exit_code = EXIT_SUCCESS; /* Assume everything will be OK */
+ int opt;
+ int longindex;
+ const char *charptr;
+
+ if (quiet_errors)
+ /* No error reporting from getopt(3) */
+ opterr = 0;
+ /* Reset getopt(3) */
+ optind = 0;
+
+ while ((opt =
+ (getopt_long_fp(argc, argv, optstr, longopts, &longindex)))
+ != EOF)
+ if (opt == '?' || opt == ':')
+ exit_code = GETOPT_EXIT_CODE;
+ else if (!quiet_output) {
+ if (opt == LONG_OPT) {
+ printf(" --%s", longopts[longindex].name);
+ if (longopts[longindex].has_arg)
+ printf(" %s", normalize(optarg ? optarg : ""));
+ } else if (opt == NON_OPT)
+ printf(" %s", normalize(optarg));
+ else {
+ printf(" -%c", opt);
+ charptr = strchr(optstr, opt);
+ if (charptr != NULL && *++charptr == ':')
+ printf(" %s", normalize(optarg ? optarg : ""));
+ }
+ }
+
+ if (!quiet_output) {
+ printf(" --");
+ while (optind < argc)
+ printf(" %s", normalize(argv[optind++]));
+ printf("\n");
+ }
+ return exit_code;
+}
+
+/*
+ * Report an error when parsing getopt's own arguments. If message is NULL,
+ * we already sent a message, we just exit with a helpful hint.
+ */
+static void __attribute__ ((__noreturn__)) parse_error(const char *message)
+{
+ if (message)
+ warnx("%s", message);
+ fprintf(stderr, _("Try `%s --help' for more information.\n"),
+ program_invocation_short_name);
+ exit(PARAMETER_EXIT_CODE);
+}
+
+static struct option *long_options = NULL;
+static int long_options_length = 0; /* Length of array */
+static int long_options_nr = 0; /* Nr of used elements in array */
+#define LONG_OPTIONS_INCR 10
+#define init_longopt() add_longopt(NULL,0)
+
+/* Register a long option. The contents of name is copied. */
+static void add_longopt(const char *name, int has_arg)
+{
+ char *tmp;
+ if (!name) {
+ /* init */
+ free(long_options);
+ long_options = NULL;
+ long_options_length = 0;
+ long_options_nr = 0;
+ }
+
+ if (long_options_nr == long_options_length) {
+ long_options_length += LONG_OPTIONS_INCR;
+ long_options = xrealloc(long_options,
+ sizeof(struct option) *
+ long_options_length);
+ }
+
+ long_options[long_options_nr].name = NULL;
+ long_options[long_options_nr].has_arg = 0;
+ long_options[long_options_nr].flag = NULL;
+ long_options[long_options_nr].val = 0;
+
+ if (long_options_nr) {
+ /* Not for init! */
+ long_options[long_options_nr - 1].has_arg = has_arg;
+ long_options[long_options_nr - 1].flag = NULL;
+ long_options[long_options_nr - 1].val = LONG_OPT;
+ tmp = xmalloc(strlen(name) + 1);
+ strcpy(tmp, name);
+ long_options[long_options_nr - 1].name = tmp;
+ }
+ long_options_nr++;
+}
+
+
+/*
+ * Register several long options. options is a string of long options,
+ * separated by commas or whitespace. This nukes options!
+ */
+static void add_long_options(char *options)
+{
+ int arg_opt;
+ char *tokptr = strtok(options, ", \t\n");
+ while (tokptr) {
+ arg_opt = no_argument;
+ if (strlen(tokptr) > 0) {
+ if (tokptr[strlen(tokptr) - 1] == ':') {
+ if (tokptr[strlen(tokptr) - 2] == ':') {
+ tokptr[strlen(tokptr) - 2] = '\0';
+ arg_opt = optional_argument;
+ } else {
+ tokptr[strlen(tokptr) - 1] = '\0';
+ arg_opt = required_argument;
+ }
+ if (strlen(tokptr) == 0)
+ parse_error(_
+ ("empty long option after "
+ "-l or --long argument"));
+ }
+ add_longopt(tokptr, arg_opt);
+ }
+ tokptr = strtok(NULL, ", \t\n");
+ }
+}
+
+static void set_shell(const char *new_shell)
+{
+ if (!strcmp(new_shell, "bash"))
+ shell = BASH;
+ else if (!strcmp(new_shell, "tcsh"))
+ shell = TCSH;
+ else if (!strcmp(new_shell, "sh"))
+ shell = BASH;
+ else if (!strcmp(new_shell, "csh"))
+ shell = TCSH;
+ else
+ parse_error(_
+ ("unknown shell after -s or --shell argument"));
+}
+
+static void __attribute__ ((__noreturn__)) print_help(void)
+{
+ fputs(_("\nUsage:\n"), stderr);
+
+ fprintf(stderr, _(
+ " %1$s optstring parameters\n"
+ " %1$s [options] [--] optstring parameters\n"
+ " %1$s [options] -o|--options optstring [options] [--] parameters\n"),
+ program_invocation_short_name);
+
+ fputs(_("\nOptions:\n"), stderr);
+ fputs(_(" -a, --alternative Allow long options starting with single -\n"), stderr);
+ fputs(_(" -h, --help This small usage guide\n"), stderr);
+ fputs(_(" -l, --longoptions <longopts> Long options to be recognized\n"), stderr);
+ fputs(_(" -n, --name <progname> The name under which errors are reported\n"), stderr);
+ fputs(_(" -o, --options <optstring> Short options to be recognized\n"), stderr);
+ fputs(_(" -q, --quiet Disable error reporting by getopt(3)\n"), stderr);
+ fputs(_(" -Q, --quiet-output No normal output\n"), stderr);
+ fputs(_(" -s, --shell <shell> Set shell quoting conventions\n"), stderr);
+ fputs(_(" -T, --test Test for getopt(1) version\n"), stderr);
+ fputs(_(" -u, --unquote Do not quote the output\n"), stderr);
+ fputs(_(" -V, --version Output version information\n"), stderr);
+ fputc('\n', stderr);
+
+ exit(PARAMETER_EXIT_CODE);
+}
+
+int main(int argc, char *argv[])
+{
+ char *optstr = NULL;
+ char *name = NULL;
+ int opt;
+ int compatible = 0;
+
+ /* Stop scanning as soon as a non-option argument is found! */
+ static const char *shortopts = "+ao:l:n:qQs:TuhV";
+ static const struct option longopts[] = {
+ {"options", required_argument, NULL, 'o'},
+ {"longoptions", required_argument, NULL, 'l'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"quiet-output", no_argument, NULL, 'Q'},
+ {"shell", required_argument, NULL, 's'},
+ {"test", no_argument, NULL, 'T'},
+ {"unquoted", no_argument, NULL, 'u'},
+ {"help", no_argument, NULL, 'h'},
+ {"alternative", no_argument, NULL, 'a'},
+ {"name", required_argument, NULL, 'n'},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0}
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ init_longopt();
+ getopt_long_fp = getopt_long;
+
+ if (getenv("GETOPT_COMPATIBLE"))
+ compatible = 1;
+
+ if (argc == 1) {
+ if (compatible) {
+ /*
+ * For some reason, the original getopt gave no
+ * error when there were no arguments.
+ */
+ printf(" --\n");
+ return EXIT_SUCCESS;
+ } else
+ parse_error(_("missing optstring argument"));
+ }
+
+ if (argv[1][0] != '-' || compatible) {
+ quote = 0;
+ optstr = xmalloc(strlen(argv[1]) + 1);
+ strcpy(optstr, argv[1] + strspn(argv[1], "-+"));
+ argv[1] = argv[0];
+ return generate_output(argv + 1, argc - 1, optstr,
+ long_options);
+ }
+
+ while ((opt =
+ getopt_long(argc, argv, shortopts, longopts, NULL)) != EOF)
+ switch (opt) {
+ case 'a':
+ getopt_long_fp = getopt_long_only;
+ break;
+ case 'h':
+ print_help();
+ case 'o':
+ free(optstr);
+ optstr = xmalloc(strlen(optarg) + 1);
+ strcpy(optstr, optarg);
+ break;
+ case 'l':
+ add_long_options(optarg);
+ break;
+ case 'n':
+ free(name);
+ name = xmalloc(strlen(optarg) + 1);
+ strcpy(name, optarg);
+ break;
+ case 'q':
+ quiet_errors = 1;
+ break;
+ case 'Q':
+ quiet_output = 1;
+ break;
+ case 's':
+ set_shell(optarg);
+ break;
+ case 'T':
+ return TEST_EXIT_CODE;
+ case 'u':
+ quote = 0;
+ break;
+ case 'V':
+ printf(_("%s from %s\n"),
+ program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ case '?':
+ case ':':
+ parse_error(NULL);
+ default:
+ parse_error(_("internal error, contact the author."));
+ }
+
+ if (!optstr) {
+ if (optind >= argc)
+ parse_error(_("missing optstring argument"));
+ else {
+ optstr = xmalloc(strlen(argv[optind]) + 1);
+ strcpy(optstr, argv[optind]);
+ optind++;
+ }
+ }
+ if (name)
+ argv[optind - 1] = name;
+ else
+ argv[optind - 1] = argv[0];
+
+ return generate_output(argv + optind - 1, argc-optind + 1,
+ optstr, long_options);
+}
diff --git a/misc-utils/kill.1 b/misc-utils/kill.1
new file mode 100644
index 0000000..09d30e9
--- /dev/null
+++ b/misc-utils/kill.1
@@ -0,0 +1,111 @@
+.\" Copyright 1994 Salvatore Valente (svalente@mit.edu)
+.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH KILL 1 "February 2011" "util-linux" "User Commands"
+.SH NAME
+kill \- terminate a process
+.SH SYNOPSIS
+.B kill
+.RB [ \-s
+.IR signal | \fB\-p\fP ]
+.RB [ \-q
+.IR sigval ]
+.RB [ \-a ]
+.RB [ \-\- ]
+.IR pid ...
+.br
+.B kill -l
+.RI [ signal ]
+.SH DESCRIPTION
+The command
+.B kill
+sends the specified signal to the specified process or process group.
+If no signal is specified, the TERM signal is sent. The TERM signal
+will kill processes which do not catch this signal. For other processes,
+it may be necessary to use the KILL (9) signal, since this signal cannot
+be caught.
+.PP
+Most modern shells have a builtin kill function, with a usage rather similar
+to that of the command described here. The `-a' and `-p' options,
+and the possibility to specify processes by command name are a local extension.
+.PP
+If sig is 0, then no signal is sent, but error checking is still performed.
+.SH OPTIONS
+.TP
+.IR pid ...
+Specify the list of processes that
+.B kill
+should signal. Each
+.I pid
+can be one of five things:
+
+.RS
+.TP
+.I n
+where
+.I n
+is larger than 0. The process with pid
+.I n
+will be signaled.
+.TP
+.B 0
+All processes in the current process group are signaled.
+.TP
+.B -1
+All processes with pid larger than 1 will be signaled.
+.TP
+.BI - n
+where
+.I n
+is larger than 1.
+All processes in process group
+.I n
+are signaled. When an argument of the form `-n' is given,
+and it is meant to denote a process group,
+either the signal must be specified first, or the argument must be preceded
+by a `--' option, otherwise it will be taken as the signal to send.
+.TP
+.I commandname
+All processes invoked using that name will be signaled.
+.RE
+.TP
+.BI \-s " signal"
+Specify the signal to send.
+The signal may be given as a signal name or number.
+.TP
+.B \-l
+Print a list of signal names. These are found in
+.I /usr/include/linux/signal.h
+.TP
+.B \-a
+Do not restrict the commandname-to-pid conversion to processes
+with the same uid as the present process.
+.TP
+.B \-p
+Specify that
+.B kill
+should only print the process id (pid)
+of the named processes, and not send any signals.
+.TP
+.BI \-q " sigval"
+Use
+.BR sigqueue (2)
+rather than
+.BR kill (2)
+and the sigval argument is used to specify an integer to be sent with the
+signal. If the receiving process has installed a handler for this signal using
+the SA_SIGINFO flag to
+.BR sigaction (2),
+then it can obtain this data via the si_value field of the siginfo_t structure.
+.SH "SEE ALSO"
+.BR bash (1),
+.BR tcsh (1),
+.BR kill (2),
+.BR sigvec (2),
+.BR signal (7)
+.SH AUTHOR
+Taken from BSD 4.4. The ability to translate process names to process
+ids was added by Salvatore Valente <svalente@mit.edu>.
+.SH AVAILABILITY
+The kill command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/misc-utils/kill.c b/misc-utils/kill.c
new file mode 100644
index 0000000..6abfd24
--- /dev/null
+++ b/misc-utils/kill.c
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * oct 5 1994 -- almost entirely re-written to allow for process names.
+ * modifications (c) salvatore valente <svalente@mit.edu>
+ * may be used / modified / distributed under the same terms as the original.
+ *
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ *
+ * 1999-11-13 aeb Accept signal numers 128+s.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h> /* for isdigit() */
+#include <unistd.h>
+#include <signal.h>
+
+#include "c.h"
+#include "kill.h"
+#include "nls.h"
+#include "closestream.h"
+#include "strutils.h"
+
+struct signv {
+ char *name;
+ int val;
+} sys_signame[] = {
+ /* POSIX signals */
+ { "HUP", SIGHUP }, /* 1 */
+ { "INT", SIGINT }, /* 2 */
+ { "QUIT", SIGQUIT }, /* 3 */
+ { "ILL", SIGILL }, /* 4 */
+ { "ABRT", SIGABRT }, /* 6 */
+ { "FPE", SIGFPE }, /* 8 */
+ { "KILL", SIGKILL }, /* 9 */
+ { "SEGV", SIGSEGV }, /* 11 */
+ { "PIPE", SIGPIPE }, /* 13 */
+ { "ALRM", SIGALRM }, /* 14 */
+ { "TERM", SIGTERM }, /* 15 */
+ { "USR1", SIGUSR1 }, /* 10 (arm,i386,m68k,ppc), 30 (alpha,sparc*), 16 (mips) */
+ { "USR2", SIGUSR2 }, /* 12 (arm,i386,m68k,ppc), 31 (alpha,sparc*), 17 (mips) */
+ { "CHLD", SIGCHLD }, /* 17 (arm,i386,m68k,ppc), 20 (alpha,sparc*), 18 (mips) */
+ { "CONT", SIGCONT }, /* 18 (arm,i386,m68k,ppc), 19 (alpha,sparc*), 25 (mips) */
+ { "STOP", SIGSTOP }, /* 19 (arm,i386,m68k,ppc), 17 (alpha,sparc*), 23 (mips) */
+ { "TSTP", SIGTSTP }, /* 20 (arm,i386,m68k,ppc), 18 (alpha,sparc*), 24 (mips) */
+ { "TTIN", SIGTTIN }, /* 21 (arm,i386,m68k,ppc,alpha,sparc*), 26 (mips) */
+ { "TTOU", SIGTTOU }, /* 22 (arm,i386,m68k,ppc,alpha,sparc*), 27 (mips) */
+ /* Miscellaneous other signals */
+#ifdef SIGTRAP
+ { "TRAP", SIGTRAP }, /* 5 */
+#endif
+#ifdef SIGIOT
+ { "IOT", SIGIOT }, /* 6, same as SIGABRT */
+#endif
+#ifdef SIGEMT
+ { "EMT", SIGEMT }, /* 7 (mips,alpha,sparc*) */
+#endif
+#ifdef SIGBUS
+ { "BUS", SIGBUS }, /* 7 (arm,i386,m68k,ppc), 10 (mips,alpha,sparc*) */
+#endif
+#ifdef SIGSYS
+ { "SYS", SIGSYS }, /* 12 (mips,alpha,sparc*) */
+#endif
+#ifdef SIGSTKFLT
+ { "STKFLT", SIGSTKFLT }, /* 16 (arm,i386,m68k,ppc) */
+#endif
+#ifdef SIGURG
+ { "URG", SIGURG }, /* 23 (arm,i386,m68k,ppc), 16 (alpha,sparc*), 21 (mips) */
+#endif
+#ifdef SIGIO
+ { "IO", SIGIO }, /* 29 (arm,i386,m68k,ppc), 23 (alpha,sparc*), 22 (mips) */
+#endif
+#ifdef SIGPOLL
+ { "POLL", SIGPOLL }, /* same as SIGIO */
+#endif
+#ifdef SIGCLD
+ { "CLD", SIGCLD }, /* same as SIGCHLD (mips) */
+#endif
+#ifdef SIGXCPU
+ { "XCPU", SIGXCPU }, /* 24 (arm,i386,m68k,ppc,alpha,sparc*), 30 (mips) */
+#endif
+#ifdef SIGXFSZ
+ { "XFSZ", SIGXFSZ }, /* 25 (arm,i386,m68k,ppc,alpha,sparc*), 31 (mips) */
+#endif
+#ifdef SIGVTALRM
+ { "VTALRM", SIGVTALRM }, /* 26 (arm,i386,m68k,ppc,alpha,sparc*), 28 (mips) */
+#endif
+#ifdef SIGPROF
+ { "PROF", SIGPROF }, /* 27 (arm,i386,m68k,ppc,alpha,sparc*), 29 (mips) */
+#endif
+#ifdef SIGPWR
+ { "PWR", SIGPWR }, /* 30 (arm,i386,m68k,ppc), 29 (alpha,sparc*), 19 (mips) */
+#endif
+#ifdef SIGINFO
+ { "INFO", SIGINFO }, /* 29 (alpha) */
+#endif
+#ifdef SIGLOST
+ { "LOST", SIGLOST }, /* 29 (arm,i386,m68k,ppc,sparc*) */
+#endif
+#ifdef SIGWINCH
+ { "WINCH", SIGWINCH }, /* 28 (arm,i386,m68k,ppc,alpha,sparc*), 20 (mips) */
+#endif
+#ifdef SIGUNUSED
+ { "UNUSED", SIGUNUSED }, /* 31 (arm,i386,m68k,ppc) */
+#endif
+};
+
+extern char *mybasename(char *);
+
+static int arg_to_signum (char *arg, int mask);
+static void nosig (char *name);
+static void printsig (int sig);
+static void printsignals (FILE *fp);
+static int usage (int status);
+static int kill_verbose (char *procname, int pid, int sig);
+
+static char *progname;
+
+#ifdef HAVE_SIGQUEUE
+static int use_sigval;
+static union sigval sigdata;
+#endif
+
+int main (int argc, char *argv[])
+{
+ int errors, numsig, pid;
+ char *ep, *arg, *p;
+ int do_pid, do_kill, check_all;
+ int *pids, *ip;
+
+ progname = argv[0];
+ if ((p = strrchr(progname, '/')) != NULL)
+ progname = p+1;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ numsig = SIGTERM;
+ do_pid = (! strcmp (progname, "pid")); /* Yecch */
+ do_kill = 0;
+ check_all = 0;
+
+ /* loop through the arguments.
+ actually, -a is the only option can be used with other options.
+ `kill' is basically a one-option-at-most program. */
+ for (argc--, argv++; argc > 0; argc--, argv++) {
+ arg = *argv;
+ if (*arg != '-') {
+ break;
+ }
+ if (! strcmp (arg, "--")) {
+ argc--, argv++;
+ break;
+ }
+ if (! strcmp (arg, "-v") || ! strcmp (arg, "-V") ||
+ ! strcmp (arg, "--version")) {
+ printf(_("%s from %s\n"), progname, PACKAGE_STRING);
+ return 0;
+ }
+ if (! strcmp (arg, "-a")) {
+ check_all++;
+ continue;
+ }
+ if (! strcmp (arg, "-l")) {
+ if (argc < 2) {
+ printsignals (stdout);
+ return 0;
+ }
+ if (argc > 2) {
+ return usage (1);
+ }
+ /* argc == 2, accept "kill -l $?" */
+ arg = argv[1];
+ if ((numsig = arg_to_signum (arg, 1)) < 0) {
+ fprintf (stderr, _("%s: unknown signal %s\n"), progname, arg);
+ return 1;
+ }
+ printsig (numsig);
+ return 0;
+ }
+ if (! strcmp (arg, "-p")) {
+ do_pid++;
+ if (do_kill)
+ return usage (1);
+ continue;
+ }
+ if (! strcmp (arg, "-s")) {
+ if (argc < 2) {
+ return usage (1);
+ }
+ do_kill++;
+ if (do_pid)
+ return usage (1);
+ argc--, argv++;
+ arg = *argv;
+ if ((numsig = arg_to_signum (arg, 0)) < 0) {
+ nosig (arg);
+ return 1;
+ }
+ continue;
+ }
+ if (! strcmp (arg, "-q")) {
+ if (argc < 2)
+ return usage (1);
+ argc--, argv++;
+ arg = *argv;
+#ifdef HAVE_SIGQUEUE
+ sigdata.sival_int = strtos32_or_err(arg, _("invalid sigval argument"));
+ use_sigval = 1;
+#endif
+ continue;
+ }
+ /* `arg' begins with a dash but is not a known option.
+ so it's probably something like -HUP, or -1/-n
+ try to deal with it.
+ -n could be signal n, or pid -n (i.e. process group n).
+ In case of doubt POSIX tells us to assume a signal.
+ If a signal has been parsed, assume it's a pid, break */
+ if (do_kill)
+ break;
+ arg++;
+ if ((numsig = arg_to_signum (arg, 0)) < 0) {
+ return usage (1);
+ }
+ do_kill++;
+ if (do_pid)
+ return usage (1);
+ continue;
+ }
+
+ if (! *argv) {
+ return usage (1);
+ }
+ if (do_pid) {
+ numsig = -1;
+ }
+
+ /* we're done with the options.
+ the rest of the arguments should be process ids and names.
+ kill them. */
+ for (errors = 0; (arg = *argv) != NULL; argv++) {
+ pid = strtol (arg, &ep, 10);
+ if (! *ep)
+ errors += kill_verbose (arg, pid, numsig);
+ else {
+ pids = get_pids (arg, check_all);
+ if (! pids) {
+ errors++;
+ fprintf (stderr, _("%s: can't find process \"%s\"\n"),
+ progname, arg);
+ continue;
+ }
+ for (ip = pids; *ip >= 0; ip++)
+ errors += kill_verbose (arg, *ip, numsig);
+ free (pids);
+ }
+ }
+ return (errors);
+}
+
+#ifdef SIGRTMIN
+static int rtsig_to_signum(char *sig)
+{
+ int num, maxi = 0;
+ char *ep = NULL;
+
+ if (strncasecmp(sig, "min+", 4) == 0)
+ sig += 4;
+ else if (strncasecmp(sig, "max-", 4) == 0) {
+ sig += 4;
+ maxi = 1;
+ }
+
+ if (!isdigit(*sig))
+ return -1;
+
+ errno = 0;
+ num = strtol(sig, &ep, 10);
+ if (!ep || sig == ep || errno || num < 0)
+ return -1;
+
+ num = maxi ? SIGRTMAX - num : SIGRTMIN + num;
+
+ if (num < SIGRTMIN || num > SIGRTMAX)
+ return -1;
+
+ return num;
+}
+#endif
+
+static int signame_to_signum (char *sig)
+{
+ size_t n;
+
+ if (! strncasecmp (sig, "sig", 3))
+ sig += 3;
+
+#ifdef SIGRTMIN
+ /* RT signals */
+ if (!strncasecmp(sig, "rt", 2))
+ return rtsig_to_signum(sig + 2);
+#endif
+ /* Normal sugnals */
+ for (n = 0; n < ARRAY_SIZE(sys_signame); n++) {
+ if (! strcasecmp (sys_signame[n].name, sig))
+ return sys_signame[n].val;
+ }
+ return (-1);
+}
+
+static int arg_to_signum (char *arg, int maskbit)
+{
+ int numsig;
+ char *ep;
+
+ if (isdigit (*arg)) {
+ numsig = strtol (arg, &ep, 10);
+ if (numsig >= NSIG && maskbit && (numsig & 128) != 0)
+ numsig -= 128;
+ if (*ep != 0 || numsig < 0 || numsig >= NSIG)
+ return (-1);
+ return (numsig);
+ }
+ return signame_to_signum (arg);
+}
+
+static void nosig (char *name)
+{
+ fprintf (stderr, _("%s: unknown signal %s; valid signals:\n"), progname, name);
+ printsignals (stderr);
+}
+
+static void printsig (int sig)
+{
+ size_t n;
+
+ for (n = 0; n < ARRAY_SIZE(sys_signame); n++) {
+ if (sys_signame[n].val == sig) {
+ printf ("%s\n", sys_signame[n].name);
+ return;
+ }
+ }
+#ifdef SIGRTMIN
+ if (sig >= SIGRTMIN && sig <= SIGRTMAX) {
+ printf ("RT%d\n", sig - SIGRTMIN);
+ return;
+ }
+#endif
+ printf("%d\n", sig);
+}
+
+static void printsignals (FILE *fp)
+{
+ size_t n, lth, lpos = 0;
+
+ for (n = 0; n < ARRAY_SIZE(sys_signame); n++) {
+ lth = 1+strlen(sys_signame[n].name);
+ if (lpos+lth > 72) {
+ fputc ('\n', fp);
+ lpos = 0;
+ } else if (lpos)
+ fputc (' ', fp);
+ lpos += lth;
+ fputs (sys_signame[n].name, fp);
+ }
+#ifdef SIGRTMIN
+ fputs (" RT<N> RTMIN+<N> RTMAX-<N>", fp);
+#endif
+ fputc ('\n', fp);
+}
+
+static int usage (int status)
+{
+ FILE *fp;
+
+ fp = (status == 0 ? stdout : stderr);
+ fprintf (fp, _("usage: %s [ -s signal | -p ] [ -a ] pid ...\n"), progname);
+ fprintf (fp, _(" %s -l [ signal ]\n"), progname);
+ return status;
+}
+
+static int kill_verbose (char *procname, int pid, int sig)
+{
+ int rc;
+
+ if (sig < 0) {
+ printf ("%d\n", pid);
+ return 0;
+ }
+#ifdef HAVE_SIGQUEUE
+ if (use_sigval)
+ rc = sigqueue(pid, sig, sigdata);
+ else
+#endif
+ rc = kill (pid, sig);
+
+ if (rc < 0) {
+ fprintf (stderr, "%s ", progname);
+ perror (procname);
+ return 1;
+ }
+ return 0;
+}
diff --git a/misc-utils/kill.h b/misc-utils/kill.h
new file mode 100644
index 0000000..27a12a8
--- /dev/null
+++ b/misc-utils/kill.h
@@ -0,0 +1 @@
+extern int *get_pids (char *process_name, int get_all);
diff --git a/misc-utils/logger.1 b/misc-utils/logger.1
new file mode 100644
index 0000000..d863ac3
--- /dev/null
+++ b/misc-utils/logger.1
@@ -0,0 +1,155 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)logger.1 8.1 (Berkeley) 6/6/93
+.\"
+.\" Section on valid facility and level strings added by
+.\" and1000@debian.org, 26 Oct 1997.
+.TH LOGGER "1" "September 2011" "util-linux" "User Commands"
+.SH NAME
+logger \- a shell command interface to the syslog(3) system log module
+.SH SYNOPSIS
+.B logger
+[options] [message]
+.SH DESCRIPTION
+.B logger
+makes entries in the system log. It provides a shell command
+interface to the
+.BR syslog (3)
+system log module.
+.SH OPTIONS
+.TP
+\fB\-d\fR, \fB\-\-udp\fR
+Use datagram (UDP) instead of the default stream connection (TCP).
+.TP
+\fB\-i\fR, \fB\-\-id\fR
+Log the process ID of the logger process with each line.
+.TP
+\fB\-f\fR, \fB\-\-file\fR \fIfile\fR
+Log the contents of the specified
+.IR file .
+This option cannot be combined with a command-line message.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display a help text and exit.
+.TP
+\fB\-n\fR, \fB\-\-server\fR \fIserver\fR
+Write to the specified remote syslog
+.I server
+using UDP instead of to the builtin syslog routines.
+.TP
+\fB\-P\fR, \fB\-\-port\fR \fIport\fR
+Use the specified UDP
+.IR port .
+The default port number is 514.
+.TP
+\fB\-p\fR, \fB\-\-priority\fR \fIpriority\fR
+Enter the message into the log with the specified
+.IR priority .
+The priority may be specified numerically or as a
+.I facility.level
+pair.
+For example,
+.B -p
+.I local3.info
+logs the message as informational in the local3 facility.
+The default is
+.IR user.notice .
+.TP
+\fB\-s\fR, \fB\-\-stderr\fR
+Output the message to standard error as well as to the system log.
+.TP
+\fB\-t\fR, \fB\-\-tag\fR \fItag\fR
+Mark every line to be logged with the specified
+.IR tag .
+.TP
+\fB\-u\fR, \fB\-\-socket\fR \fIsocket\fR
+Write to the specified
+.I socket
+instead of to the builtin syslog routines.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Display version information and exit.
+.TP
+\fB\-\-\fR
+End the argument list. This is to allow the
+.I message
+to start with a hyphen (\-).
+.TP
+.I message
+Write the message to log; if not specified, and the
+.I \-f
+flag is not provided, standard input is logged.
+.PP
+The
+.B logger
+utility exits 0 on success, and >0 if an error occurs.
+.PP
+Valid facility names are:
+.IR auth , \ authpriv
+(for security information of a sensitive nature),
+.IR cron , \ daemon , \ ftp , \ kern
+(can't be generated from user process),
+.IR lpr , \ mail , \ news , \ security
+(deprecated synonym for
+.IR auth ), \ syslog , \ user , \ uucp ,
+and
+.IR local0 \ to \ local7 ,
+inclusive.
+.PP
+Valid level names are:
+.IR alert , \ crit , \ debug , \ emerg , \ err , \ error
+(deprecated synonym for
+.IR err ), \ info , \ notice , \ panic
+(deprecated synonym for
+.IR emerg ), \ warning , \ warn
+(deprecated synonym for
+.IR warning ).
+For the priority order and intended purposes of these levels, see
+.BR syslog (3).
+.SH EXAMPLES
+logger System rebooted
+.br
+logger \-p local0.notice \-t HOSTIDM \-f /dev/idmc
+.br
+logger \-n loghost.example.com System rebooted
+.SH SEE ALSO
+.BR syslog (3),
+.BR syslogd (8)
+.SH STANDARDS
+The
+.B logger
+command is expected to be IEEE Std 1003.2 ("POSIX.2") compatible.
+.SH AVAILABILITY
+The logger command is part of the util-linux package and is available from
+.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
diff --git a/misc-utils/logger.c b/misc-utils/logger.c
new file mode 100644
index 0000000..6f9edc2
--- /dev/null
+++ b/misc-utils/logger.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ * Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * - fixed strerr(errno) in gettext calls
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <getopt.h>
+
+#include "c.h"
+#include "closestream.h"
+#include "nls.h"
+#include "strutils.h"
+
+#define SYSLOG_NAMES
+#include <syslog.h>
+
+static int optd = 0;
+static uint16_t udpport = 514;
+
+static int decode(char *name, CODE *codetab)
+{
+ register CODE *c;
+
+ if (isdigit(*name))
+ return (atoi(name));
+
+ for (c = codetab; c->c_name; c++)
+ if (!strcasecmp(name, c->c_name))
+ return (c->c_val);
+
+ return -1;
+}
+
+static int pencode(char *s)
+{
+ char *save;
+ int fac, lev;
+
+ for (save = s; *s && *s != '.'; ++s);
+ if (*s) {
+ *s = '\0';
+ fac = decode(save, facilitynames);
+ if (fac < 0)
+ errx(EXIT_FAILURE, _("unknown facility name: %s."), save);
+ *s++ = '.';
+ }
+ else {
+ fac = LOG_USER;
+ s = save;
+ }
+ lev = decode(s, prioritynames);
+ if (lev < 0)
+ errx(EXIT_FAILURE, _("unknown priority name: %s."), save);
+ return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK));
+}
+
+static int
+myopenlog(const char *sock) {
+ int fd;
+ static struct sockaddr_un s_addr; /* AF_UNIX address of local logger */
+
+ if (strlen(sock) >= sizeof(s_addr.sun_path))
+ errx(EXIT_FAILURE, _("openlog %s: pathname too long"), sock);
+
+ s_addr.sun_family = AF_UNIX;
+ (void)strcpy(s_addr.sun_path, sock);
+
+ if ((fd = socket(AF_UNIX, optd ? SOCK_DGRAM : SOCK_STREAM, 0)) == -1)
+ err(EXIT_FAILURE, _("socket %s"), sock);
+
+ if (connect(fd, (struct sockaddr *) &s_addr, sizeof(s_addr)) == -1)
+ err(EXIT_FAILURE, _("connect %s"), sock);
+
+ return fd;
+}
+
+static int
+udpopenlog(const char *servername, uint16_t port) {
+ int fd;
+ struct sockaddr_in s_addr;
+ struct hostent *serverhost;
+
+ if ((serverhost = gethostbyname(servername)) == NULL )
+ errx(EXIT_FAILURE, _("unable to resolve '%s'"), servername);
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM , 0)) == -1)
+ err(EXIT_FAILURE, _("socket"));
+
+ memcpy(&s_addr.sin_addr, serverhost->h_addr, serverhost->h_length);
+ s_addr.sin_family=AF_INET;
+ s_addr.sin_port=htons(port);
+
+ if (connect(fd, (struct sockaddr *) &s_addr, sizeof(s_addr)) == -1)
+ err(EXIT_FAILURE, _("connect"));
+
+ return fd;
+}
+static void
+mysyslog(int fd, int logflags, int pri, char *tag, char *msg) {
+ char buf[1000], pid[30], *cp, *tp;
+ time_t now;
+
+ if (fd > -1) {
+ if (logflags & LOG_PID)
+ snprintf (pid, sizeof(pid), "[%d]", getpid());
+ else
+ pid[0] = 0;
+ if (tag)
+ cp = tag;
+ else {
+ cp = getlogin();
+ if (!cp)
+ cp = "<someone>";
+ }
+ (void)time(&now);
+ tp = ctime(&now)+4;
+
+ snprintf(buf, sizeof(buf), "<%d>%.15s %.200s%s: %.400s",
+ pri, tp, cp, pid, msg);
+
+ if (write(fd, buf, strlen(buf)+1) < 0)
+ return; /* error */
+ }
+}
+
+static void __attribute__ ((__noreturn__)) usage(FILE *out)
+{
+ fputs(_("\nUsage:\n"), out);
+ fprintf(out,
+ _(" %s [options] [message]\n"), program_invocation_short_name);
+
+ fputs(_("\nOptions:\n"), out);
+ fputs(_(" -d, --udp use UDP (TCP is default)\n"
+ " -i, --id log the process ID too\n"
+ " -f, --file <file> log the contents of this file\n"
+ " -h, --help display this help text and exit\n"), out);
+ fputs(_(" -n, --server <name> write to this remote syslog server\n"
+ " -P, --port <number> use this UDP port\n"
+ " -p, --priority <prio> mark given message with this priority\n"
+ " -s, --stderr output message to standard error as well\n"), out);
+ fputs(_(" -t, --tag <tag> mark every line with this tag\n"
+ " -u, --socket <socket> write to this Unix socket\n"
+ " -V, --version output version information and exit\n\n"), out);
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+/*
+ * logger -- read and log utility
+ *
+ * Reads from an input and arranges to write the result on the system
+ * log.
+ */
+int
+main(int argc, char **argv) {
+ int ch, logflags, pri;
+ char *tag, buf[1024];
+ char *usock = NULL;
+ char *udpserver = NULL;
+ int LogSock = -1;
+
+ static const struct option longopts[] = {
+ { "id", no_argument, 0, 'i' },
+ { "stderr", no_argument, 0, 's' },
+ { "file", required_argument, 0, 'f' },
+ { "priority", required_argument, 0, 'p' },
+ { "tag", required_argument, 0, 't' },
+ { "socket", required_argument, 0, 'u' },
+ { "udp", no_argument, 0, 'd' },
+ { "server", required_argument, 0, 'n' },
+ { "port", required_argument, 0, 'P' },
+ { "version", no_argument, 0, 'V' },
+ { "help", no_argument, 0, 'h' },
+ { NULL, 0, 0, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ tag = NULL;
+ pri = LOG_NOTICE;
+ logflags = 0;
+ while ((ch = getopt_long(argc, argv, "f:ip:st:u:dn:P:Vh",
+ longopts, NULL)) != -1) {
+ switch((char)ch) {
+ case 'f': /* file to log */
+ if (freopen(optarg, "r", stdin) == NULL)
+ err(EXIT_FAILURE, _("file %s"),
+ optarg);
+ break;
+ case 'i': /* log process id also */
+ logflags |= LOG_PID;
+ break;
+ case 'p': /* priority */
+ pri = pencode(optarg);
+ break;
+ case 's': /* log to standard error */
+ logflags |= LOG_PERROR;
+ break;
+ case 't': /* tag */
+ tag = optarg;
+ break;
+ case 'u': /* unix socket */
+ usock = optarg;
+ break;
+ case 'd':
+ optd = 1; /* use datagrams */
+ break;
+ case 'n': /* udp socket */
+ optd = 1; /* use datagrams because udp */
+ udpserver = optarg;
+ break;
+ case 'P': /* change udp port */
+ udpport = strtou16_or_err(optarg,
+ _("invalid port number argument"));
+ break;
+ case 'V':
+ printf(_("%s from %s\n"), program_invocation_short_name,
+ PACKAGE_STRING);
+ exit(EXIT_SUCCESS);
+ case 'h':
+ usage(stdout);
+ case '?':
+ default:
+ usage(stderr);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* setup for logging */
+ if (!usock && !udpserver)
+ openlog(tag ? tag : getlogin(), logflags, 0);
+ else if (udpserver)
+ LogSock = udpopenlog(udpserver,udpport);
+ else
+ LogSock = myopenlog(usock);
+
+ /* log input line if appropriate */
+ if (argc > 0) {
+ register char *p, *endp;
+ size_t len;
+
+ for (p = buf, endp = buf + sizeof(buf) - 2; *argv;) {
+ len = strlen(*argv);
+ if (p + len > endp && p > buf) {
+ if (!usock && !udpserver)
+ syslog(pri, "%s", buf);
+ else
+ mysyslog(LogSock, logflags, pri, tag, buf);
+ p = buf;
+ }
+ if (len > sizeof(buf) - 1) {
+ if (!usock && !udpserver)
+ syslog(pri, "%s", *argv++);
+ else
+ mysyslog(LogSock, logflags, pri, tag, *argv++);
+ } else {
+ if (p != buf)
+ *p++ = ' ';
+ memmove(p, *argv++, len);
+ *(p += len) = '\0';
+ }
+ }
+ if (p != buf) {
+ if (!usock && !udpserver)
+ syslog(pri, "%s", buf);
+ else
+ mysyslog(LogSock, logflags, pri, tag, buf);
+ }
+ } else {
+ while (fgets(buf, sizeof(buf), stdin) != NULL) {
+ /* glibc is buggy and adds an additional newline,
+ so we have to remove it here until glibc is fixed */
+ int len = strlen(buf);
+
+ if (len > 0 && buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+
+ if (!usock && !udpserver)
+ syslog(pri, "%s", buf);
+ else
+ mysyslog(LogSock, logflags, pri, tag, buf);
+ }
+ }
+ if (!usock && !udpserver)
+ closelog();
+ else
+ close(LogSock);
+
+ return EXIT_SUCCESS;
+}
diff --git a/misc-utils/look.1 b/misc-utils/look.1
new file mode 100644
index 0000000..fe8d8fd
--- /dev/null
+++ b/misc-utils/look.1
@@ -0,0 +1,116 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)look.1 8.1 (Berkeley) 6/14/93
+.\"
+.TH LOOK 1 "June 2011" "util-linux" "User Commands"
+.SH NAME
+look \- display lines beginning with a given string
+.SH SYNOPSIS
+.B look
+.RI [ options ] " string " [ file ]
+.SH DESCRIPTION
+The
+.B look
+utility displays any lines in
+.I file
+which contain
+.IR string .
+As
+.B look
+performs a binary search, the lines in
+.I file
+must be sorted (where sort(1) got the same options
+.BR "\-d " and/or " \-f " that
+.B look
+is invoked with).
+.PP
+If
+.I file
+is not specified, the file
+.I /usr/share/dict/words
+is used, only alphanumeric characters are compared and the case of
+alphabetic characters is ignored.
+.SH OPTIONS
+.TP
+.BR \-a , " \-\-alternative"
+Use the alternative dictionary file.
+.TP
+.BR \-d , " \-\-alphanum"
+Use normal dictionary character set and order, i.e. only alphanumeric characters
+are compared. (This is on by default if no file is specified.)
+.TP
+.BR \-f , " \-\-ignore\-case"
+Ignore the case of alphabetic characters. (This is on by default if no file is
+specified.)
+.TP
+.BR \-t , " \-\-terminate " \fIcharacter\fR
+Specify a string termination character, i.e. only the characters
+in \fIstring\fR up to and including the first occurrence of \fIcharacter\fR
+are compared.
+.TP
+.BR \-h , " \-\-help"
+Display help text and exit.
+.TP
+.BR \-V , " \-\-version"
+Output version information and exit.
+.PP
+The
+.B look
+utility exits 0 if one or more lines were found and displayed, 1 if
+no lines were found, and >1 if an error occurred.
+.SH EXAMPLE
+.RS
+.nf
+sort -d /etc/passwd -o /tmp/look.dict
+look -t: root:foobar /tmp/look.dict
+.nf
+.RE
+.SH FILES
+.IX Header "FILES"
+.IP "\fB/usr/share/dict/words\fR" 4
+the dictionary
+.IP "\fB/usr/share/dict/web2\fR" 4
+the alternative dictionary
+.SH "SEE ALSO"
+.BR grep (1),
+.BR sort (1)
+.SH COMPATIBILITY
+The original manual page stated that tabs and blank characters participated
+in comparisons when the alphanum option was specified. This was incorrect,
+and the current man page matches the historic implementation.
+.SH HISTORY
+The
+.B look
+utility appeared in Version 7 AT&T Unix.
+.SH AVAILABILITY
+The look command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/misc-utils/look.c b/misc-utils/look.c
new file mode 100644
index 0000000..351d007
--- /dev/null
+++ b/misc-utils/look.c
@@ -0,0 +1,381 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * David Hitz of Auspex Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+ /* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ */
+
+/*
+ * look -- find lines in a sorted list.
+ *
+ * The man page said that TABs and SPACEs participate in -d comparisons.
+ * In fact, they were ignored. This implements historic practice, not
+ * the manual page.
+ */
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+
+#include "nls.h"
+#include "xalloc.h"
+#include "pathnames.h"
+#include "closestream.h"
+
+#define EQUAL 0
+#define GREATER 1
+#define LESS (-1)
+
+int dflag, fflag;
+/* uglified the source a bit with globals, so that we only need
+ to allocate comparbuf once */
+int stringlen;
+char *string;
+char *comparbuf;
+
+static char *binary_search (char *, char *);
+static int compare (char *, char *);
+static char *linear_search (char *, char *);
+static int look (char *, char *);
+static void print_from (char *, char *);
+static void __attribute__ ((__noreturn__)) usage(FILE * out);
+
+int
+main(int argc, char *argv[])
+{
+ struct stat sb;
+ int ch, fd, termchar;
+ char *back, *file, *front, *p;
+
+ static const struct option longopts[] = {
+ {"alternative", no_argument, NULL, 'a'},
+ {"alphanum", no_argument, NULL, 'd'},
+ {"ignore-case", no_argument, NULL, 'f'},
+ {"terminate", required_argument, NULL, 't'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ setlocale(LC_ALL, "");
+
+ file = _PATH_WORDS;
+ termchar = '\0';
+ string = NULL; /* just for gcc */
+
+ while ((ch = getopt_long(argc, argv, "adft:Vh", longopts, NULL)) != -1)
+ switch(ch) {
+ case 'a':
+ file = _PATH_WORDS_ALT;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 't':
+ termchar = *optarg;
+ break;
+ case 'V':
+ printf(_("%s from %s\n"),
+ program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ case '?':
+ default:
+ usage(stderr);
+ }
+ argc -= optind;
+ argv += optind;
+
+ switch (argc) {
+ case 2: /* Don't set -df for user. */
+ string = *argv++;
+ file = *argv;
+ break;
+ case 1: /* But set -df by default. */
+ dflag = fflag = 1;
+ string = *argv;
+ break;
+ default:
+ usage(stderr);
+ }
+
+ if (termchar != '\0' && (p = strchr(string, termchar)) != NULL)
+ *++p = '\0';
+
+ if ((fd = open(file, O_RDONLY, 0)) < 0 || fstat(fd, &sb))
+ err(EXIT_FAILURE, "%s", file);
+ front = mmap(NULL, (size_t) sb.st_size, PROT_READ,
+#ifdef MAP_FILE
+ MAP_FILE |
+#endif
+ MAP_SHARED, fd, (off_t) 0);
+ if
+#ifdef MAP_FAILED
+ (front == MAP_FAILED)
+#else
+ ((void *)(front) <= (void *)0)
+#endif
+ err(EXIT_FAILURE, "%s", file);
+
+#if 0
+ /* workaround for mmap problem (rmiller@duskglow.com) */
+ if (front == (void *)0)
+ return 1;
+#endif
+
+ back = front + sb.st_size;
+ return look(front, back);
+}
+
+int
+look(char *front, char *back)
+{
+ int ch;
+ char *readp, *writep;
+
+ /* Reformat string string to avoid doing it multiple times later. */
+ if (dflag) {
+ for (readp = writep = string; (ch = *readp++) != 0;) {
+ if (isalnum(ch))
+ *(writep++) = ch;
+ }
+ *writep = '\0';
+ stringlen = writep - string;
+ } else
+ stringlen = strlen(string);
+
+ comparbuf = xmalloc(stringlen+1);
+
+ front = binary_search(front, back);
+ front = linear_search(front, back);
+
+ if (front)
+ print_from(front, back);
+
+ free(comparbuf);
+
+ return (front ? 0 : 1);
+}
+
+
+/*
+ * Binary search for "string" in memory between "front" and "back".
+ *
+ * This routine is expected to return a pointer to the start of a line at
+ * *or before* the first word matching "string". Relaxing the constraint
+ * this way simplifies the algorithm.
+ *
+ * Invariants:
+ * front points to the beginning of a line at or before the first
+ * matching string.
+ *
+ * back points to the beginning of a line at or after the first
+ * matching line.
+ *
+ * Advancing the Invariants:
+ *
+ * p = first newline after halfway point from front to back.
+ *
+ * If the string at "p" is not greater than the string to match,
+ * p is the new front. Otherwise it is the new back.
+ *
+ * Termination:
+ *
+ * The definition of the routine allows it return at any point,
+ * since front is always at or before the line to print.
+ *
+ * In fact, it returns when the chosen "p" equals "back". This
+ * implies that there exists a string is least half as long as
+ * (back - front), which in turn implies that a linear search will
+ * be no more expensive than the cost of simply printing a string or two.
+ *
+ * Trying to continue with binary search at this point would be
+ * more trouble than it's worth.
+ */
+#define SKIP_PAST_NEWLINE(p, back) \
+ while (p < back && *p++ != '\n')
+
+char *
+binary_search(char *front, char *back)
+{
+ char *p;
+
+ p = front + (back - front) / 2;
+ SKIP_PAST_NEWLINE(p, back);
+
+ /*
+ * If the file changes underneath us, make sure we don't
+ * infinitely loop.
+ */
+ while (p < back && back > front) {
+ if (compare(p, back) == GREATER)
+ front = p;
+ else
+ back = p;
+ p = front + (back - front) / 2;
+ SKIP_PAST_NEWLINE(p, back);
+ }
+ return (front);
+}
+
+/*
+ * Find the first line that starts with string, linearly searching from front
+ * to back.
+ *
+ * Return NULL for no such line.
+ *
+ * This routine assumes:
+ *
+ * o front points at the first character in a line.
+ * o front is before or at the first line to be printed.
+ */
+char *
+linear_search(char *front, char *back)
+{
+ while (front < back) {
+ switch (compare(front, back)) {
+ case EQUAL: /* Found it. */
+ return (front);
+ break;
+ case LESS: /* No such string. */
+ return (NULL);
+ break;
+ case GREATER: /* Keep going. */
+ break;
+ }
+ SKIP_PAST_NEWLINE(front, back);
+ }
+ return (NULL);
+}
+
+/*
+ * Print as many lines as match string, starting at front.
+ */
+void
+print_from(char *front, char *back)
+{
+ int eol;
+
+ while (front < back && compare(front, back) == EQUAL) {
+ if (compare(front, back) == EQUAL) {
+ eol = 0;
+ while (front < back && !eol) {
+ if (putchar(*front) == EOF)
+ err(EXIT_FAILURE, "stdout");
+ if (*front++ == '\n')
+ eol = 1;
+ }
+ } else
+ SKIP_PAST_NEWLINE(front, back);
+ }
+}
+
+/*
+ * Return LESS, GREATER, or EQUAL depending on how string compares with
+ * string2 (s1 ??? s2).
+ *
+ * o Matches up to len(s1) are EQUAL.
+ * o Matches up to len(s2) are GREATER.
+ *
+ * Compare understands about the -f and -d flags, and treats comparisons
+ * appropriately.
+ *
+ * The string "string" is null terminated. The string "s2" is '\n' terminated
+ * (or "s2end" terminated).
+ *
+ * We use strcasecmp etc, since it knows how to ignore case also
+ * in other locales.
+ */
+int
+compare(char *s2, char *s2end) {
+ int i;
+ char *p;
+
+ /* copy, ignoring things that should be ignored */
+ p = comparbuf;
+ i = stringlen;
+ while(s2 < s2end && *s2 != '\n' && i) {
+ if (!dflag || isalnum(*s2))
+ {
+ *p++ = *s2;
+ i--;
+ }
+ s2++;
+ }
+ *p = 0;
+
+ /* and compare */
+ if (fflag)
+ i = strncasecmp(comparbuf, string, stringlen);
+ else
+ i = strncmp(comparbuf, string, stringlen);
+
+ return ((i > 0) ? LESS : (i < 0) ? GREATER : EQUAL);
+}
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fputs(_("\nUsage:\n"), out),
+ fprintf(out,
+ _(" %s [options] string [file]\n"), program_invocation_short_name);
+
+ fputs(_("\nOptions:\n"), out);
+ fputs(_(" -a, --alternative use alternate dictionary\n"
+ " -d, --alphanum compare only alpha numeric characters\n"
+ " -f, --ignore-case ignore when comparing\n"
+ " -t, --terminate <char> define string termination character\n"
+ " -V, --version output version information and exit\n"
+ " -h, --help display this help and exit\n\n"), out);
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
diff --git a/misc-utils/lsblk.8 b/misc-utils/lsblk.8
new file mode 100644
index 0000000..4ed024f
--- /dev/null
+++ b/misc-utils/lsblk.8
@@ -0,0 +1,104 @@
+.\" -*- nroff -*-
+.TH LSBLK 8 "April 2010" "util-linux" "System Administration"
+.SH NAME
+lsblk \- list block devices
+.SH SYNOPSIS
+.B lsblk
+.RB [ options ]
+.sp
+.B lsblk
+.RB [ options ]
+.IR device...
+.SH DESCRIPTION
+.B lsblk
+lists information about all or the specified block devices. The
+.B lsblk
+command reads the
+.I sysfs
+filesystem to gather information.
+.PP
+The command prints all block devices (except RAM disks) in a tree-like format
+by default. Use
+.B "lsblk --help"
+to get a list of all available columns.
+.PP
+The default output as well as default output from options like --topology and
+--fs is subject to change, so whenever possible you should avoid using default
+outputs in your scripts. Always explicitly define expected columns by
+.B \-\-output
+.IR columns
+in environment where a stable output is required.
+.SH OPTIONS
+.IP "\fB\-a, \-\-all\fP"
+.B lsblk
+does not list empty devices by default. This option disables this restriction.
+.IP "\fB\-b, \-\-bytes\fP"
+Print the SIZE column in bytes rather than in human-readable format.
+.IP "\fB\-d, \-\-nodeps\fP"
+Don't print device holders or slaves. For example "lsblk --nodeps /dev/sda" prints
+information about the sda device only.
+.IP "\fB\-D, \-\-discard\fP"
+Print information about the discard (TRIM, UNMAP) capabilities for each device.
+.IP "\fB\-e, \-\-exclude \fIlist\fP
+Exclude the devices specified by a comma-separated \fIlist\fR of major device numbers.
+Note that RAM disks (major=1) are excluded by default. The filter is applied to the top-level
+devices only.
+.IP "\fB\-I, \-\-include \fIlist\fP
+Include devices specified by a comma-separated \fIlist\fR of major device numbers only.
+The filter is applied to the top-level devices.
+.IP "\fB\-f, \-\-fs\fP
+Output info about filesystems. This option is equivalent to "-o NAME,FSTYPE,LABEL,MOUNTPOINT".
+The authoritative information about filesystems and raids is provided by the
+.BR blkid (8)
+command.
+.IP "\fB\-h, \-\-help\fP"
+Print a help text and exit.
+.IP "\fB\-i, \-\-ascii\fP"
+Use ASCII characters for tree formatting.
+.IP "\fB\-m, \-\-perms\fP
+Output info about device owner, group and mode. This option is equivalent to "-o NAME,SIZE,OWNER,GROUP,MODE".
+.IP "\fB\-l, \-\-list\fP"
+Use the list output format.
+.IP "\fB\-n, \-\-noheadings\fP"
+Do not print a header line.
+.IP "\fB\-o, \-\-output \fIlist\fP"
+Specify which output columns to print. Use
+.B "--help"
+to get a list of all supported columns.
+.IP "\fB\-P, \-\-pairs\fP"
+Use key="value" output format. All potentially unsafe characters are hex-escaped (\\x<code>).
+.IP "\fB\-r, \-\-raw\fP"
+Use the raw output format. All potentially unsafe characters are hex-escaped
+(\\x<code>) in NAME, KNAME, LABEL, PARTLABEL and MOUNTPOINT columns.
+.IP "\fB\-s, \-\-inverse\fP"
+Print dependencies in inverse order.
+.IP "\fB\-t, \-\-topology\fP"
+Output info about block device topology.
+This option is equivalent to "-o NAME,ALIGNMENT,MIN-IO,OPT-IO,PHY-SEC,LOG-SEC,ROTA,SCHED,RQ-SIZE".
+.IP "\fB\-V, \-\-version\fP"
+Output version information and exit.
+.SH NOTES
+For partitions, some information (e.g. queue attributes) is inherited from the
+parent device.
+
+.PP
+The
+.B lsblk
+needs to be able to lookup sysfs path by major:minor, which is done
+done by using
+.BR /sys/dev/block .
+The block sysfs appeared in kernel 2.6.27 (October 2008). In case of
+problem with new enough kernel check that CONFIG_SYSFS was enabled at
+the time of kernel build.
+.SH AUTHORS
+.nf
+Milan Broz <mbroz@redhat.com>
+Karel Zak <kzak@redhat.com>
+.fi
+.SH SEE ALSO
+.BR findmnt (8),
+.BR blkid (8),
+.BR ls (1)
+.SH AVAILABILITY
+The lsblk command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/misc-utils/lsblk.c b/misc-utils/lsblk.c
new file mode 100644
index 0000000..2410de4
--- /dev/null
+++ b/misc-utils/lsblk.c
@@ -0,0 +1,1421 @@
+/*
+ * lsblk(8) - list block devices
+ *
+ * Copyright (C) 2010,2011 Red Hat, Inc. All rights reserved.
+ * Written by Milan Broz <mbroz@redhat.com>
+ * Karel Zak <kzak@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <locale.h>
+#include <pwd.h>
+#include <grp.h>
+#include <ctype.h>
+
+#include <blkid.h>
+#include <libmount.h>
+
+#ifdef HAVE_LIBUDEV
+#include <libudev.h>
+#endif
+
+#include <assert.h>
+
+#include "c.h"
+#include "pathnames.h"
+#include "blkdev.h"
+#include "canonicalize.h"
+#include "nls.h"
+#include "tt.h"
+#include "xalloc.h"
+#include "strutils.h"
+#include "at.h"
+#include "sysfs.h"
+#include "closestream.h"
+#include "mangle.h"
+#include "optutils.h"
+
+/* column IDs */
+enum {
+ COL_NAME = 0,
+ COL_KNAME,
+ COL_MAJMIN,
+ COL_FSTYPE,
+ COL_TARGET,
+ COL_LABEL,
+ COL_UUID,
+ COL_PARTLABEL,
+ COL_PARTUUID,
+ COL_RA,
+ COL_RO,
+ COL_RM,
+ COL_MODEL,
+ COL_SIZE,
+ COL_STATE,
+ COL_OWNER,
+ COL_GROUP,
+ COL_MODE,
+ COL_ALIOFF,
+ COL_MINIO,
+ COL_OPTIO,
+ COL_PHYSEC,
+ COL_LOGSEC,
+ COL_ROTA,
+ COL_SCHED,
+ COL_RQ_SIZE,
+ COL_TYPE,
+ COL_DALIGN,
+ COL_DGRAN,
+ COL_DMAX,
+ COL_DZERO,
+ COL_WWN,
+};
+
+/* column names */
+struct colinfo {
+ const char *name; /* header */
+ double whint; /* width hint (N < 1 is in percent of termwidth) */
+ int flags; /* TT_FL_* */
+ const char *help;
+};
+
+/* columns descriptions */
+static struct colinfo infos[] = {
+ [COL_NAME] = { "NAME", 0.25, TT_FL_TREE | TT_FL_NOEXTREMES, N_("device name") },
+ [COL_KNAME] = { "KNAME", 0.3, 0, N_("internal kernel device name") },
+ [COL_MAJMIN] = { "MAJ:MIN", 6, 0, N_("major:minor device number") },
+ [COL_FSTYPE] = { "FSTYPE", 0.1, TT_FL_TRUNC, N_("filesystem type") },
+ [COL_TARGET] = { "MOUNTPOINT", 0.10, TT_FL_TRUNC, N_("where the device is mounted") },
+ [COL_LABEL] = { "LABEL", 0.1, 0, N_("filesystem LABEL") },
+ [COL_UUID] = { "UUID", 36, 0, N_("filesystem UUID") },
+
+ [COL_PARTLABEL] = { "PARTLABEL", 0.1, 0, N_("partition LABEL") },
+ [COL_PARTUUID] = { "PARTUUID", 36, 0, N_("partition UUID") },
+
+ [COL_RA] = { "RA", 4, TT_FL_RIGHT, N_("read-ahead of the device") },
+ [COL_RO] = { "RO", 1, TT_FL_RIGHT, N_("read-only device") },
+ [COL_RM] = { "RM", 1, TT_FL_RIGHT, N_("removable device") },
+ [COL_ROTA] = { "ROTA", 1, TT_FL_RIGHT, N_("rotational device") },
+ [COL_MODEL] = { "MODEL", 0.1, TT_FL_TRUNC, N_("device identifier") },
+ [COL_SIZE] = { "SIZE", 5, TT_FL_RIGHT, N_("size of the device") },
+ [COL_STATE] = { "STATE", 7, TT_FL_TRUNC, N_("state of the device") },
+ [COL_OWNER] = { "OWNER", 0.1, TT_FL_TRUNC, N_("user name"), },
+ [COL_GROUP] = { "GROUP", 0.1, TT_FL_TRUNC, N_("group name") },
+ [COL_MODE] = { "MODE", 10, 0, N_("device node permissions") },
+ [COL_ALIOFF] = { "ALIGNMENT", 6, TT_FL_RIGHT, N_("alignment offset") },
+ [COL_MINIO] = { "MIN-IO", 6, TT_FL_RIGHT, N_("minimum I/O size") },
+ [COL_OPTIO] = { "OPT-IO", 6, TT_FL_RIGHT, N_("optimal I/O size") },
+ [COL_PHYSEC] = { "PHY-SEC", 7, TT_FL_RIGHT, N_("physical sector size") },
+ [COL_LOGSEC] = { "LOG-SEC", 7, TT_FL_RIGHT, N_("logical sector size") },
+ [COL_SCHED] = { "SCHED", 0.1, 0, N_("I/O scheduler name") },
+ [COL_RQ_SIZE]= { "RQ-SIZE", 5, TT_FL_RIGHT, N_("request queue size") },
+ [COL_TYPE] = { "TYPE", 4, 0, N_("device type") },
+ [COL_DALIGN] = { "DISC-ALN", 6, TT_FL_RIGHT, N_("discard alignment offset") },
+ [COL_DGRAN] = { "DISC-GRAN", 6, TT_FL_RIGHT, N_("discard granularity") },
+ [COL_DMAX] = { "DISC-MAX", 6, TT_FL_RIGHT, N_("discard max bytes") },
+ [COL_DZERO] = { "DISC-ZERO", 1, TT_FL_RIGHT, N_("discard zeroes data") },
+ [COL_WWN] = { "WWN", 18, 0, N_("unique storage identifier") },
+
+};
+
+struct lsblk {
+ struct tt *tt; /* output table */
+ unsigned int all_devices:1; /* print all devices, including empty */
+ unsigned int bytes:1; /* print SIZE in bytes */
+ unsigned int inverse:1; /* print inverse dependencies */
+ unsigned int nodeps:1; /* don't print slaves/holders */
+};
+
+struct lsblk *lsblk; /* global handler */
+
+#define NCOLS ARRAY_SIZE(infos)
+static int columns[NCOLS];/* enabled columns */
+static int ncolumns; /* number of enabled columns */
+
+static int excludes[256];
+static size_t nexcludes;
+
+static int includes[256];
+static size_t nincludes;
+
+static struct libmnt_table *mtab, *swaps;
+static struct libmnt_cache *mntcache;
+
+struct blkdev_cxt {
+ struct blkdev_cxt *parent;
+
+ struct tt_line *tt_line;
+ struct stat st;
+
+ char *name; /* kernel name in /sys/block */
+ char *dm_name; /* DM name (dm/block) */
+
+ char *filename; /* path to device node */
+
+ struct sysfs_cxt sysfs;
+
+ int partition; /* is partition? TRUE/FALSE */
+
+ int probed; /* already probed */
+ char *fstype; /* detected fs, NULL or "?" if cannot detect */
+ char *uuid; /* filesystem UUID (or stack uuid) */
+ char *label; /* filesystem label */
+ char *partuuid; /* partition UUID */
+ char *partlabel; /* partiton label */
+ char *wwn; /* storage WWN */
+
+ int npartitions; /* # of partitions this device has */
+ int nholders; /* # of devices mapped directly to this device
+ * /sys/block/.../holders */
+ int nslaves; /* # of devices this device maps to */
+ int maj, min; /* devno */
+ int discard; /* supports discard */
+
+ uint64_t size; /* device size */
+};
+
+static int is_maj_excluded(int maj)
+{
+ size_t i;
+
+ assert(ARRAY_SIZE(excludes) > nexcludes);
+
+ if (!nexcludes)
+ return 0; /* filter not enabled, device not exluded */
+
+ for (i = 0; i < nexcludes; i++)
+ if (excludes[i] == maj)
+ return 1;
+ return 0;
+}
+
+static int is_maj_included(int maj)
+{
+ size_t i;
+
+ assert(ARRAY_SIZE(includes) > nincludes);
+
+ if (!nincludes)
+ return 1; /* filter not enabled, device is included */
+
+ for (i = 0; i < nincludes; i++)
+ if (includes[i] == maj)
+ return 1;
+ return 0;
+}
+
+/* array with IDs of enabled columns */
+static int get_column_id(int num)
+{
+ assert(ARRAY_SIZE(columns) == NCOLS);
+ assert(num < ncolumns);
+ assert(columns[num] < (int) NCOLS);
+ return columns[num];
+}
+
+static struct colinfo *get_column_info(int num)
+{
+ return &infos[ get_column_id(num) ];
+}
+
+static int column_name_to_id(const char *name, size_t namesz)
+{
+ size_t i;
+
+ for (i = 0; i < NCOLS; i++) {
+ const char *cn = infos[i].name;
+
+ if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
+ return i;
+ }
+ warnx(_("unknown column: %s"), name);
+ return -1;
+}
+
+static void reset_blkdev_cxt(struct blkdev_cxt *cxt)
+{
+ if (!cxt)
+ return;
+ free(cxt->name);
+ free(cxt->dm_name);
+ free(cxt->filename);
+ free(cxt->fstype);
+ free(cxt->uuid);
+ free(cxt->label);
+ free(cxt->partuuid);
+ free(cxt->partlabel);
+ free(cxt->wwn);
+
+ sysfs_deinit(&cxt->sysfs);
+
+ memset(cxt, 0, sizeof(*cxt));
+}
+
+static int is_dm(const char *name)
+{
+ return strncmp(name, "dm-", 3) ? 0 : 1;
+}
+
+static struct dirent *xreaddir(DIR *dp)
+{
+ struct dirent *d;
+
+ assert(dp);
+
+ while ((d = readdir(dp))) {
+ if (!strcmp(d->d_name, ".") ||
+ !strcmp(d->d_name, ".."))
+ continue;
+
+ /* blacklist here? */
+ break;
+ }
+ return d;
+}
+
+static char *get_device_path(struct blkdev_cxt *cxt)
+{
+ char path[PATH_MAX];
+
+ assert(cxt);
+ assert(cxt->name);
+
+ if (is_dm(cxt->name))
+ return canonicalize_dm_name(cxt->name);
+
+ snprintf(path, sizeof(path), "/dev/%s", cxt->name);
+ return xstrdup(path);
+}
+
+static int is_active_swap(const char *filename)
+{
+ if (!swaps) {
+ swaps = mnt_new_table();
+ if (!swaps)
+ return 0;
+ if (!mntcache)
+ mntcache = mnt_new_cache();
+
+ mnt_table_set_cache(swaps, mntcache);
+ mnt_table_parse_swaps(swaps, NULL);
+ }
+
+ return mnt_table_find_srcpath(swaps, filename, MNT_ITER_BACKWARD) != 0;
+}
+
+static char *get_device_mountpoint(struct blkdev_cxt *cxt)
+{
+ struct libmnt_fs *fs;
+ const char *fsroot;
+
+ assert(cxt);
+ assert(cxt->filename);
+
+ if (!mtab) {
+ mtab = mnt_new_table();
+ if (!mtab)
+ return NULL;
+ if (!mntcache)
+ mntcache = mnt_new_cache();
+
+ mnt_table_set_cache(mtab, mntcache);
+ mnt_table_parse_mtab(mtab, NULL);
+ }
+
+ /* try /etc/mtab or /proc/self/mountinfo */
+ fs = mnt_table_find_srcpath(mtab, cxt->filename, MNT_ITER_BACKWARD);
+ if (!fs)
+ return is_active_swap(cxt->filename) ? xstrdup("[SWAP]") : NULL;
+
+ /* found */
+ fsroot = mnt_fs_get_root(fs);
+ if (fsroot && strcmp(fsroot, "/") != 0) {
+ /* hmm.. we found bind mount or btrfs subvolume, let's try to
+ * get real FS root mountpoint */
+ struct libmnt_fs *rfs;
+ struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_BACKWARD);
+
+ mnt_table_set_iter(mtab, itr, fs);
+ while (mnt_table_next_fs(mtab, itr, &rfs) == 0) {
+ fsroot = mnt_fs_get_root(rfs);
+ if ((!fsroot || strcmp(fsroot, "/") == 0)
+ && mnt_fs_match_source(rfs, cxt->filename, mntcache)) {
+ fs = rfs;
+ break;
+ }
+ }
+ mnt_free_iter(itr);
+ }
+
+ return xstrdup(mnt_fs_get_target(fs));
+}
+
+#ifndef HAVE_LIBUDEV
+static int get_udev_properties(struct blkdev_cxt *cxt
+ __attribute__((__unused__)))
+{
+ return -1;
+}
+#else
+static int get_udev_properties(struct blkdev_cxt *cxt)
+{
+ struct udev *udev;
+ struct udev_device *dev;
+
+ if (cxt->probed)
+ return 0; /* already done */
+
+ udev = udev_new();
+ if (!udev)
+ return -1;
+
+ dev = udev_device_new_from_subsystem_sysname(udev, "block", cxt->name);
+ if (dev) {
+ const char *data;
+
+ if ((data = udev_device_get_property_value(dev, "ID_FS_LABEL_ENC"))) {
+ cxt->label = xstrdup(data);
+ unhexmangle_string(cxt->label);
+ }
+ if ((data = udev_device_get_property_value(dev, "ID_FS_UUID_ENC"))) {
+ cxt->uuid = xstrdup(data);
+ unhexmangle_string(cxt->uuid);
+ }
+ if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_NAME"))) {
+ cxt->partlabel = xstrdup(data);
+ unhexmangle_string(cxt->partlabel);
+ }
+ if ((data = udev_device_get_property_value(dev, "ID_FS_TYPE")))
+ cxt->fstype = xstrdup(data);
+ if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_UUID")))
+ cxt->partuuid = xstrdup(data);
+ if ((data = udev_device_get_property_value(dev, "ID_WWN")))
+ cxt->wwn = xstrdup(data);
+
+ udev_device_unref(dev);
+ cxt->probed = 1;
+ }
+
+ udev_unref(udev);
+
+ return cxt->probed == 1 ? 0 : -1;
+
+}
+#endif /* HAVE_LIBUDEV */
+
+static void probe_device(struct blkdev_cxt *cxt)
+{
+ blkid_probe pr = NULL;
+
+ if (cxt->probed)
+ return;
+
+ if (!cxt->size)
+ return;
+
+ /* try udev DB */
+ if (get_udev_properties(cxt) == 0)
+ return; /* success */
+
+ cxt->probed = 1;
+
+ /* try libblkid (fallback) */
+ if (getuid() != 0)
+ return; /* no permissions to read from the device */
+
+ pr = blkid_new_probe_from_filename(cxt->filename);
+ if (!pr)
+ return;
+
+ blkid_probe_enable_superblocks(pr, 1);
+ blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_LABEL |
+ BLKID_SUBLKS_UUID |
+ BLKID_SUBLKS_TYPE);
+ blkid_probe_enable_partitions(pr, 1);
+ blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
+
+ if (!blkid_do_safeprobe(pr)) {
+ const char *data = NULL;
+
+ if (!blkid_probe_lookup_value(pr, "TYPE", &data, NULL))
+ cxt->fstype = xstrdup(data);
+ if (!blkid_probe_lookup_value(pr, "UUID", &data, NULL))
+ cxt->uuid = xstrdup(data);
+ if (!blkid_probe_lookup_value(pr, "LABEL", &data, NULL))
+ cxt->label = xstrdup(data);
+ if (!blkid_probe_lookup_value(pr, "PART_ENTRY_UUID", &data, NULL))
+ cxt->partuuid = xstrdup(data);
+ if (!blkid_probe_lookup_value(pr, "PART_ENTRY_NAME", &data, NULL))
+ cxt->partlabel = xstrdup(data);
+
+ }
+
+ blkid_free_probe(pr);
+ return;
+}
+
+static int is_readonly_device(struct blkdev_cxt *cxt)
+{
+ int fd, ro = 0;
+
+ if (sysfs_scanf(&cxt->sysfs, "ro", "%d", &ro) == 1)
+ return ro;
+
+ /* fallback if "ro" attribute does not exist */
+ fd = open(cxt->filename, O_RDONLY);
+ if (fd != -1) {
+ if (ioctl(fd, BLKROGET, &ro) != 0)
+ ro = 0;
+ close(fd);
+ }
+ return ro;
+}
+
+static char *get_scheduler(struct blkdev_cxt *cxt)
+{
+ char *str = sysfs_strdup(&cxt->sysfs, "queue/scheduler");
+ char *p, *res = NULL;
+
+ if (!str)
+ return NULL;
+ p = strchr(str, '[');
+ if (p) {
+ res = p + 1;
+ p = strchr(res, ']');
+ if (p) {
+ *p = '\0';
+ res = xstrdup(res);
+ } else
+ res = NULL;
+ }
+ free(str);
+ return res;
+}
+
+static char *get_type(struct blkdev_cxt *cxt)
+{
+ char *res = NULL, *p;
+
+ if (is_dm(cxt->name)) {
+ char *dm_uuid = sysfs_strdup(&cxt->sysfs, "dm/uuid");
+
+ /* The DM_UUID prefix should be set to subsystem owning
+ * the device - LVM, CRYPT, DMRAID, MPATH, PART */
+ if (dm_uuid) {
+ char *tmp = dm_uuid;
+ char *dm_uuid_prefix = strsep(&tmp, "-");
+
+ if (dm_uuid_prefix) {
+ /* kpartx hack to remove partition number */
+ if (strncasecmp(dm_uuid_prefix, "part", 4) == 0)
+ dm_uuid_prefix[4] = '\0';
+
+ res = xstrdup(dm_uuid_prefix);
+ }
+ }
+
+ free(dm_uuid);
+ if (!res)
+ /* No UUID or no prefix - just mark it as DM device */
+ res = xstrdup("dm");
+
+ } else if (!strncmp(cxt->name, "loop", 4)) {
+ res = xstrdup("loop");
+
+ } else if (!strncmp(cxt->name, "md", 2)) {
+ char *md_level = sysfs_strdup(&cxt->sysfs, "md/level");
+ res = md_level ? md_level : xstrdup("md");
+
+ } else {
+ const char *type;
+ int x = 0;
+
+ sysfs_read_int(&cxt->sysfs, "device/type", &x);
+
+ type = blkdev_scsi_type_to_name(x);
+ if (!type)
+ type = cxt->partition ? "part" : "disk";
+ res = xstrdup(type);
+ }
+
+ for (p = res; p && *p; p++)
+ *p = tolower((unsigned char) *p);
+ return res;
+}
+
+#define is_parsable(_l) (((_l)->tt->flags & TT_FL_RAW) || \
+ ((_l)->tt->flags & TT_FL_EXPORT))
+
+static void set_tt_data(struct blkdev_cxt *cxt, int col, int id, struct tt_line *ln)
+{
+ char buf[1024];
+ char *p = NULL;
+ int st_rc = 0;
+
+ if (!cxt->st.st_rdev && (id == COL_OWNER || id == COL_GROUP ||
+ id == COL_MODE))
+ st_rc = stat(cxt->filename, &cxt->st);
+
+ switch(id) {
+ case COL_NAME:
+ if (cxt->dm_name) {
+ if (is_parsable(lsblk))
+ tt_line_set_data(ln, col, xstrdup(cxt->dm_name));
+ else {
+ snprintf(buf, sizeof(buf), "%s (%s)",
+ cxt->dm_name, cxt->name);
+ tt_line_set_data(ln, col, xstrdup(buf));
+ }
+ break;
+ }
+ case COL_KNAME:
+ tt_line_set_data(ln, col, xstrdup(cxt->name));
+ break;
+ case COL_OWNER:
+ {
+ struct passwd *pw = st_rc ? NULL : getpwuid(cxt->st.st_uid);
+ if (pw)
+ tt_line_set_data(ln, col, xstrdup(pw->pw_name));
+ break;
+ }
+ case COL_GROUP:
+ {
+ struct group *gr = st_rc ? NULL : getgrgid(cxt->st.st_gid);
+ if (gr)
+ tt_line_set_data(ln, col, xstrdup(gr->gr_name));
+ break;
+ }
+ case COL_MODE:
+ {
+ char md[11];
+
+ if (!st_rc) {
+ strmode(cxt->st.st_mode, md);
+ tt_line_set_data(ln, col, xstrdup(md));
+ }
+ break;
+ }
+ case COL_MAJMIN:
+ if (is_parsable(lsblk))
+ snprintf(buf, sizeof(buf), "%u:%u", cxt->maj, cxt->min);
+ else
+ snprintf(buf, sizeof(buf), "%3u:%-3u", cxt->maj, cxt->min);
+ tt_line_set_data(ln, col, xstrdup(buf));
+ break;
+ case COL_FSTYPE:
+ probe_device(cxt);
+ if (cxt->fstype)
+ tt_line_set_data(ln, col, xstrdup(cxt->fstype));
+ break;
+ case COL_TARGET:
+ if (!(cxt->nholders + cxt->npartitions)) {
+ if ((p = get_device_mountpoint(cxt)))
+ tt_line_set_data(ln, col, p);
+ }
+ break;
+ case COL_LABEL:
+ probe_device(cxt);
+ if (!cxt->label)
+ break;
+
+ tt_line_set_data(ln, col, xstrdup(cxt->label));
+ break;
+ case COL_UUID:
+ probe_device(cxt);
+ if (cxt->uuid)
+ tt_line_set_data(ln, col, xstrdup(cxt->uuid));
+ break;
+ case COL_PARTLABEL:
+ probe_device(cxt);
+ if (!cxt->partlabel)
+ break;
+
+ tt_line_set_data(ln, col, xstrdup(cxt->partlabel));
+ break;
+ case COL_PARTUUID:
+ probe_device(cxt);
+ if (cxt->uuid)
+ tt_line_set_data(ln, col, xstrdup(cxt->partuuid));
+ break;
+ case COL_WWN:
+ get_udev_properties(cxt);
+ if (cxt->wwn)
+ tt_line_set_data(ln, col, xstrdup(cxt->wwn));
+ break;
+ case COL_RA:
+ p = sysfs_strdup(&cxt->sysfs, "queue/read_ahead_kb");
+ if (p)
+ tt_line_set_data(ln, col, p);
+ break;
+ case COL_RO:
+ tt_line_set_data(ln, col, is_readonly_device(cxt) ?
+ xstrdup("1") : xstrdup("0"));
+ break;
+ case COL_RM:
+ p = sysfs_strdup(&cxt->sysfs, "removable");
+ if (!p && cxt->sysfs.parent)
+ p = sysfs_strdup(cxt->sysfs.parent, "removable");
+ if (p)
+ tt_line_set_data(ln, col, p);
+ break;
+ case COL_ROTA:
+ p = sysfs_strdup(&cxt->sysfs, "queue/rotational");
+ if (p)
+ tt_line_set_data(ln, col, p);
+ break;
+ case COL_MODEL:
+ if (!cxt->partition && cxt->nslaves == 0) {
+ p = sysfs_strdup(&cxt->sysfs, "device/model");
+ if (p)
+ tt_line_set_data(ln, col, p);
+ }
+ break;
+ case COL_SIZE:
+ if (cxt->size) {
+ if (lsblk->bytes) {
+ if (xasprintf(&p, "%jd", cxt->size) < 0)
+ p = NULL;
+ } else
+ p = size_to_human_string(SIZE_SUFFIX_1LETTER, cxt->size);
+ if (p)
+ tt_line_set_data(ln, col, p);
+ }
+ break;
+ case COL_STATE:
+ if (!cxt->partition && !cxt->dm_name) {
+ p = sysfs_strdup(&cxt->sysfs, "device/state");
+ } else if (cxt->dm_name) {
+ int x = 0;
+ if (sysfs_read_int(&cxt->sysfs, "dm/suspended", &x) == 0)
+ p = x ? xstrdup("suspended") : xstrdup("running");
+ }
+ if (p)
+ tt_line_set_data(ln, col, p);
+ break;
+ case COL_ALIOFF:
+ p = sysfs_strdup(&cxt->sysfs, "alignment_offset");
+ if (p)
+ tt_line_set_data(ln, col, p);
+ break;
+ case COL_MINIO:
+ p = sysfs_strdup(&cxt->sysfs, "queue/minimum_io_size");
+ if (p)
+ tt_line_set_data(ln, col, p);
+ break;
+ case COL_OPTIO:
+ p = sysfs_strdup(&cxt->sysfs, "queue/optimal_io_size");
+ if (p)
+ tt_line_set_data(ln, col, p);
+ break;
+ case COL_PHYSEC:
+ p = sysfs_strdup(&cxt->sysfs, "queue/physical_block_size");
+ if (p)
+ tt_line_set_data(ln, col, p);
+ break;
+ case COL_LOGSEC:
+ p = sysfs_strdup(&cxt->sysfs, "queue/logical_block_size");
+ if (p)
+ tt_line_set_data(ln, col, p);
+ break;
+ case COL_SCHED:
+ p = get_scheduler(cxt);
+ if (p)
+ tt_line_set_data(ln, col, p);
+ break;
+ case COL_RQ_SIZE:
+ p = sysfs_strdup(&cxt->sysfs, "queue/nr_requests");
+ if (p)
+ tt_line_set_data(ln, col, p);
+ break;
+ case COL_TYPE:
+ p = get_type(cxt);
+ if (p)
+ tt_line_set_data(ln, col, p);
+ break;
+ case COL_DALIGN:
+ p = sysfs_strdup(&cxt->sysfs, "discard_alignment");
+ if (cxt->discard && p)
+ tt_line_set_data(ln, col, p);
+ else
+ tt_line_set_data(ln, col, "0");
+ break;
+ case COL_DGRAN:
+ if (lsblk->bytes)
+ p = sysfs_strdup(&cxt->sysfs, "queue/discard_granularity");
+ else {
+ uint64_t x;
+
+ if (sysfs_read_u64(&cxt->sysfs,
+ "queue/discard_granularity", &x) == 0)
+ p = size_to_human_string(SIZE_SUFFIX_1LETTER, x);
+ }
+ if (p)
+ tt_line_set_data(ln, col, p);
+ break;
+ case COL_DMAX:
+ if (lsblk->bytes)
+ p = sysfs_strdup(&cxt->sysfs, "queue/discard_max_bytes");
+ else {
+ uint64_t x;
+
+ if (sysfs_read_u64(&cxt->sysfs,
+ "queue/discard_max_bytes", &x) == 0)
+ p = size_to_human_string(SIZE_SUFFIX_1LETTER, x);
+ }
+ if (p)
+ tt_line_set_data(ln, col, p);
+ break;
+ case COL_DZERO:
+ p = sysfs_strdup(&cxt->sysfs, "queue/discard_zeroes_data");
+ if (cxt->discard && p)
+ tt_line_set_data(ln, col, p);
+ else
+ tt_line_set_data(ln, col, "0");
+ break;
+ };
+}
+
+static void print_device(struct blkdev_cxt *cxt, struct tt_line *tt_parent)
+{
+ int i;
+
+ cxt->tt_line = tt_add_line(lsblk->tt, tt_parent);
+
+ for (i = 0; i < ncolumns; i++)
+ set_tt_data(cxt, i, get_column_id(i), cxt->tt_line);
+}
+
+static int set_cxt(struct blkdev_cxt *cxt,
+ struct blkdev_cxt *parent,
+ struct blkdev_cxt *wholedisk,
+ const char *name)
+{
+ dev_t devno;
+
+ cxt->parent = parent;
+ cxt->name = xstrdup(name);
+ cxt->partition = wholedisk != NULL;
+
+ cxt->filename = get_device_path(cxt);
+ if (!cxt->filename) {
+ warnx(_("%s: failed to get device path"), name);
+ return -1;
+ }
+
+ devno = sysfs_devname_to_devno(name, wholedisk ? wholedisk->name : NULL);
+
+ if (!devno) {
+ warnx(_("%s: unknown device name"), name);
+ return -1;
+ }
+
+ if (lsblk->inverse) {
+ if (sysfs_init(&cxt->sysfs, devno, wholedisk ? &wholedisk->sysfs : NULL)) {
+ warnx(_("%s: failed to initialize sysfs handler"), name);
+ return -1;
+ }
+ if (parent)
+ parent->sysfs.parent = &cxt->sysfs;
+ } else {
+ if (sysfs_init(&cxt->sysfs, devno, parent ? &parent->sysfs : NULL)) {
+ warnx(_("%s: failed to initialize sysfs handler"), name);
+ return -1;
+ }
+ }
+
+ cxt->maj = major(devno);
+ cxt->min = minor(devno);
+ cxt->size = 0;
+
+ if (sysfs_read_u64(&cxt->sysfs, "size", &cxt->size) == 0) /* in sectors */
+ cxt->size <<= 9; /* in bytes */
+
+ sysfs_read_int(&cxt->sysfs, "queue/discard_granularity", &cxt->discard);
+
+ /* Ignore devices of zero size */
+ if (!lsblk->all_devices && cxt->size == 0)
+ return -1;
+
+ if (is_dm(name)) {
+ cxt->dm_name = sysfs_strdup(&cxt->sysfs, "dm/name");
+ if (!cxt->dm_name) {
+ warnx(_("%s: failed to get dm name"), name);
+ return -1;
+ }
+ }
+
+ cxt->npartitions = sysfs_count_partitions(&cxt->sysfs, name);
+ cxt->nholders = sysfs_count_dirents(&cxt->sysfs, "holders");
+ cxt->nslaves = sysfs_count_dirents(&cxt->sysfs, "slaves");
+
+ return 0;
+}
+
+static int process_blkdev(struct blkdev_cxt *cxt, struct blkdev_cxt *parent,
+ int do_partitions, const char *part_name);
+
+/*
+ * List device partitions if any.
+ */
+static int list_partitions(struct blkdev_cxt *wholedisk_cxt, struct blkdev_cxt *parent_cxt,
+ const char *part_name)
+{
+ DIR *dir;
+ struct dirent *d;
+ struct blkdev_cxt part_cxt = {};
+ int r = -1;
+
+ assert(wholedisk_cxt);
+
+ /*
+ * Do not process further if there are no partitions for
+ * this device or the device itself is a partition.
+ */
+ if (!wholedisk_cxt->npartitions || wholedisk_cxt->partition)
+ return -1;
+
+ dir = sysfs_opendir(&wholedisk_cxt->sysfs, NULL);
+ if (!dir)
+ err(EXIT_FAILURE, _("failed to open device directory in sysfs"));
+
+ while ((d = xreaddir(dir))) {
+ /* Process particular partition only? */
+ if (part_name && strcmp(part_name, d->d_name))
+ continue;
+
+ if (!(sysfs_is_partition_dirent(dir, d, wholedisk_cxt->name)))
+ continue;
+
+ if (lsblk->inverse) {
+ /*
+ * <parent_cxt>
+ * `-<part_cxt>
+ * `-<wholedisk_cxt>
+ * `-...
+ */
+ if (set_cxt(&part_cxt, parent_cxt, wholedisk_cxt, d->d_name))
+ goto next;
+
+ if (!parent_cxt && part_cxt.nholders)
+ goto next;
+
+ wholedisk_cxt->parent = &part_cxt;
+ print_device(&part_cxt, parent_cxt ? parent_cxt->tt_line : NULL);
+ if (!lsblk->nodeps)
+ process_blkdev(wholedisk_cxt, &part_cxt, 0, NULL);
+ } else {
+ /*
+ * <parent_cxt>
+ * `-<wholedisk_cxt>
+ * `-<part_cxt>
+ * `-...
+ */
+ if (set_cxt(&part_cxt, wholedisk_cxt, wholedisk_cxt, d->d_name))
+ goto next;
+ /* Print whole disk only once */
+ if (r)
+ print_device(wholedisk_cxt, parent_cxt ? parent_cxt->tt_line : NULL);
+ if (!lsblk->nodeps)
+ process_blkdev(&part_cxt, wholedisk_cxt, 0, NULL);
+ }
+ next:
+ reset_blkdev_cxt(&part_cxt);
+ r = 0;
+ }
+
+ closedir(dir);
+ return r;
+}
+
+static int get_wholedisk_from_partition_dirent(DIR *dir, const char *dirname,
+ struct dirent *d, struct blkdev_cxt *cxt)
+{
+ char path[PATH_MAX];
+ char *p;
+ int len;
+
+ if ((len = readlink_at(dirfd(dir), dirname,
+ d->d_name, path, sizeof(path))) < 0)
+ return 0;
+
+ path[len] = '\0';
+
+ /* The path ends with ".../<device>/<partition>" */
+ p = strrchr(path, '/');
+ if (!p)
+ return 0;
+ *p = '\0';
+
+ p = strrchr(path, '/');
+ if (!p)
+ return 0;
+ p++;
+
+ return set_cxt(cxt, NULL, NULL, p);
+}
+
+/*
+ * List device dependencies: partitions, holders (inverse = 0) or slaves (inverse = 1).
+ */
+static int list_deps(struct blkdev_cxt *cxt)
+{
+ DIR *dir;
+ struct dirent *d;
+ struct blkdev_cxt dep = {};
+ char dirname[PATH_MAX];
+ const char *depname;
+
+ assert(cxt);
+
+ if (lsblk->nodeps)
+ return 0;
+
+ if (!(lsblk->inverse ? cxt->nslaves : cxt->nholders))
+ return 0;
+
+ depname = lsblk->inverse ? "slaves" : "holders";
+ dir = sysfs_opendir(&cxt->sysfs, depname);
+ if (!dir)
+ return 0;
+
+ snprintf(dirname, sizeof(dirname), "%s/%s", cxt->sysfs.dir_path, depname);
+
+ while ((d = xreaddir(dir))) {
+ /* Is the dependency a partition? */
+ if (sysfs_is_partition_dirent(dir, d, NULL)) {
+ if (!get_wholedisk_from_partition_dirent(dir, dirname, d, &dep))
+ process_blkdev(&dep, cxt, 1, d->d_name);
+ }
+ /* The dependency is a whole device. */
+ else if (!set_cxt(&dep, cxt, NULL, d->d_name))
+ process_blkdev(&dep, cxt, 1, NULL);
+
+ reset_blkdev_cxt(&dep);
+ }
+ closedir(dir);
+
+ return 0;
+}
+
+static int process_blkdev(struct blkdev_cxt *cxt, struct blkdev_cxt *parent,
+ int do_partitions, const char *part_name)
+{
+ if (do_partitions && cxt->npartitions)
+ return list_partitions(cxt, parent, part_name);
+
+ print_device(cxt, parent ? parent->tt_line : NULL);
+ return list_deps(cxt);
+}
+
+/* Iterate devices in sysfs */
+static int iterate_block_devices(void)
+{
+ DIR *dir;
+ struct dirent *d;
+ struct blkdev_cxt cxt = {};
+
+ if (!(dir = opendir(_PATH_SYS_BLOCK)))
+ return EXIT_FAILURE;
+
+ while ((d = xreaddir(dir))) {
+ if (set_cxt(&cxt, NULL, NULL, d->d_name))
+ goto next;
+
+ if (is_maj_excluded(cxt.maj) || !is_maj_included(cxt.maj))
+ goto next;
+
+ /* Skip devices in the middle of dependency tree. */
+ if ((lsblk->inverse ? cxt.nholders : cxt.nslaves) > 0)
+ goto next;
+
+ process_blkdev(&cxt, NULL, 1, NULL);
+ next:
+ reset_blkdev_cxt(&cxt);
+ }
+
+ closedir(dir);
+
+ return EXIT_SUCCESS;
+}
+
+static int process_one_device(char *devname)
+{
+ struct blkdev_cxt parent = {}, cxt = {};
+ struct stat st;
+ char buf[PATH_MAX + 1], *diskname = NULL;
+ dev_t disk = 0;
+ int status = EXIT_FAILURE;
+
+ if (stat(devname, &st) || !S_ISBLK(st.st_mode)) {
+ warnx(_("%s: not a block device"), devname);
+ return EXIT_FAILURE;
+ }
+ if (blkid_devno_to_wholedisk(st.st_rdev, buf, sizeof(buf), &disk)) {
+ warn(_("%s: failed to get whole-disk device number"), devname);
+ return EXIT_FAILURE;
+ }
+ if (st.st_rdev == disk) {
+ /*
+ * Device is not a partition.
+ */
+ if (set_cxt(&cxt, NULL, NULL, buf))
+ goto leave;
+ process_blkdev(&cxt, NULL, !lsblk->inverse, NULL);
+ } else {
+ /*
+ * Partition, read sysfs name of the device.
+ */
+ ssize_t len;
+ char path[PATH_MAX], *name;
+
+ if (!sysfs_devno_path(st.st_rdev, path, sizeof(path))) {
+ warn(_("failed to compose sysfs path for %s"), devname);
+ goto leave;
+ }
+
+ diskname = xstrdup(buf);
+ len = readlink(path, buf, PATH_MAX);
+ if (len < 0) {
+ warn(_("%s: failed to read link"), path);
+ goto leave;
+ }
+ buf[len] = '\0';
+
+ /* sysfs device name */
+ name = strrchr(buf, '/') + 1;
+
+ if (set_cxt(&parent, NULL, NULL, diskname))
+ goto leave;
+ if (set_cxt(&cxt, &parent, &parent, name))
+ goto leave;
+
+ if (lsblk->inverse)
+ process_blkdev(&parent, &cxt, 1, cxt.name);
+ else
+ process_blkdev(&cxt, &parent, 1, NULL);
+ }
+
+ status = EXIT_SUCCESS;
+leave:
+ free(diskname);
+ reset_blkdev_cxt(&cxt);
+
+ if (st.st_rdev != disk)
+ reset_blkdev_cxt(&parent);
+
+ return status;
+}
+
+static void parse_excludes(const char *str0)
+{
+ const char *str = str0;
+
+ while (str && *str) {
+ char *end = NULL;
+ unsigned long n;
+
+ errno = 0;
+ n = strtoul(str, &end, 10);
+
+ if (end == str || (end && *end && *end != ','))
+ errx(EXIT_FAILURE, _("failed to parse list '%s'"), str0);
+ if (errno != 0 && (n == ULONG_MAX || n == 0))
+ err(EXIT_FAILURE, _("failed to parse list '%s'"), str0);
+ excludes[nexcludes++] = n;
+
+ if (nexcludes == ARRAY_SIZE(excludes))
+ /* TRANSLATORS: The standard value for %d is 256. */
+ errx(EXIT_FAILURE, _("the list of excluded devices is "
+ "too large (limit is %d devices)"),
+ (int)ARRAY_SIZE(excludes));
+
+ str = end && *end ? end + 1 : NULL;
+ }
+}
+
+static void parse_includes(const char *str0)
+{
+ const char *str = str0;
+
+ while (str && *str) {
+ char *end = NULL;
+ unsigned long n;
+
+ errno = 0;
+ n = strtoul(str, &end, 10);
+
+ if (end == str || (end && *end && *end != ','))
+ errx(EXIT_FAILURE, _("failed to parse list '%s'"), str0);
+ if (errno != 0 && (n == ULONG_MAX || n == 0))
+ err(EXIT_FAILURE, _("failed to parse list '%s'"), str0);
+ includes[nincludes++] = n;
+
+ if (nincludes == ARRAY_SIZE(includes))
+ /* TRANSLATORS: The standard value for %d is 256. */
+ errx(EXIT_FAILURE, _("the list of included devices is "
+ "too large (limit is %d devices)"),
+ (int)ARRAY_SIZE(includes));
+ str = end && *end ? end + 1 : NULL;
+ }
+}
+
+static void __attribute__((__noreturn__)) help(FILE *out)
+{
+ size_t i;
+
+ fprintf(out, _(
+ "\nUsage:\n"
+ " %s [options] [<device> ...]\n"), program_invocation_short_name);
+
+ fprintf(out, _(
+ "\nOptions:\n"
+ " -a, --all print all devices\n"
+ " -b, --bytes print SIZE in bytes rather than in human readable format\n"
+ " -d, --nodeps don't print slaves or holders\n"
+ " -D, --discard print discard capabilities\n"
+ " -e, --exclude <list> exclude devices by major number (default: RAM disks)\n"
+ " -I, --include <list> show only devices with specified major numbers\n"
+ " -f, --fs output info about filesystems\n"
+ " -h, --help usage information (this)\n"
+ " -i, --ascii use ascii characters only\n"
+ " -m, --perms output info about permissions\n"
+ " -l, --list use list format ouput\n"
+ " -n, --noheadings don't print headings\n"
+ " -o, --output <list> output columns\n"
+ " -P, --pairs use key=\"value\" output format\n"
+ " -r, --raw use raw output format\n"
+ " -s, --inverse inverse dependencies\n"
+ " -t, --topology output info about topology\n"
+ " -V, --version output version information and exit\n"));
+
+ fprintf(out, _("\nAvailable columns (for --output):\n"));
+
+ for (i = 0; i < NCOLS; i++)
+ fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help));
+
+ fprintf(out, USAGE_MAN_TAIL("lsblk(8)"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static void check_sysdevblock(void)
+{
+ if (access(_PATH_SYS_DEVBLOCK, R_OK) != 0)
+ err(EXIT_FAILURE, _("failed to access sysfs directory: %s"),
+ _PATH_SYS_DEVBLOCK);
+}
+
+int main(int argc, char *argv[])
+{
+ struct lsblk _ls;
+ int tt_flags = TT_FL_TREE;
+ int i, c, status = EXIT_FAILURE;
+ char *outarg = NULL;
+
+ static const struct option longopts[] = {
+ { "all", 0, 0, 'a' },
+ { "bytes", 0, 0, 'b' },
+ { "nodeps", 0, 0, 'd' },
+ { "discard", 0, 0, 'D' },
+ { "help", 0, 0, 'h' },
+ { "output", 1, 0, 'o' },
+ { "perms", 0, 0, 'm' },
+ { "noheadings", 0, 0, 'n' },
+ { "list", 0, 0, 'l' },
+ { "ascii", 0, 0, 'i' },
+ { "raw", 0, 0, 'r' },
+ { "inverse", 0, 0, 's' },
+ { "fs", 0, 0, 'f' },
+ { "exclude", 1, 0, 'e' },
+ { "include", 1, 0, 'I' },
+ { "topology", 0, 0, 't' },
+ { "pairs", 0, 0, 'P' },
+ { "version", 0, 0, 'V' },
+ { NULL, 0, 0, 0 },
+ };
+
+ static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */
+ { 'I','e' },
+ { 'P','l','r' },
+ { 0 }
+ };
+ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ lsblk = &_ls;
+ memset(lsblk, 0, sizeof(*lsblk));
+
+ while((c = getopt_long(argc, argv,
+ "abdDe:fhlnmo:PiI:rstV", longopts, NULL)) != -1) {
+
+ err_exclusive_options(c, longopts, excl, excl_st);
+
+ switch(c) {
+ case 'a':
+ lsblk->all_devices = 1;
+ break;
+ case 'b':
+ lsblk->bytes = 1;
+ break;
+ case 'd':
+ lsblk->nodeps = 1;
+ break;
+ case 'D':
+ columns[ncolumns++] = COL_NAME;
+ columns[ncolumns++] = COL_DALIGN;
+ columns[ncolumns++] = COL_DGRAN;
+ columns[ncolumns++] = COL_DMAX;
+ columns[ncolumns++] = COL_DZERO;
+ break;
+ case 'e':
+ parse_excludes(optarg);
+ break;
+ case 'h':
+ help(stdout);
+ break;
+ case 'l':
+ tt_flags &= ~TT_FL_TREE; /* disable the default */
+ break;
+ case 'n':
+ tt_flags |= TT_FL_NOHEADINGS;
+ break;
+ case 'o':
+ outarg = optarg;
+ break;
+ case 'P':
+ tt_flags |= TT_FL_EXPORT;
+ tt_flags &= ~TT_FL_TREE; /* disable the default */
+ break;
+ case 'i':
+ tt_flags |= TT_FL_ASCII;
+ break;
+ case 'I':
+ parse_includes(optarg);
+ break;
+ case 'r':
+ tt_flags &= ~TT_FL_TREE; /* disable the default */
+ tt_flags |= TT_FL_RAW; /* enable raw */
+ break;
+ case 's':
+ lsblk->inverse = 1;
+ break;
+ case 'f':
+ columns[ncolumns++] = COL_NAME;
+ columns[ncolumns++] = COL_FSTYPE;
+ columns[ncolumns++] = COL_LABEL;
+ columns[ncolumns++] = COL_UUID;
+ columns[ncolumns++] = COL_TARGET;
+ break;
+ case 'm':
+ columns[ncolumns++] = COL_NAME;
+ columns[ncolumns++] = COL_SIZE;
+ columns[ncolumns++] = COL_OWNER;
+ columns[ncolumns++] = COL_GROUP;
+ columns[ncolumns++] = COL_MODE;
+ break;
+ case 't':
+ columns[ncolumns++] = COL_NAME;
+ columns[ncolumns++] = COL_ALIOFF;
+ columns[ncolumns++] = COL_MINIO;
+ columns[ncolumns++] = COL_OPTIO;
+ columns[ncolumns++] = COL_PHYSEC;
+ columns[ncolumns++] = COL_LOGSEC;
+ columns[ncolumns++] = COL_ROTA;
+ columns[ncolumns++] = COL_SCHED;
+ columns[ncolumns++] = COL_RQ_SIZE;
+ columns[ncolumns++] = COL_RA;
+ break;
+ case 'V':
+ printf(_("%s from %s\n"), program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ default:
+ help(stderr);
+ }
+ }
+
+ check_sysdevblock();
+
+ if (!ncolumns) {
+ columns[ncolumns++] = COL_NAME;
+ columns[ncolumns++] = COL_MAJMIN;
+ columns[ncolumns++] = COL_RM;
+ columns[ncolumns++] = COL_SIZE;
+ columns[ncolumns++] = COL_RO;
+ columns[ncolumns++] = COL_TYPE;
+ columns[ncolumns++] = COL_TARGET;
+ }
+
+ if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
+ &ncolumns, column_name_to_id) < 0)
+ return EXIT_FAILURE;
+
+ if (nexcludes == 0 && nincludes == 0)
+ excludes[nexcludes++] = 1; /* default: ignore RAM disks */
+
+ mnt_init_debug(0);
+
+ /*
+ * initialize output columns
+ */
+ if (!(lsblk->tt = tt_new_table(tt_flags)))
+ errx(EXIT_FAILURE, _("failed to initialize output table"));
+
+ for (i = 0; i < ncolumns; i++) {
+ struct colinfo *ci = get_column_info(i);
+ int fl = ci->flags;
+
+ if (!(tt_flags & TT_FL_TREE) && get_column_id(i) == COL_NAME)
+ fl &= ~TT_FL_TREE;
+
+ if (!tt_define_column(lsblk->tt, ci->name, ci->whint, fl)) {
+ warn(_("failed to initialize output column"));
+ goto leave;
+ }
+ }
+
+ if (optind == argc)
+ status = iterate_block_devices();
+ else while (optind < argc)
+ status = process_one_device(argv[optind++]);
+
+ tt_print_table(lsblk->tt);
+
+leave:
+ tt_free_table(lsblk->tt);
+ mnt_free_table(mtab);
+ mnt_free_table(swaps);
+ mnt_free_cache(mntcache);
+ return status;
+}
diff --git a/misc-utils/lslocks.8 b/misc-utils/lslocks.8
new file mode 100644
index 0000000..24cda14
--- /dev/null
+++ b/misc-utils/lslocks.8
@@ -0,0 +1,82 @@
+.\" lslocks.8 --
+.\" Copyright 2012 Davidlohr Bueso <dave@gnu.org>
+.\" May be distributed under the GNU General Public License
+
+.TH LSLOCKS 8 "February 2012" "util-linux" "System Administration"
+.SH NAME
+lslocks \-
+list local system locks
+.SH SYNOPSIS
+.B lslocks
+.RB [options]
+
+.SH DESCRIPTION
+.B lslocks
+lists information about all the file held locks in a Linux system.
+
+.SH OPTIONS
+.IP "\fB\-p, \-\-pid\fP"
+Specify the process id, if none is given, it will display all locks
+.IP "\fB\-h, \-\-help\fP"
+Print a help text and exit.
+.IP "\fB\-o, \-\-output \fIlist\fP"
+Specify which output columns to print. Use
+.B "--help"
+to get a list of all supported columns.
+.IP "\fB\-n, \-\-noheadings\fP"
+Do not print a header line.
+.IP "\fB\-u, \-\-notruncate\fP"
+Do not truncate text in columns.
+.IP "\fB\-r, \-\-raw\fP"
+Use the raw output format.
+
+.SH OUTPUT
+.IP "COMMAND"
+The command name of the process holding the lock.
+
+.IP "PID"
+Process ID which holds the lock.
+
+.IP "TYPE"
+Type of lock, can be FLOCK (created with flock(2)) or POSIX (created with fcntl(2) and lockf(2))
+
+.IP "SIZE"
+Size of the locked file.
+
+.IP "MODE"
+Lock access permissions (read, write).
+
+.IP "M"
+Mandatory state of the lock: 0 if none; 1 if set. (See chmod(1)).
+
+.IP "START"
+Relative byte offset of the lock.
+
+.IP "END"
+Ending offset of the lock.
+
+.IP "PATH"
+Full path of the lock - if none is found or no permissions to read the path it
+will fallback to the device's mountpoint. The path might be truncated, use
+.B "--notruncate"
+to get the full path.
+
+.SH NOTES
+.nf
+The lslocks command is meant to replace the lslk(8) command, originally written by
+Victor A. Abell <abe@purdue.edu> and unmaintained since 2001.
+.fi
+
+.SH AUTHORS
+.nf
+Davidlohr Bueso <dave@gnu.org>
+.fi
+
+.SH "SEE ALSO"
+.BR flock (1)
+.BR fcntl (2)
+.BR lockf (2)
+
+.SH AVAILABILITY
+The lslocks command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/misc-utils/lslocks.c b/misc-utils/lslocks.c
new file mode 100644
index 0000000..45fb6de
--- /dev/null
+++ b/misc-utils/lslocks.c
@@ -0,0 +1,574 @@
+/*
+ * lslocks(8) - list local system locks
+ *
+ * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org>
+ *
+ * Very generally based on lslk(8) by Victor A. Abell <abe@purdue.edu>
+ * Since it stopped being maingained over a decade ago, this
+ * program should be considered its replacement.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "pathnames.h"
+#include "canonicalize.h"
+#include "nls.h"
+#include "tt.h"
+#include "xalloc.h"
+#include "at.h"
+#include "strutils.h"
+#include "c.h"
+#include "closestream.h"
+
+/* column IDs */
+enum {
+ COL_SRC = 0,
+ COL_PID,
+ COL_TYPE,
+ COL_SIZE,
+ COL_MODE,
+ COL_M,
+ COL_START,
+ COL_END,
+ COL_PATH,
+};
+
+/* column names */
+struct colinfo {
+ const char *name; /* header */
+ double whint; /* width hint (N < 1 is in percent of termwidth) */
+ int flags; /* TT_FL_* */
+ const char *help;
+};
+
+/* columns descriptions */
+static struct colinfo infos[] = {
+ [COL_SRC] = { "COMMAND",15, 0, N_("command of the process holding the lock") },
+ [COL_PID] = { "PID", 5, TT_FL_RIGHT, N_("PID of the process holding the lock") },
+ [COL_TYPE] = { "TYPE", 5, TT_FL_RIGHT, N_("kind of lock: FL_FLOCK or FL_POSIX.") },
+ [COL_SIZE] = { "SIZE", 4, TT_FL_RIGHT, N_("size of the lock") },
+ [COL_MODE] = { "MODE", 5, 0, N_("lock access mode") },
+ [COL_M] = { "M", 1, 0, N_("mandatory state of the lock: 0 (none), 1 (set)")},
+ [COL_START] = { "START", 10, TT_FL_RIGHT, N_("relative byte offset of the lock")},
+ [COL_END] = { "END", 10, TT_FL_RIGHT, N_("ending offset of the lock")},
+ [COL_PATH] = { "PATH", 0, TT_FL_TRUNC, N_("path of the locked file")},
+};
+#define NCOLS ARRAY_SIZE(infos)
+static int columns[NCOLS], ncolumns;
+static pid_t pid = 0;
+
+struct lock {
+ struct list_head locks;
+
+ char *cmdname;
+ pid_t pid;
+ char *path;
+ char *type;
+ char *mode;
+ off_t start;
+ off_t end;
+ int mandatory;
+ char *size;
+};
+
+static void disable_columns_truncate(void)
+{
+ size_t i;
+
+ for (i = 0; i < NCOLS; i++)
+ infos[i].flags &= ~TT_FL_TRUNC;
+}
+
+/*
+ * Return a PID's command name
+ */
+static char *get_cmdname(pid_t id)
+{
+ FILE *fp;
+ char path[PATH_MAX], *ret = NULL;
+
+ sprintf(path, "/proc/%d/comm", id);
+ if (!(fp = fopen(path, "r")))
+ return NULL;
+
+ if (!fgets(path, sizeof(path), fp))
+ goto out;
+
+ path[strlen(path) - 1] = '\0';
+ ret = xstrdup(path);
+out:
+ fclose(fp);
+ return ret;
+}
+
+/*
+ * Associate the device's mountpoint for a filename
+ */
+static char *get_fallback_filename(dev_t dev)
+{
+ char buf[BUFSIZ], target[PATH_MAX], *ret = NULL;
+ int maj, min;
+ FILE *fp;
+
+ if (!(fp = fopen(_PATH_PROC_MOUNTINFO, "r")))
+ return NULL;
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ sscanf(buf, "%*u %*u %u:%u %*s %s",
+ &maj, &min, target);
+
+ if (dev == makedev(maj, min)) {
+ ret = xstrdup(target);
+ goto out;
+
+ }
+ }
+out:
+ fclose(fp);
+ return ret;
+}
+
+/*
+ * Return the absolute path of a file from
+ * a given inode number (and its size)
+ */
+static char *get_filename_sz(ino_t inode, pid_t pid, size_t *size)
+{
+ struct stat sb;
+ struct dirent *dp;
+ DIR *dirp;
+ size_t len;
+ int fd;
+ char path[PATH_MAX], sym[PATH_MAX], *ret = NULL;
+
+ *size = 0;
+ memset(path, 0, sizeof(path));
+ memset(sym, 0, sizeof(sym));
+
+ /*
+ * We know the pid so we don't have to
+ * iterate the *entire* filesystem searching
+ * for the damn file.
+ */
+ sprintf(path, "/proc/%d/fd/", pid);
+ if (!(dirp = opendir(path)))
+ return NULL;
+
+ if ((len = strlen(path)) >= (sizeof(path) - 2))
+ goto out;
+
+ if ((fd = dirfd(dirp)) < 0 )
+ goto out;
+
+ while ((dp = readdir(dirp))) {
+ if (!strcmp(dp->d_name, ".") ||
+ !strcmp(dp->d_name, ".."))
+ continue;
+
+ /* care only for numerical descriptors */
+ if (!strtol(dp->d_name, (char **) NULL, 10))
+ continue;
+
+ if (!fstat_at(fd, path, dp->d_name, &sb, 0)
+ && inode != sb.st_ino)
+ continue;
+
+ if ((len = readlink_at(fd, path, dp->d_name,
+ sym, sizeof(path))) < 1)
+ goto out;
+
+ *size = sb.st_size;
+ sym[len] = '\0';
+
+ ret = xstrdup(sym);
+ break;
+ }
+out:
+ closedir(dirp);
+ return ret;
+}
+
+/*
+ * Return the inode number from a string
+ */
+static ino_t get_dev_inode(char *str, dev_t *dev)
+{
+ int maj = 0, min = 0;
+ ino_t inum = 0;
+
+ sscanf(str, "%02x:%02x:%lu", &maj, &min, &inum);
+
+ *dev = (dev_t) makedev(maj, min);
+ return inum;
+}
+
+static int get_local_locks(struct list_head *locks)
+{
+ int i;
+ ino_t inode = 0;
+ FILE *fp;
+ char buf[PATH_MAX], *szstr = NULL, *tok = NULL;
+ size_t sz;
+ struct lock *l;
+ dev_t dev = 0;
+
+ if (!(fp = fopen(_PATH_PROC_LOCKS, "r")))
+ return -1;
+
+ while (fgets(buf, sizeof(buf), fp)) {
+
+ l = xcalloc(1, sizeof(*l));
+ INIT_LIST_HEAD(&l->locks);
+
+ for (tok = strtok(buf, " "), i = 0; tok;
+ tok = strtok(NULL, " "), i++) {
+
+ /*
+ * /proc/locks has *exactly* 8 "blocks" of text
+ * separated by ' ' - check <kernel>/fs/locks.c
+ */
+ switch (i) {
+ case 0: /* ignore */
+ break;
+ case 1: /* posix, flock, etc */
+ l->type = xstrdup(tok);
+ break;
+
+ case 2: /* is this a mandatory lock? other values are advisory or noinode */
+ l->mandatory = *tok == 'M' ? TRUE : FALSE;
+ break;
+ case 3: /* lock mode */
+ l->mode = xstrdup(tok);
+ break;
+
+ case 4: /* PID */
+ /*
+ * If user passed a pid we filter it later when adding
+ * to the list, no need to worry now.
+ */
+ l->pid = strtos32_or_err(tok, _("failed to parse pid"));
+ l->cmdname = get_cmdname(l->pid);
+ if (!l->cmdname)
+ l->cmdname = xstrdup(_("(unknown)"));
+ break;
+
+ case 5: /* device major:minor and inode number */
+ inode = get_dev_inode(tok, &dev);
+ break;
+
+ case 6: /* start */
+ l->start = !strcmp(tok, "EOF") ? 0 :
+ strtou64_or_err(tok, _("failed to parse start"));
+ break;
+
+ case 7: /* end */
+ /* replace '\n' character */
+ tok[strlen(tok)-1] = '\0';
+ l->end = !strcmp(tok, "EOF") ? 0 :
+ strtou64_or_err(tok, _("failed to parse end"));
+ break;
+ default:
+ break;
+ }
+
+ l->path = get_filename_sz(inode, l->pid, &sz);
+ if (!l->path)
+ /* probably no permission to peek into l->pid's path */
+ l->path = get_fallback_filename(dev);
+
+ /* avoid leaking */
+ szstr = size_to_human_string(SIZE_SUFFIX_1LETTER, sz);
+ l->size = xstrdup(szstr);
+ free(szstr);
+ }
+
+ if (pid && pid != l->pid) {
+ /*
+ * It's easier to just parse the file then decide if
+ * it should be added to the list - otherwise just
+ * get rid of stored data
+ */
+ free(l->path);
+ free(l->size);
+ free(l->mode);
+ free(l->cmdname);
+ free(l->type);
+ free(l);
+
+ continue;
+ }
+
+ list_add(&l->locks, locks);
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+static int column_name_to_id(const char *name, size_t namesz)
+{
+ size_t i;
+
+ assert(name);
+
+ for (i = 0; i < NCOLS; i++) {
+ const char *cn = infos[i].name;
+
+ if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
+ return i;
+ }
+ warnx(_("unknown column: %s"), name);
+ return -1;
+}
+
+static inline int get_column_id(int num)
+{
+ assert(ARRAY_SIZE(columns) == NCOLS);
+ assert(num < ncolumns);
+ assert(columns[num] < (int) NCOLS);
+
+ return columns[num];
+}
+
+
+static inline struct colinfo *get_column_info(unsigned num)
+{
+ return &infos[ get_column_id(num) ];
+}
+
+static void rem_lock(struct lock *lock)
+{
+ if (!lock)
+ return;
+
+ free(lock->path);
+ free(lock->size);
+ free(lock->mode);
+ free(lock->cmdname);
+ free(lock->type);
+ list_del(&lock->locks);
+ free(lock);
+}
+
+static void add_tt_line(struct tt *tt, struct lock *l)
+{
+ int i;
+ struct tt_line *line;
+ /*
+ * Whenever cmdname or filename is NULL it is most
+ * likely because there's no read permissions
+ * for the specified process.
+ */
+ const char *notfnd = "";
+
+ assert(l);
+ assert(tt);
+
+ line = tt_add_line(tt, NULL);
+ if (!line) {
+ warn(_("failed to add line to output"));
+ return;
+ }
+
+ for (i = 0; i < ncolumns; i++) {
+ char *str = NULL;
+ int rc = 0;
+
+ switch (get_column_id(i)) {
+ case COL_SRC:
+ rc = xasprintf(&str, "%s", l->cmdname ? l->cmdname : notfnd);
+ break;
+ case COL_PID:
+ rc = xasprintf(&str, "%d", l->pid);
+ break;
+ case COL_TYPE:
+ rc = xasprintf(&str, "%s", l->type);
+ break;
+ case COL_SIZE:
+ rc = xasprintf(&str, "%s", l->size);
+ break;
+ case COL_MODE:
+ rc = xasprintf(&str, "%s", l->mode);
+ break;
+ case COL_M:
+ rc = xasprintf(&str, "%d", l->mandatory);
+ break;
+ case COL_START:
+ rc = xasprintf(&str, "%ld", l->start);
+ break;
+ case COL_END:
+ rc = xasprintf(&str, "%ld", l->end);
+ break;
+ case COL_PATH:
+ rc = xasprintf(&str, "%s", l->path ? l->path : notfnd);
+ break;
+ default:
+ break;
+ }
+
+ if (rc || str)
+ tt_line_set_data(line, i, str);
+ }
+}
+
+static int show_locks(struct list_head *locks, int tt_flags)
+{
+ int i, rc = 0;
+ struct list_head *p, *pnext;
+ struct tt *tt;
+
+ tt = tt_new_table(tt_flags);
+ if (!tt) {
+ warn(_("failed to initialize output table"));
+ return -1;
+ }
+
+ for (i = 0; i < ncolumns; i++) {
+ struct colinfo *col = get_column_info(i);
+
+ if (!tt_define_column(tt, col->name, col->whint, col->flags)) {
+ warnx(_("failed to initialize output column"));
+ rc = -1;
+ goto done;
+ }
+ }
+
+ list_for_each_safe(p, pnext, locks) {
+ struct lock *lock = list_entry(p, struct lock, locks);
+ add_tt_line(tt, lock);
+ rem_lock(lock);
+ }
+
+ tt_print_table(tt);
+done:
+ tt_free_table(tt);
+ return rc;
+}
+
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ size_t i;
+
+ fputs(USAGE_HEADER, out);
+
+ fprintf(out,
+ _(" %s [options]\n"), program_invocation_short_name);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -p, --pid <pid> process id\n"
+ " -o, --output <list> define which output columns to use\n"
+ " -n, --noheadings don't print headings\n"
+ " -r --raw use the raw output format\n"
+ " -u, --notruncate don't truncate text in columns\n"
+ " -h, --help display this help and exit\n"
+ " -V, --version output version information and exit\n"), out);
+
+ fputs(_("\nAvailable columns (for --output):\n"), out);
+
+ for (i = 0; i < NCOLS; i++)
+ fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help));
+
+ fprintf(out, USAGE_MAN_TAIL("lslocks(8)"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+ int c, tt_flags = 0, rc = 0;
+ struct list_head locks;
+ static const struct option long_opts[] = {
+ { "pid", required_argument, NULL, 'p' },
+ { "help", no_argument, NULL, 'h' },
+ { "output", required_argument, NULL, 'o' },
+ { "notruncate", no_argument, NULL, 'u' },
+ { "version", no_argument, NULL, 'V' },
+ { "noheadings", no_argument, NULL, 'n' },
+ { "raw", no_argument, NULL, 'r' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while ((c = getopt_long(argc, argv,
+ "p:o:nruhV", long_opts, NULL)) != -1) {
+
+ switch(c) {
+ case 'p':
+ pid = strtos32_or_err(optarg, _("invalid PID argument"));
+ break;
+ case 'o':
+ ncolumns = string_to_idarray(optarg,
+ columns, ARRAY_SIZE(columns),
+ column_name_to_id);
+ if (ncolumns < 0)
+ return EXIT_FAILURE;
+ break;
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ case 'n':
+ tt_flags |= TT_FL_NOHEADINGS;
+ break;
+ case 'r':
+ tt_flags |= TT_FL_RAW;
+ break;
+ case 'u':
+ disable_columns_truncate();
+ break;
+ case '?':
+ default:
+ usage(stderr);
+ }
+ }
+
+ INIT_LIST_HEAD(&locks);
+
+ if (!ncolumns) {
+ /* default columns */
+ columns[ncolumns++] = COL_SRC;
+ columns[ncolumns++] = COL_PID;
+ columns[ncolumns++] = COL_TYPE;
+ columns[ncolumns++] = COL_SIZE;
+ columns[ncolumns++] = COL_MODE;
+ columns[ncolumns++] = COL_M;
+ columns[ncolumns++] = COL_START;
+ columns[ncolumns++] = COL_END;
+ columns[ncolumns++] = COL_PATH;
+ }
+
+ rc = get_local_locks(&locks);
+
+ if (!rc && !list_empty(&locks))
+ rc = show_locks(&locks, tt_flags);
+
+ return rc;
+}
diff --git a/misc-utils/mcookie.1 b/misc-utils/mcookie.1
new file mode 100644
index 0000000..e7579c4
--- /dev/null
+++ b/misc-utils/mcookie.1
@@ -0,0 +1,65 @@
+.\" mcookie.1 --
+.\" Public Domain 1995 Rickard E. Faith (faith@cs.unc.edu)
+.TH MCOOKIE 1 "June 2011" "util-linux" "User Commands"
+.SH NAME
+mcookie \- generate magic cookies for xauth
+.SH SYNOPSIS
+.B mcookie
+[\fIoptions\fR]
+.SH DESCRIPTION
+.B mcookie
+generates a 128-bit random hexadecimal number for use with the X authority
+system. Typical usage:
+.RS
+xauth add :0 . `mcookie`
+.RE
+.PP
+The "random" number generated is actually the output of the MD5 message
+digest fed with various pieces of random information: the current time, the
+process id, the parent process id, and optionally the contents of an input
+file. and several bytes of information from the first of the following
+devices which is present:
+.IR /dev/random ,
+.IR /dev/urandom ,
+files in
+.IR /proc ,
+.IR /dev/audio .
+.SH OPTIONS
+.TP
+\fB\-f\fR, \fB\-\-file\fR=\fIFILE\fR
+Use file as a macig cookie seed. When file is defined as `-' character
+input is read from stdin.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+Explain what is being done.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Output version information and exit.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display this help and exit.
+.SH BUGS
+The entropy in the generated 128-bit is probably quite small (and,
+therefore, vulnerable to attack) unless a non-pseudorandom number generator
+is used (e.g.,
+.I /dev/random
+under Linux).
+.PP
+It is assumed that none of the devices opened will block.
+.SH FILES
+.I /dev/random
+.br
+.I /dev/urandom
+.br
+.I /dev/audio
+.br
+.I /proc/stat
+.br
+.I /proc/loadavg
+.SH "SEE ALSO"
+.BR X (1),
+.BR xauth (1),
+.BR md5sum (1)
+.SH AVAILABILITY
+The mcookie command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/misc-utils/mcookie.c b/misc-utils/mcookie.c
new file mode 100644
index 0000000..b7b1762
--- /dev/null
+++ b/misc-utils/mcookie.c
@@ -0,0 +1,190 @@
+/* mcookie.c -- Generates random numbers for xauth
+ * Created: Fri Feb 3 10:42:48 1995 by faith@cs.unc.edu
+ * Revised: Fri Mar 19 07:48:01 1999 by faith@acm.org
+ * Public Domain 1995, 1999 Rickard E. Faith (faith@acm.org)
+ * This program comes with ABSOLUTELY NO WARRANTY.
+ *
+ * This program gathers some random bits of data and used the MD5
+ * message-digest algorithm to generate a 128-bit hexadecimal number for
+ * use with xauth(1).
+ *
+ * NOTE: Unless /dev/random is available, this program does not actually
+ * gather 128 bits of random information, so the magic cookie generated
+ * will be considerably easier to guess than one might expect.
+ *
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ * 1999-03-21 aeb: Added some fragments of code from Colin Plumb.
+ *
+ */
+
+#include "c.h"
+#include "md5.h"
+#include "nls.h"
+#include "closestream.h"
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#define BUFFERSIZE 4096
+
+struct rngs {
+ const char *path;
+ int minlength, maxlength;
+} rngs[] = {
+ {"/dev/random", 16, 16}, /* 16 bytes = 128 bits suffice */
+ {"/proc/interrupts", 0, 0},
+ {"/proc/slabinfo", 0, 0},
+ {"/proc/stat", 0, 0},
+ {"/dev/urandom", 32, 64},
+};
+
+#define RNGS (sizeof(rngs)/sizeof(struct rngs))
+
+/* The basic function to hash a file */
+static off_t hash_file(struct MD5Context *ctx, int fd)
+{
+ off_t count = 0;
+ ssize_t r;
+ unsigned char buf[BUFFERSIZE];
+
+ while ((r = read(fd, buf, sizeof(buf))) > 0) {
+ MD5Update(ctx, buf, r);
+ count += r;
+ }
+ /* Separate files with a null byte */
+ buf[0] = '\0';
+ MD5Update(ctx, buf, 1);
+ return count;
+}
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fputs(_("\nUsage:\n"), out);
+ fprintf(out,
+ _(" %s [options]\n"), program_invocation_short_name);
+
+ fputs(_("\nOptions:\n"), out);
+ fputs(_(" -f, --file <file> use file as a cookie seed\n"
+ " -v, --verbose explain what is being done\n"
+ " -V, --version output version information and exit\n"
+ " -h, --help display this help and exit\n\n"), out);
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ size_t i;
+ struct MD5Context ctx;
+ unsigned char digest[MD5LENGTH];
+ unsigned char buf[BUFFERSIZE];
+ int fd;
+ int c;
+ pid_t pid;
+ char *file = NULL;
+ int verbose = 0;
+ int r;
+ struct timeval tv;
+ struct timezone tz;
+
+ static const struct option longopts[] = {
+ {"file", required_argument, NULL, 'f'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while ((c =
+ getopt_long(argc, argv, "f:vVh", longopts, NULL)) != -1)
+ switch (c) {
+ case 'v':
+ verbose = 1;
+ break;
+ case 'f':
+ file = optarg;
+ break;
+ case 'V':
+ printf(_("%s from %s\n"),
+ program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+
+ MD5Init(&ctx);
+ gettimeofday(&tv, &tz);
+ MD5Update(&ctx, (unsigned char *) &tv, sizeof(tv));
+
+ pid = getppid();
+ MD5Update(&ctx, (unsigned char *) &pid, sizeof(pid));
+ pid = getpid();
+ MD5Update(&ctx, (unsigned char *) &pid, sizeof(pid));
+
+ if (file) {
+ int count = 0;
+
+ if (file[0] == '-' && !file[1])
+ fd = STDIN_FILENO;
+ else
+ fd = open(file, O_RDONLY);
+
+ if (fd < 0) {
+ warn(_("cannot open %s"), file);
+ } else {
+ count = hash_file(&ctx, fd);
+ if (verbose)
+ fprintf(stderr,
+ _("Got %d bytes from %s\n"), count,
+ file);
+
+ if (fd != STDIN_FILENO)
+ if (close(fd))
+ err(EXIT_FAILURE,
+ _("closing %s failed"), file);
+ }
+ }
+
+ for (i = 0; i < RNGS; i++) {
+ if ((fd = open(rngs[i].path, O_RDONLY | O_NONBLOCK)) >= 0) {
+ int count = sizeof(buf);
+
+ if (rngs[i].maxlength && count > rngs[i].maxlength)
+ count = rngs[i].maxlength;
+ r = read(fd, buf, count);
+ if (r > 0)
+ MD5Update(&ctx, buf, r);
+ else
+ r = 0;
+ close(fd);
+ if (verbose)
+ fprintf(stderr,
+ _("Got %d bytes from %s\n"), r,
+ rngs[i].path);
+ if (rngs[i].minlength && r >= rngs[i].minlength)
+ break;
+ } else if (verbose)
+ warn(_("cannot open %s"), rngs[i].path);
+ }
+
+ MD5Final(digest, &ctx);
+ for (i = 0; i < MD5LENGTH; i++)
+ printf("%02x", digest[i]);
+ putchar('\n');
+
+ return EXIT_SUCCESS;
+}
diff --git a/misc-utils/namei.1 b/misc-utils/namei.1
new file mode 100644
index 0000000..88e249d
--- /dev/null
+++ b/misc-utils/namei.1
@@ -0,0 +1,74 @@
+.\"
+.\" Version 1.4 of namei
+.\"
+.TH NAMEI 1 "June 2011" "util-linux" "User Commands"
+.SH NAME
+namei \- follow a pathname until a terminal point is found
+.SH SYNOPSIS
+.B namei
+.RI [ options ]
+.IR pathname ...
+.SH DESCRIPTION
+.B namei
+uses its arguments as pathnames to any type
+of Unix file (symlinks, files, directories, and so forth).
+.B namei
+then follows each pathname until an endpoint
+is found (a file, a directory, a device node, etc).
+If it finds a symbolic link, it shows the link, and starts
+following it, indenting the output to show the context.
+.PP
+This program is useful for finding "too many levels of
+symbolic links" problems.
+.PP
+For each line of output,
+.B namei
+uses the following characters to identify the file type found:
+.LP
+.nf
+ f: = the pathname currently being resolved
+ d = directory
+ l = symbolic link (both the link and its contents are output)
+ s = socket
+ b = block device
+ c = character device
+ p = FIFO (named pipe)
+ - = regular file
+ ? = an error of some kind
+.fi
+.PP
+.B namei
+prints an informative message when
+the maximum number of symbolic links this system can have has been exceeded.
+.SH OPTIONS
+.IP "\fB\-l, \-\-long\fP"
+Use the long listing format (same as -m -o -v).
+.IP "\fB\-m, \-\-modes\fP"
+Show the mode bits of each file type in the style of ls(1),
+for example 'rwxr-xr-x'.
+.IP "\fB\-o, \-\-owners\fP"
+Show owner and group name of each file.
+.IP "\fB\-n, \-\-nosymlinks\fP"
+Don't follow symlinks.
+.IP "\fB\-v, \-\-vertical\fP"
+Vertically align the modes and owners.
+.IP "\fB\-x, \-\-mountpoints\fP"
+Show mountpoint directories with a 'D' rather than a 'd'.
+.IP "\fB\-h\fR, \fB\-\-help\fR"
+Output help text and exit.
+.IP "\fB\-V\fR, \fB\-\-version\fR"
+Output version information and exit.
+.SH AUTHOR
+The original
+.B namei
+program was written by Roger Southwick <rogers@amadeus.wr.tek.com>.
+
+The program was re-written by Karel Zak <kzak@redhat.com>.
+.SH BUGS
+To be discovered.
+.SH "SEE ALSO"
+.BR ls (1),
+.BR stat (1)
+.SH AVAILABILITY
+The namei command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/misc-utils/namei.c b/misc-utils/namei.c
new file mode 100644
index 0000000..376a2f0
--- /dev/null
+++ b/misc-utils/namei.c
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file is part of util-linux.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * The original namei(1) was writtent by:
+ * Roger S. Southwick (May 2, 1990)
+ * Steve Tell (March 28, 1991)
+ * Arkadiusz Mikiewicz (1999-02-22)
+ * Li Zefan (2007-09-10).
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include "c.h"
+#include "xalloc.h"
+#include "nls.h"
+#include "widechar.h"
+#include "strutils.h"
+#include "closestream.h"
+
+#ifndef MAXSYMLINKS
+#define MAXSYMLINKS 256
+#endif
+
+#ifndef LOGIN_NAME_MAX
+#define LOGIN_NAME_MAX 256
+#endif
+
+#define NAMEI_NOLINKS (1 << 1)
+#define NAMEI_MODES (1 << 2)
+#define NAMEI_MNTS (1 << 3)
+#define NAMEI_OWNERS (1 << 4)
+#define NAMEI_VERTICAL (1 << 5)
+
+
+struct namei {
+ struct stat st; /* item lstat() */
+ char *name; /* item name */
+ char *abslink; /* absolute symlink path */
+ int relstart; /* offset of relative path in 'abslink' */
+ struct namei *next; /* next item */
+ int level;
+ int mountpoint; /* is mount point */
+ int noent; /* is this item not existing */
+};
+
+struct idcache {
+ unsigned long int id;
+ char *name;
+ struct idcache *next;
+};
+
+static int flags;
+static int uwidth; /* maximal width of username */
+static int gwidth; /* maximal width of groupname */
+static struct idcache *gcache; /* groupnames */
+static struct idcache *ucache; /* usernames */
+
+static struct idcache *
+get_id(struct idcache *ic, unsigned long int id)
+{
+ while(ic) {
+ if (ic->id == id)
+ return ic;
+ ic = ic->next;
+ }
+ return NULL;
+}
+
+static void
+free_idcache(struct idcache *ic)
+{
+ while(ic) {
+ struct idcache *next = ic->next;
+ free(ic->name);
+ free(ic);
+ ic = next;
+ }
+}
+
+static void
+add_id(struct idcache **ic, char *name, unsigned long int id, int *width)
+{
+ struct idcache *nc, *x;
+ int w = 0;
+
+ nc = xcalloc(1, sizeof(*nc));
+ nc->id = id;
+
+ if (name) {
+#ifdef HAVE_WIDECHAR
+ wchar_t wc[LOGIN_NAME_MAX + 1];
+
+ if (mbstowcs(wc, name, LOGIN_NAME_MAX) > 0) {
+ wc[LOGIN_NAME_MAX] = '\0';
+ w = wcswidth(wc, LOGIN_NAME_MAX);
+ }
+ else
+#endif
+ w = strlen(name);
+ }
+ /* note, we ignore names with non-printable widechars */
+ if (w > 0)
+ nc->name = xstrdup(name);
+ else if (xasprintf(&nc->name, "%lu", id) == -1)
+ nc->name = NULL;
+
+ for (x = *ic; x && x->next; x = x->next);
+
+ /* add 'nc' at end of the 'ic' list */
+ if (x)
+ x->next = nc;
+ else
+ *ic = nc;
+ if (w <= 0)
+ w = nc->name ? strlen(nc->name) : 0;
+
+ *width = *width < w ? w : *width;
+ return;
+}
+
+static void
+add_uid(unsigned long int id)
+{
+ struct idcache *ic = get_id(ucache, id);
+
+ if (!ic) {
+ struct passwd *pw = getpwuid((uid_t) id);
+ add_id(&ucache, pw ? pw->pw_name : NULL, id, &uwidth);
+ }
+}
+
+static void
+add_gid(unsigned long int id)
+{
+ struct idcache *ic = get_id(gcache, id);
+
+ if (!ic) {
+ struct group *gr = getgrgid((gid_t) id);
+ add_id(&gcache, gr ? gr->gr_name : NULL, id, &gwidth);
+ }
+}
+
+static void
+free_namei(struct namei *nm)
+{
+ while (nm) {
+ struct namei *next = nm->next;
+ free(nm->name);
+ free(nm->abslink);
+ free(nm);
+ nm = next;
+ }
+}
+
+static void
+readlink_to_namei(struct namei *nm, const char *path)
+{
+ char sym[PATH_MAX];
+ ssize_t sz;
+ int isrel = 0;
+
+ sz = readlink(path, sym, sizeof(sym));
+ if (sz < 1)
+ err(EXIT_FAILURE, _("failed to read symlink: %s"), path);
+ if (*sym != '/') {
+ char *p = strrchr(path, '/');
+
+ if (p) {
+ isrel = 1;
+ nm->relstart = p ? p - path : 0;
+ sz += nm->relstart + 1;
+ }
+ }
+ nm->abslink = xmalloc(sz + 1);
+
+ if (*sym != '/' && isrel) {
+ /* create the absolute path from the relative symlink */
+ memcpy(nm->abslink, path, nm->relstart);
+ *(nm->abslink + nm->relstart) = '/';
+ nm->relstart++;
+ memcpy(nm->abslink + nm->relstart, sym, sz - nm->relstart);
+ } else
+ /* - absolute link (foo -> /path/bar)
+ * - or link without any subdir (foo -> bar)
+ */
+ memcpy(nm->abslink, sym, sz);
+
+ nm->abslink[sz] = '\0';
+}
+
+static struct stat *
+dotdot_stat(const char *dirname, struct stat *st)
+{
+ char *path;
+ size_t len;
+
+#define DOTDOTDIR "/.."
+
+ if (!dirname)
+ return NULL;
+
+ len = strlen(dirname);
+ path = xmalloc(len + sizeof(DOTDOTDIR));
+
+ memcpy(path, dirname, len);
+ memcpy(path + len, DOTDOTDIR, sizeof(DOTDOTDIR));
+
+ if (stat(path, st))
+ err(EXIT_FAILURE, _("stat failed %s"), path);
+ free(path);
+ return st;
+}
+
+static struct namei *
+new_namei(struct namei *parent, const char *path, const char *fname, int lev)
+{
+ struct namei *nm;
+
+ if (!fname)
+ return NULL;
+ nm = xcalloc(1, sizeof(*nm));
+ if (parent)
+ parent->next = nm;
+
+ nm->level = lev;
+ nm->name = xstrdup(fname);
+
+ nm->noent = (lstat(path, &nm->st) == -1);
+ if (nm->noent)
+ return nm;
+
+ if (S_ISLNK(nm->st.st_mode))
+ readlink_to_namei(nm, path);
+ if (flags & NAMEI_OWNERS) {
+ add_uid(nm->st.st_uid);
+ add_gid(nm->st.st_gid);
+ }
+
+ if ((flags & NAMEI_MNTS) && S_ISDIR(nm->st.st_mode)) {
+ struct stat stbuf, *sb = NULL;
+
+ if (parent && S_ISDIR(parent->st.st_mode))
+ sb = &parent->st;
+ else if (!parent || S_ISLNK(parent->st.st_mode))
+ sb = dotdot_stat(path, &stbuf);
+
+ if (sb && (sb->st_dev != nm->st.st_dev || /* different device */
+ sb->st_ino == nm->st.st_ino)) /* root directory */
+ nm->mountpoint = 1;
+ }
+
+ return nm;
+}
+
+static struct namei *
+add_namei(struct namei *parent, const char *orgpath, int start, struct namei **last)
+{
+ struct namei *nm = NULL, *first = NULL;
+ char *fname, *end, *path;
+ int level = 0;
+
+ if (!orgpath)
+ return NULL;
+ if (parent) {
+ nm = parent;
+ level = parent->level + 1;
+ }
+ path = xstrdup(orgpath);
+ fname = path + start;
+
+ /* root directory */
+ if (*fname == '/') {
+ while (*fname == '/')
+ fname++; /* eat extra '/' */
+ first = nm = new_namei(nm, "/", "/", level);
+ }
+
+ for (end = fname; fname && end; ) {
+ /* set end of filename */
+ if (*fname) {
+ end = strchr(fname, '/');
+ if (end)
+ *end = '\0';
+
+ /* create a new entry */
+ nm = new_namei(nm, path, fname, level);
+ } else
+ end = NULL;
+ if (!first)
+ first = nm;
+ /* set begin of the next filename */
+ if (end) {
+ *end++ = '/';
+ while (*end == '/')
+ end++; /* eat extra '/' */
+ }
+ fname = end;
+ }
+
+ if (last)
+ *last = nm;
+
+ free(path);
+
+ return first;
+}
+
+static int
+follow_symlinks(struct namei *nm)
+{
+ int symcount = 0;
+
+ for (; nm; nm = nm->next) {
+ struct namei *next, *last;
+
+ if (nm->noent)
+ continue;
+ if (!S_ISLNK(nm->st.st_mode))
+ continue;
+ if (++symcount > MAXSYMLINKS) {
+ /* drop the rest of the list */
+ free_namei(nm->next);
+ nm->next = NULL;
+ return -1;
+ }
+ next = nm->next;
+ nm->next = add_namei(nm, nm->abslink, nm->relstart, &last);
+ if (last)
+ last->next = next;
+ else
+ nm->next = next;
+ }
+ return 0;
+}
+
+static int
+print_namei(struct namei *nm, char *path)
+{
+ int i;
+
+ if (path)
+ printf("f: %s\n", path);
+
+ for (; nm; nm = nm->next) {
+ char md[11];
+
+ if (nm->noent) {
+ printf(_("%s - No such file or directory\n"), nm->name);
+ return -1;
+ }
+
+ strmode(nm->st.st_mode, md);
+
+ if (nm->mountpoint)
+ md[0] = 'D';
+
+ if (!(flags & NAMEI_VERTICAL)) {
+ for (i = 0; i < nm->level; i++)
+ fputs(" ", stdout);
+ fputc(' ', stdout);
+ }
+
+ if (flags & NAMEI_MODES)
+ printf("%s", md);
+ else
+ printf("%c", md[0]);
+
+ if (flags & NAMEI_OWNERS) {
+ printf(" %-*s", uwidth,
+ get_id(ucache, nm->st.st_uid)->name);
+ printf(" %-*s", gwidth,
+ get_id(gcache, nm->st.st_gid)->name);
+ }
+
+ if (flags & NAMEI_VERTICAL)
+ for (i = 0; i < nm->level; i++)
+ fputs(" ", stdout);
+
+ if (S_ISLNK(nm->st.st_mode))
+ printf(" %s -> %s\n", nm->name,
+ nm->abslink + nm->relstart);
+ else
+ printf(" %s\n", nm->name);
+ }
+ return 0;
+}
+
+static void usage(int rc)
+{
+ const char *p = program_invocation_short_name;
+ FILE *out = rc == EXIT_FAILURE ? stderr : stdout;
+
+ if (!*p)
+ p = "namei";
+
+ fputs(_("\nUsage:\n"), out);
+ fprintf(out,
+ _(" %s [options] pathname [pathname ...]\n"), p);
+
+ fputs(_("\nOptions:\n"), out);
+ fputs(_(" -h, --help displays this help text\n"
+ " -V, --version output version information and exit\n"
+ " -x, --mountpoints show mount point directories with a 'D'\n"
+ " -m, --modes show the mode bits of each file\n"
+ " -o, --owners show owner and group name of each file\n"
+ " -l, --long use a long listing format (-m -o -v) \n"
+ " -n, --nosymlinks don't follow symlinks\n"
+ " -v, --vertical vertical align of modes and owners\n"), out);
+
+ fputs(_("\nFor more information see namei(1).\n"), out);
+ exit(rc);
+}
+
+static const struct option longopts[] =
+{
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'V' },
+ { "mountpoints",0, 0, 'x' },
+ { "modes", 0, 0, 'm' },
+ { "owners", 0, 0, 'o' },
+ { "long", 0, 0, 'l' },
+ { "nolinks", 0, 0, 'n' },
+ { "vertical", 0, 0, 'v' },
+ { NULL, 0, 0, 0 },
+};
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ int rc = EXIT_SUCCESS;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while ((c = getopt_long(argc, argv, "hVlmnovx", longopts, NULL)) != -1) {
+ switch(c) {
+ case 'h':
+ usage(EXIT_SUCCESS);
+ break;
+ case 'V':
+ printf(_("%s from %s\n"), program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ case 'l':
+ flags |= (NAMEI_OWNERS | NAMEI_MODES | NAMEI_VERTICAL);
+ break;
+ case 'm':
+ flags |= NAMEI_MODES;
+ break;
+ case 'n':
+ flags |= NAMEI_NOLINKS;
+ break;
+ case 'o':
+ flags |= NAMEI_OWNERS;
+ break;
+ case 'x':
+ flags |= NAMEI_MNTS;
+ break;
+ case 'v':
+ flags |= NAMEI_VERTICAL;
+ break;
+ default:
+ usage(EXIT_FAILURE);
+ }
+ }
+
+ if (optind == argc) {
+ warnx(_("pathname argument is missing"));
+ usage(EXIT_FAILURE);
+ }
+
+ for(; optind < argc; optind++) {
+ char *path = argv[optind];
+ struct namei *nm = NULL;
+ struct stat st;
+
+ if (stat(path, &st) != 0)
+ rc = EXIT_FAILURE;
+
+ nm = add_namei(NULL, path, 0, NULL);
+ if (nm) {
+ int sml = 0;
+ if (!(flags & NAMEI_NOLINKS))
+ sml = follow_symlinks(nm);
+ if (print_namei(nm, path)) {
+ rc = EXIT_FAILURE;
+ continue;
+ }
+ free_namei(nm);
+ if (sml == -1) {
+ rc = EXIT_FAILURE;
+ warnx(_("%s: exceeded limit of symlinks"), path);
+ continue;
+ }
+ }
+ }
+
+ free_idcache(ucache);
+ free_idcache(gcache);
+
+ return rc;
+}
+
diff --git a/misc-utils/procs.c b/misc-utils/procs.c
new file mode 100644
index 0000000..e76c390
--- /dev/null
+++ b/misc-utils/procs.c
@@ -0,0 +1,126 @@
+/*
+ * procs.c -- functions to parse the linux /proc filesystem.
+ * (c) 1994 salvatore valente <svalente@mit.edu>
+ *
+ * this program is free software. you can redistribute it and
+ * modify it under the terms of the gnu general public license.
+ * there is no warranty.
+ *
+ * faith
+ * 1.2
+ * 1995/02/23 01:20:40
+ *
+ */
+
+#define _POSIX_SOURCE 1
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <unistd.h>
+#include "kill.h"
+#include "xalloc.h"
+
+extern char *mybasename (char *);
+static char *parse_parens (char *buf);
+
+int *
+get_pids (char *process_name, int get_all) {
+ DIR *dir;
+ struct dirent *ent;
+ int status;
+ char *dname, fname[100], *cp, buf[256];
+ struct stat st;
+ uid_t uid;
+ FILE *fp;
+ int pid, *pids, num_pids, pids_size;
+
+ dir = opendir ("/proc");
+ if (! dir) {
+ perror ("opendir /proc");
+ return NULL;
+ }
+ uid = getuid ();
+ pids = NULL;
+ num_pids = pids_size = 0;
+
+ while ((ent = readdir (dir)) != NULL) {
+ dname = ent->d_name;
+ if (! isdigit (*dname)) continue;
+ pid = atoi (dname);
+ sprintf (fname, "/proc/%d/cmdline", pid);
+ /* get the process owner */
+ status = stat (fname, &st);
+ if (status != 0) continue;
+ if (! get_all && uid != st.st_uid) continue;
+ /* get the command line */
+ fp = fopen (fname, "r");
+ if (! fp) continue;
+ cp = fgets (buf, sizeof (buf), fp);
+ fclose (fp);
+ /* an empty command line means the process is swapped out */
+ if (! cp || ! *cp) {
+ /* get the process name from the statfile */
+ sprintf (fname, "/proc/%d/stat", pid);
+ fp = fopen (fname, "r");
+ if (! fp) continue;
+ cp = fgets (buf, sizeof (buf), fp);
+ fclose (fp);
+ if (cp == NULL) continue;
+ cp = parse_parens (buf);
+ if (cp == NULL) continue;
+ }
+ /* ok, we got the process name. */
+ if (strcmp (process_name, mybasename (cp))) continue;
+ while (pids_size < num_pids + 2) {
+ pids_size += 5;
+ pids = (int *) xrealloc (pids, sizeof (int) * pids_size);
+ }
+ if (pids) {
+ pids[num_pids++] = pid;
+ pids[num_pids] = -1;
+ }
+ }
+ closedir (dir);
+ return pids;
+}
+
+/*
+ * parse_parens () -- return an index just past the first open paren in
+ * buf, and terminate the string at the matching close paren.
+ */
+static char *parse_parens (char *buf)
+{
+ char *cp, *ip;
+ int depth;
+
+ cp = strchr (buf, '(');
+ if (cp == NULL) return NULL;
+ cp++;
+ depth = 1;
+ for (ip = cp; *ip; ip++) {
+ if (*ip == '(')
+ depth++;
+ if (*ip == ')') {
+ depth--;
+ if (depth == 0) {
+ *ip = 0;
+ break;
+ }
+ }
+ }
+ return cp;
+}
+
+char *mybasename (char *path)
+{
+ char *cp;
+
+ cp = strrchr (path, '/');
+ return (cp ? cp + 1 : path);
+}
+
diff --git a/misc-utils/rename.1 b/misc-utils/rename.1
new file mode 100644
index 0000000..5f86b23
--- /dev/null
+++ b/misc-utils/rename.1
@@ -0,0 +1,60 @@
+.\" Written by Andries E. Brouwer (aeb@cwi.nl)
+.\" Placed in the public domain
+.\"
+.TH RENAME 1 "June 2011" "util-linux" "User Commands"
+.SH NAME
+rename \- rename files
+.SH SYNOPSIS
+.B rename
+.RI [ options ] " expression replacement file" ...
+.SH DESCRIPTION
+.B rename
+will rename the specified files by replacing the first occurrence of
+.I expression
+in their name by
+.IR replacement .
+.SH OPTIONS
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+Give visual feedback which files where renamed, if any.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Display version information and exit.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display help text and exit.
+.SH EXAMPLES
+Given the files
+.IR foo1 ", ..., " foo9 ", " foo10 ", ..., " foo278 ,
+the commands
+.RS
+.PP
+.nf
+rename foo foo0 foo?
+rename foo foo0 foo??
+.fi
+.PP
+.RE
+will turn them into
+.IR foo001 ", ..., " foo009 ", " foo010 ", ..., " foo278 .
+And
+.RS
+.PP
+.nf
+rename .htm .html *.htm
+.fi
+.PP
+.RE
+will fix the extension of your html files.
+.SH WARNING
+The renaming has no safeguards. If the user has permission to rewrite file names,
+the command will perform the action without any questions. For example, the
+result can be quite drastic when the command is run as root in the /lib
+directory. Always make a backup before running the command, unless you truly
+know what you are doing.
+.SH "SEE ALSO"
+.BR mmv (1),
+.BR mv (1)
+.SH AVAILABILITY
+The rename command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/misc-utils/rename.c b/misc-utils/rename.c
new file mode 100644
index 0000000..b17e03b
--- /dev/null
+++ b/misc-utils/rename.c
@@ -0,0 +1,126 @@
+/*
+ * rename.c - aeb 2000-01-01
+ *
+--------------------------------------------------------------
+#!/bin/sh
+if [ $# -le 2 ]; then echo call: rename from to files; exit; fi
+FROM="$1"
+TO="$2"
+shift
+shift
+for i in $@; do N=`echo "$i" | sed "s/$FROM/$TO/g"`; mv "$i" "$N"; done
+--------------------------------------------------------------
+ * This shell script will do renames of files, but may fail
+ * in cases involving special characters. Here a C version.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include "nls.h"
+#include "xalloc.h"
+#include "c.h"
+#include "closestream.h"
+
+static int do_rename(char *from, char *to, char *s, int verbose)
+{
+ char *newname, *where, *p, *q;
+ int flen, tlen, slen;
+
+ where = strstr(s, from);
+ if (where == NULL)
+ return 0;
+
+ flen = strlen(from);
+ tlen = strlen(to);
+ slen = strlen(s);
+ newname = xmalloc(tlen + slen + 1);
+
+ p = s;
+ q = newname;
+ while (p < where)
+ *q++ = *p++;
+ p = to;
+ while (*p)
+ *q++ = *p++;
+ p = where + flen;
+ while (*p)
+ *q++ = *p++;
+ *q = 0;
+
+ if (rename(s, newname) != 0)
+ err(EXIT_FAILURE, _("renaming %s to %s failed"),
+ s, newname);
+ if (verbose)
+ printf("`%s' -> `%s'\n", s, newname);
+
+ free(newname);
+ return 1;
+}
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fputs(_("\nUsage:\n"), out);
+ fprintf(out,
+ _(" %s [options] expression replacement file...\n"),
+ program_invocation_short_name);
+
+ fputs(_("\nOptions:\n"), out);
+ fputs(_(" -v, --verbose explain what is being done\n"
+ " -V, --version output version information and exit\n"
+ " -h, --help display this help and exit\n\n"), out);
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ char *from, *to;
+ int i, c, verbose = 0;
+
+ static const struct option longopts[] = {
+ {"verbose", no_argument, NULL, 'v'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while ((c = getopt_long(argc, argv, "vVh", longopts, NULL)) != -1)
+ switch (c) {
+ case 'v':
+ verbose = 1;
+ break;
+ case 'V':
+ printf(_("%s from %s\n"),
+ program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 3) {
+ warnx("not enough arguments");
+ usage(stderr);
+ }
+
+ from = argv[0];
+ to = argv[1];
+
+ for (i = 2; i < argc; i++)
+ do_rename(from, to, argv[i], verbose);
+
+ return EXIT_SUCCESS;
+}
diff --git a/misc-utils/sd-daemon.c b/misc-utils/sd-daemon.c
new file mode 100644
index 0000000..763e079
--- /dev/null
+++ b/misc-utils/sd-daemon.c
@@ -0,0 +1,530 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ Copyright 2010 Lennart Poettering
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+***/
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#ifdef __BIONIC__
+#include <linux/fcntl.h>
+#else
+#include <sys/fcntl.h>
+#endif
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <limits.h>
+
+#if defined(__linux__)
+#include <mqueue.h>
+#endif
+
+#include "sd-daemon.h"
+
+#if (__GNUC__ >= 4)
+#ifdef SD_EXPORT_SYMBOLS
+/* Export symbols */
+#define _sd_export_ __attribute__ ((visibility("default")))
+#else
+/* Don't export the symbols */
+#define _sd_export_ __attribute__ ((visibility("hidden")))
+#endif
+#else
+#define _sd_export_
+#endif
+
+_sd_export_ int sd_listen_fds(int unset_environment) {
+
+#if defined(DISABLE_SYSTEMD) || !defined(__linux__)
+ return 0;
+#else
+ int r, fd;
+ const char *e;
+ char *p = NULL;
+ unsigned long l;
+
+ if (!(e = getenv("LISTEN_PID"))) {
+ r = 0;
+ goto finish;
+ }
+
+ errno = 0;
+ l = strtoul(e, &p, 10);
+
+ if (errno != 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (!p || *p || l <= 0) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ /* Is this for us? */
+ if (getpid() != (pid_t) l) {
+ r = 0;
+ goto finish;
+ }
+
+ if (!(e = getenv("LISTEN_FDS"))) {
+ r = 0;
+ goto finish;
+ }
+
+ errno = 0;
+ l = strtoul(e, &p, 10);
+
+ if (errno != 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (!p || *p) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) {
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFD)) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (flags & FD_CLOEXEC)
+ continue;
+
+ if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
+ r = -errno;
+ goto finish;
+ }
+ }
+
+ r = (int) l;
+
+finish:
+ if (unset_environment) {
+ unsetenv("LISTEN_PID");
+ unsetenv("LISTEN_FDS");
+ }
+
+ return r;
+#endif
+}
+
+_sd_export_ int sd_is_fifo(int fd, const char *path) {
+ struct stat st_fd;
+
+ if (fd < 0)
+ return -EINVAL;
+
+ memset(&st_fd, 0, sizeof(st_fd));
+ if (fstat(fd, &st_fd) < 0)
+ return -errno;
+
+ if (!S_ISFIFO(st_fd.st_mode))
+ return 0;
+
+ if (path) {
+ struct stat st_path;
+
+ memset(&st_path, 0, sizeof(st_path));
+ if (stat(path, &st_path) < 0) {
+
+ if (errno == ENOENT || errno == ENOTDIR)
+ return 0;
+
+ return -errno;
+ }
+
+ return
+ st_path.st_dev == st_fd.st_dev &&
+ st_path.st_ino == st_fd.st_ino;
+ }
+
+ return 1;
+}
+
+_sd_export_ int sd_is_special(int fd, const char *path) {
+ struct stat st_fd;
+
+ if (fd < 0)
+ return -EINVAL;
+
+ if (fstat(fd, &st_fd) < 0)
+ return -errno;
+
+ if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode))
+ return 0;
+
+ if (path) {
+ struct stat st_path;
+
+ if (stat(path, &st_path) < 0) {
+
+ if (errno == ENOENT || errno == ENOTDIR)
+ return 0;
+
+ return -errno;
+ }
+
+ if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode))
+ return
+ st_path.st_dev == st_fd.st_dev &&
+ st_path.st_ino == st_fd.st_ino;
+ else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode))
+ return st_path.st_rdev == st_fd.st_rdev;
+ else
+ return 0;
+ }
+
+ return 1;
+}
+
+static int sd_is_socket_internal(int fd, int type, int listening) {
+ struct stat st_fd;
+
+ if (fd < 0 || type < 0)
+ return -EINVAL;
+
+ if (fstat(fd, &st_fd) < 0)
+ return -errno;
+
+ if (!S_ISSOCK(st_fd.st_mode))
+ return 0;
+
+ if (type != 0) {
+ int other_type = 0;
+ socklen_t l = sizeof(other_type);
+
+ if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
+ return -errno;
+
+ if (l != sizeof(other_type))
+ return -EINVAL;
+
+ if (other_type != type)
+ return 0;
+ }
+
+ if (listening >= 0) {
+ int accepting = 0;
+ socklen_t l = sizeof(accepting);
+
+ if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
+ return -errno;
+
+ if (l != sizeof(accepting))
+ return -EINVAL;
+
+ if (!accepting != !listening)
+ return 0;
+ }
+
+ return 1;
+}
+
+union sockaddr_union {
+ struct sockaddr sa;
+ struct sockaddr_in in4;
+ struct sockaddr_in6 in6;
+ struct sockaddr_un un;
+ struct sockaddr_storage storage;
+};
+
+_sd_export_ int sd_is_socket(int fd, int family, int type, int listening) {
+ int r;
+
+ if (family < 0)
+ return -EINVAL;
+
+ if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
+ return r;
+
+ if (family > 0) {
+ union sockaddr_union sockaddr;
+ socklen_t l;
+
+ memset(&sockaddr, 0, sizeof(sockaddr));
+ l = sizeof(sockaddr);
+
+ if (getsockname(fd, &sockaddr.sa, &l) < 0)
+ return -errno;
+
+ if (l < sizeof(sa_family_t))
+ return -EINVAL;
+
+ return sockaddr.sa.sa_family == family;
+ }
+
+ return 1;
+}
+
+_sd_export_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
+ union sockaddr_union sockaddr;
+ socklen_t l;
+ int r;
+
+ if (family != 0 && family != AF_INET && family != AF_INET6)
+ return -EINVAL;
+
+ if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
+ return r;
+
+ memset(&sockaddr, 0, sizeof(sockaddr));
+ l = sizeof(sockaddr);
+
+ if (getsockname(fd, &sockaddr.sa, &l) < 0)
+ return -errno;
+
+ if (l < sizeof(sa_family_t))
+ return -EINVAL;
+
+ if (sockaddr.sa.sa_family != AF_INET &&
+ sockaddr.sa.sa_family != AF_INET6)
+ return 0;
+
+ if (family > 0)
+ if (sockaddr.sa.sa_family != family)
+ return 0;
+
+ if (port > 0) {
+ if (sockaddr.sa.sa_family == AF_INET) {
+ if (l < sizeof(struct sockaddr_in))
+ return -EINVAL;
+
+ return htons(port) == sockaddr.in4.sin_port;
+ } else {
+ if (l < sizeof(struct sockaddr_in6))
+ return -EINVAL;
+
+ return htons(port) == sockaddr.in6.sin6_port;
+ }
+ }
+
+ return 1;
+}
+
+_sd_export_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
+ union sockaddr_union sockaddr;
+ socklen_t l;
+ int r;
+
+ if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
+ return r;
+
+ memset(&sockaddr, 0, sizeof(sockaddr));
+ l = sizeof(sockaddr);
+
+ if (getsockname(fd, &sockaddr.sa, &l) < 0)
+ return -errno;
+
+ if (l < sizeof(sa_family_t))
+ return -EINVAL;
+
+ if (sockaddr.sa.sa_family != AF_UNIX)
+ return 0;
+
+ if (path) {
+ if (length <= 0)
+ length = strlen(path);
+
+ if (length <= 0)
+ /* Unnamed socket */
+ return l == offsetof(struct sockaddr_un, sun_path);
+
+ if (path[0])
+ /* Normal path socket */
+ return
+ (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
+ memcmp(path, sockaddr.un.sun_path, length+1) == 0;
+ else
+ /* Abstract namespace socket */
+ return
+ (l == offsetof(struct sockaddr_un, sun_path) + length) &&
+ memcmp(path, sockaddr.un.sun_path, length) == 0;
+ }
+
+ return 1;
+}
+
+_sd_export_ int sd_is_mq(int fd, const char *path) {
+#if !defined(__linux__)
+ return 0;
+#else
+ struct mq_attr attr;
+
+ if (fd < 0)
+ return -EINVAL;
+
+ if (mq_getattr(fd, &attr) < 0)
+ return -errno;
+
+ if (path) {
+ char fpath[PATH_MAX];
+ struct stat a, b;
+
+ if (path[0] != '/')
+ return -EINVAL;
+
+ if (fstat(fd, &a) < 0)
+ return -errno;
+
+ strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12);
+ fpath[sizeof(fpath)-1] = 0;
+
+ if (stat(fpath, &b) < 0)
+ return -errno;
+
+ if (a.st_dev != b.st_dev ||
+ a.st_ino != b.st_ino)
+ return 0;
+ }
+
+ return 1;
+#endif
+}
+
+_sd_export_ int sd_notify(int unset_environment, const char *state) {
+#if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
+ return 0;
+#else
+ int fd = -1, r;
+ struct msghdr msghdr;
+ struct iovec iovec;
+ union sockaddr_union sockaddr;
+ const char *e;
+
+ if (!state) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ if (!(e = getenv("NOTIFY_SOCKET")))
+ return 0;
+
+ /* Must be an abstract socket, or an absolute path */
+ if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ memset(&sockaddr, 0, sizeof(sockaddr));
+ sockaddr.sa.sa_family = AF_UNIX;
+ strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
+
+ if (sockaddr.un.sun_path[0] == '@')
+ sockaddr.un.sun_path[0] = 0;
+
+ memset(&iovec, 0, sizeof(iovec));
+ iovec.iov_base = (char*) state;
+ iovec.iov_len = strlen(state);
+
+ memset(&msghdr, 0, sizeof(msghdr));
+ msghdr.msg_name = &sockaddr;
+ msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
+
+ if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
+ msghdr.msg_namelen = sizeof(struct sockaddr_un);
+
+ msghdr.msg_iov = &iovec;
+ msghdr.msg_iovlen = 1;
+
+ if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ r = 1;
+
+finish:
+ if (unset_environment)
+ unsetenv("NOTIFY_SOCKET");
+
+ if (fd >= 0)
+ close(fd);
+
+ return r;
+#endif
+}
+
+_sd_export_ int sd_notifyf(int unset_environment, const char *format, ...) {
+#if defined(DISABLE_SYSTEMD) || !defined(__linux__)
+ return 0;
+#else
+ va_list ap;
+ char *p = NULL;
+ int r;
+
+ va_start(ap, format);
+ r = vasprintf(&p, format, ap);
+ va_end(ap);
+
+ if (r < 0 || !p)
+ return -ENOMEM;
+
+ r = sd_notify(unset_environment, p);
+ free(p);
+
+ return r;
+#endif
+}
+
+_sd_export_ int sd_booted(void) {
+#if defined(DISABLE_SYSTEMD) || !defined(__linux__)
+ return 0;
+#else
+
+ struct stat a, b;
+
+ /* We simply test whether the systemd cgroup hierarchy is
+ * mounted */
+
+ if (lstat("/sys/fs/cgroup", &a) < 0)
+ return 0;
+
+ if (lstat("/sys/fs/cgroup/systemd", &b) < 0)
+ return 0;
+
+ return a.st_dev != b.st_dev;
+#endif
+}
diff --git a/misc-utils/sd-daemon.h b/misc-utils/sd-daemon.h
new file mode 100644
index 0000000..fe51159
--- /dev/null
+++ b/misc-utils/sd-daemon.h
@@ -0,0 +1,282 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosddaemonhfoo
+#define foosddaemonhfoo
+
+/***
+ Copyright 2010 Lennart Poettering
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+***/
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ Reference implementation of a few systemd related interfaces for
+ writing daemons. These interfaces are trivial to implement. To
+ simplify porting we provide this reference implementation.
+ Applications are welcome to reimplement the algorithms described
+ here if they do not want to include these two source files.
+
+ The following functionality is provided:
+
+ - Support for logging with log levels on stderr
+ - File descriptor passing for socket-based activation
+ - Daemon startup and status notification
+ - Detection of systemd boots
+
+ You may compile this with -DDISABLE_SYSTEMD to disable systemd
+ support. This makes all those calls NOPs that are directly related to
+ systemd (i.e. only sd_is_xxx() will stay useful).
+
+ Since this is drop-in code we don't want any of our symbols to be
+ exported in any case. Hence we declare hidden visibility for all of
+ them.
+
+ You may find an up-to-date version of these source files online:
+
+ http://cgit.freedesktop.org/systemd/systemd/plain/src/systemd/sd-daemon.h
+ http://cgit.freedesktop.org/systemd/systemd/plain/src/sd-daemon.c
+
+ This should compile on non-Linux systems, too, but with the
+ exception of the sd_is_xxx() calls all functions will become NOPs.
+
+ See sd-daemon(7) for more information.
+*/
+
+#ifndef _sd_printf_attr_
+#if __GNUC__ >= 4
+#define _sd_printf_attr_(a,b) __attribute__ ((format (printf, a, b)))
+#else
+#define _sd_printf_attr_(a,b)
+#endif
+#endif
+
+/*
+ Log levels for usage on stderr:
+
+ fprintf(stderr, SD_NOTICE "Hello World!\n");
+
+ This is similar to printk() usage in the kernel.
+*/
+#define SD_EMERG "<0>" /* system is unusable */
+#define SD_ALERT "<1>" /* action must be taken immediately */
+#define SD_CRIT "<2>" /* critical conditions */
+#define SD_ERR "<3>" /* error conditions */
+#define SD_WARNING "<4>" /* warning conditions */
+#define SD_NOTICE "<5>" /* normal but significant condition */
+#define SD_INFO "<6>" /* informational */
+#define SD_DEBUG "<7>" /* debug-level messages */
+
+/* The first passed file descriptor is fd 3 */
+#define SD_LISTEN_FDS_START 3
+
+/*
+ Returns how many file descriptors have been passed, or a negative
+ errno code on failure. Optionally, removes the $LISTEN_FDS and
+ $LISTEN_PID file descriptors from the environment (recommended, but
+ problematic in threaded environments). If r is the return value of
+ this function you'll find the file descriptors passed as fds
+ SD_LISTEN_FDS_START to SD_LISTEN_FDS_START+r-1. Returns a negative
+ errno style error code on failure. This function call ensures that
+ the FD_CLOEXEC flag is set for the passed file descriptors, to make
+ sure they are not passed on to child processes. If FD_CLOEXEC shall
+ not be set, the caller needs to unset it after this call for all file
+ descriptors that are used.
+
+ See sd_listen_fds(3) for more information.
+*/
+int sd_listen_fds(int unset_environment);
+
+/*
+ Helper call for identifying a passed file descriptor. Returns 1 if
+ the file descriptor is a FIFO in the file system stored under the
+ specified path, 0 otherwise. If path is NULL a path name check will
+ not be done and the call only verifies if the file descriptor
+ refers to a FIFO. Returns a negative errno style error code on
+ failure.
+
+ See sd_is_fifo(3) for more information.
+*/
+int sd_is_fifo(int fd, const char *path);
+
+/*
+ Helper call for identifying a passed file descriptor. Returns 1 if
+ the file descriptor is a special character device on the file
+ system stored under the specified path, 0 otherwise.
+ If path is NULL a path name check will not be done and the call
+ only verifies if the file descriptor refers to a special character.
+ Returns a negative errno style error code on failure.
+
+ See sd_is_special(3) for more information.
+*/
+int sd_is_special(int fd, const char *path);
+
+/*
+ Helper call for identifying a passed file descriptor. Returns 1 if
+ the file descriptor is a socket of the specified family (AF_INET,
+ ...) and type (SOCK_DGRAM, SOCK_STREAM, ...), 0 otherwise. If
+ family is 0 a socket family check will not be done. If type is 0 a
+ socket type check will not be done and the call only verifies if
+ the file descriptor refers to a socket. If listening is > 0 it is
+ verified that the socket is in listening mode. (i.e. listen() has
+ been called) If listening is == 0 it is verified that the socket is
+ not in listening mode. If listening is < 0 no listening mode check
+ is done. Returns a negative errno style error code on failure.
+
+ See sd_is_socket(3) for more information.
+*/
+int sd_is_socket(int fd, int family, int type, int listening);
+
+/*
+ Helper call for identifying a passed file descriptor. Returns 1 if
+ the file descriptor is an Internet socket, of the specified family
+ (either AF_INET or AF_INET6) and the specified type (SOCK_DGRAM,
+ SOCK_STREAM, ...), 0 otherwise. If version is 0 a protocol version
+ check is not done. If type is 0 a socket type check will not be
+ done. If port is 0 a socket port check will not be done. The
+ listening flag is used the same way as in sd_is_socket(). Returns a
+ negative errno style error code on failure.
+
+ See sd_is_socket_inet(3) for more information.
+*/
+int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port);
+
+/*
+ Helper call for identifying a passed file descriptor. Returns 1 if
+ the file descriptor is an AF_UNIX socket of the specified type
+ (SOCK_DGRAM, SOCK_STREAM, ...) and path, 0 otherwise. If type is 0
+ a socket type check will not be done. If path is NULL a socket path
+ check will not be done. For normal AF_UNIX sockets set length to
+ 0. For abstract namespace sockets set length to the length of the
+ socket name (including the initial 0 byte), and pass the full
+ socket path in path (including the initial 0 byte). The listening
+ flag is used the same way as in sd_is_socket(). Returns a negative
+ errno style error code on failure.
+
+ See sd_is_socket_unix(3) for more information.
+*/
+int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length);
+
+/*
+ Helper call for identifying a passed file descriptor. Returns 1 if
+ the file descriptor is a POSIX Message Queue of the specified name,
+ 0 otherwise. If path is NULL a message queue name check is not
+ done. Returns a negative errno style error code on failure.
+*/
+int sd_is_mq(int fd, const char *path);
+
+/*
+ Informs systemd about changed daemon state. This takes a number of
+ newline separated environment-style variable assignments in a
+ string. The following variables are known:
+
+ READY=1 Tells systemd that daemon startup is finished (only
+ relevant for services of Type=notify). The passed
+ argument is a boolean "1" or "0". Since there is
+ little value in signaling non-readiness the only
+ value daemons should send is "READY=1".
+
+ STATUS=... Passes a single-line status string back to systemd
+ that describes the daemon state. This is free-from
+ and can be used for various purposes: general state
+ feedback, fsck-like programs could pass completion
+ percentages and failing programs could pass a human
+ readable error message. Example: "STATUS=Completed
+ 66% of file system check..."
+
+ ERRNO=... If a daemon fails, the errno-style error code,
+ formatted as string. Example: "ERRNO=2" for ENOENT.
+
+ BUSERROR=... If a daemon fails, the D-Bus error-style error
+ code. Example: "BUSERROR=org.freedesktop.DBus.Error.TimedOut"
+
+ MAINPID=... The main pid of a daemon, in case systemd did not
+ fork off the process itself. Example: "MAINPID=4711"
+
+ WATCHDOG=1 Tells systemd to update the watchdog timestamp.
+ Services using this feature should do this in
+ regular intervals. A watchdog framework can use the
+ timestamps to detect failed services.
+
+ Daemons can choose to send additional variables. However, it is
+ recommended to prefix variable names not listed above with X_.
+
+ Returns a negative errno-style error code on failure. Returns > 0
+ if systemd could be notified, 0 if it couldn't possibly because
+ systemd is not running.
+
+ Example: When a daemon finished starting up, it could issue this
+ call to notify systemd about it:
+
+ sd_notify(0, "READY=1");
+
+ See sd_notifyf() for more complete examples.
+
+ See sd_notify(3) for more information.
+*/
+int sd_notify(int unset_environment, const char *state);
+
+/*
+ Similar to sd_notify() but takes a format string.
+
+ Example 1: A daemon could send the following after initialization:
+
+ sd_notifyf(0, "READY=1\n"
+ "STATUS=Processing requests...\n"
+ "MAINPID=%lu",
+ (unsigned long) getpid());
+
+ Example 2: A daemon could send the following shortly before
+ exiting, on failure:
+
+ sd_notifyf(0, "STATUS=Failed to start up: %s\n"
+ "ERRNO=%i",
+ strerror(errno),
+ errno);
+
+ See sd_notifyf(3) for more information.
+*/
+int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_attr_(2,3);
+
+/*
+ Returns > 0 if the system was booted with systemd. Returns < 0 on
+ error. Returns 0 if the system was not booted with systemd. Note
+ that all of the functions above handle non-systemd boots just
+ fine. You should NOT protect them with a call to this function. Also
+ note that this function checks whether the system, not the user
+ session is controlled by systemd. However the functions above work
+ for both user and system services.
+
+ See sd_booted(3) for more information.
+*/
+int sd_booted(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/misc-utils/uuidd.8 b/misc-utils/uuidd.8
new file mode 100644
index 0000000..39f31cb
--- /dev/null
+++ b/misc-utils/uuidd.8
@@ -0,0 +1,94 @@
+.\" -*- nroff -*-
+.\" Copyright 2007 by Theodore Ts'o. All Rights Reserved.
+.\" This file may be copied under the terms of the GNU Public License.
+.\"
+.TH UUIDD 8 "June 2011" "util-linux" "System Administration"
+.SH NAME
+uuidd \- UUID generation daemon
+.SH SYNOPSIS
+.B uuidd
+.RI [ options ]
+.SH DESCRIPTION
+The
+.B uuidd
+daemon is used by the UUID library to generate
+universally unique identifiers (UUIDs), especially time-based UUIDs,
+in a secure and guaranteed-unique fashion, even in the face of large
+numbers of threads running on different CPUs trying to grab UUIDs.
+.SH OPTIONS
+.TP
+.B \-d
+Run uuidd in debugging mode. This prevents uuidd from running as a daemon.
+.TP
+.BR \-h , " \-\-help "
+Display help screen and exit.
+.TP
+.BR \-k , " \-\-kill "
+If currently a uuidd daemon is running, kill it.
+.TP
+.BR \-n , " \-\-uuids " \fInumber\fR
+When issuing a test request to a running uuidd, request a bulk response
+of
+.I number
+UUIDs.
+.TP
+.BR \-p , " \-\-pid " \fIpath\fR
+Specify the pathname where the pid file should be written. By default,
+the pid file is written to /run/uuidd/uuidd.pid.
+.TP
+.BR \-P , " \-\-no-pid "
+Do not create pid file.
+.TP
+.BR \-F , " \-\-no-fork "
+Do not daemonize using double-fork.
+.TP
+.BR \-S , " \-\-socket-activation "
+Do not create the socket and instead expect it to be provided by the calling
+process. Implies --no-fork and --no-pid. As of this writing, this option is
+supposed to be used only with systemd. This option must be enabled with a configure
+option.
+.TP
+.B \-q
+Suppress some failure messages.
+.TP
+.BR \-r , " \-\-random "
+Test uuidd by trying to connect to a running uuidd daemon and
+request it to return a random-based UUID.
+.TP
+.BR \-s , " \-\-socket " \fIpath\fR
+Specify the pathname used for the unix-domain socket used by uuidd. By
+default, the pathname used is /run/uuidd/request. This is primarily
+for debugging purposes, since the pathname is hard-coded in the libuuid
+library.
+.TP
+.BR \-T , " \-\-timeout " \fItimeout\fR
+Specify a timeout for uuidd. If specified, then uuidd will exit after
+.I timeout
+seconds of inactivity.
+.TP
+.BR \-t , " \-\-time "
+Test uuidd by trying to connect to a running uuidd daemon and
+request it to return a time-based UUID.
+.TP
+.BR \-V , " \-\-version "
+Output version information and exit.
+.SH EXAMPLE
+Start up a daemon, print 42 random keys, and then stop the daemon.
+.PP
+.RS
+.nf
+uuidd -p /tmp/uuidd.pid -s /tmp/uuidd.socket
+uuidd -d -r -n 42 -s /tmp/uuidd.socket
+uuidd -d -k -s /tmp/uuidd.socket
+.nf
+.RE
+.SH AUTHOR
+The
+.B uuidd
+daemon was written by Theodore Ts'o <tytso@mit.edu>.
+.SH AVAILABILITY
+The uuidd daemon is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuidgen (1)
diff --git a/misc-utils/uuidd.8.in b/misc-utils/uuidd.8.in
new file mode 100644
index 0000000..f35a41e
--- /dev/null
+++ b/misc-utils/uuidd.8.in
@@ -0,0 +1,94 @@
+.\" -*- nroff -*-
+.\" Copyright 2007 by Theodore Ts'o. All Rights Reserved.
+.\" This file may be copied under the terms of the GNU Public License.
+.\"
+.TH UUIDD 8 "June 2011" "util-linux" "System Administration"
+.SH NAME
+uuidd \- UUID generation daemon
+.SH SYNOPSIS
+.B uuidd
+.RI [ options ]
+.SH DESCRIPTION
+The
+.B uuidd
+daemon is used by the UUID library to generate
+universally unique identifiers (UUIDs), especially time-based UUIDs,
+in a secure and guaranteed-unique fashion, even in the face of large
+numbers of threads running on different CPUs trying to grab UUIDs.
+.SH OPTIONS
+.TP
+.B \-d
+Run uuidd in debugging mode. This prevents uuidd from running as a daemon.
+.TP
+.BR \-h , " \-\-help "
+Display help screen and exit.
+.TP
+.BR \-k , " \-\-kill "
+If currently a uuidd daemon is running, kill it.
+.TP
+.BR \-n , " \-\-uuids " \fInumber\fR
+When issuing a test request to a running uuidd, request a bulk response
+of
+.I number
+UUIDs.
+.TP
+.BR \-p , " \-\-pid " \fIpath\fR
+Specify the pathname where the pid file should be written. By default,
+the pid file is written to @localstatedir@/uuidd/uuidd.pid.
+.TP
+.BR \-P , " \-\-no-pid "
+Do not create pid file.
+.TP
+.BR \-F , " \-\-no-fork "
+Do not daemonize using double-fork.
+.TP
+.BR \-S , " \-\-socket-activation "
+Do not create the socket and instead expect it to be provided by the calling
+process. Implies --no-fork and --no-pid. As of this writing, this option is
+supposed to be used only with systemd. This option must be enabled with a configure
+option.
+.TP
+.B \-q
+Suppress some failure messages.
+.TP
+.BR \-r , " \-\-random "
+Test uuidd by trying to connect to a running uuidd daemon and
+request it to return a random-based UUID.
+.TP
+.BR \-s , " \-\-socket " \fIpath\fR
+Specify the pathname used for the unix-domain socket used by uuidd. By
+default, the pathname used is @localstatedir@/uuidd/request. This is primarily
+for debugging purposes, since the pathname is hard-coded in the libuuid
+library.
+.TP
+.BR \-T , " \-\-timeout " \fItimeout\fR
+Specify a timeout for uuidd. If specified, then uuidd will exit after
+.I timeout
+seconds of inactivity.
+.TP
+.BR \-t , " \-\-time "
+Test uuidd by trying to connect to a running uuidd daemon and
+request it to return a time-based UUID.
+.TP
+.BR \-V , " \-\-version "
+Output version information and exit.
+.SH EXAMPLE
+Start up a daemon, print 42 random keys, and then stop the daemon.
+.PP
+.RS
+.nf
+uuidd -p /tmp/uuidd.pid -s /tmp/uuidd.socket
+uuidd -d -r -n 42 -s /tmp/uuidd.socket
+uuidd -d -k -s /tmp/uuidd.socket
+.nf
+.RE
+.SH AUTHOR
+The
+.B uuidd
+daemon was written by Theodore Ts'o <tytso@mit.edu>.
+.SH AVAILABILITY
+The uuidd daemon is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuidgen (1)
diff --git a/misc-utils/uuidd.c b/misc-utils/uuidd.c
new file mode 100644
index 0000000..3dde49e
--- /dev/null
+++ b/misc-utils/uuidd.c
@@ -0,0 +1,669 @@
+/*
+ * uuidd.c --- UUID-generation daemon
+ *
+ * Copyright (C) 2007 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <unistd.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+extern int getopt(int argc, char * const argv[], const char *optstring);
+extern char *optarg;
+extern int optind;
+#endif
+
+#include "uuid.h"
+#include "uuidd.h"
+#include "all-io.h"
+#include "c.h"
+#include "closestream.h"
+
+#ifdef USE_SOCKET_ACTIVATION
+#include "sd-daemon.h"
+#endif
+
+#include "nls.h"
+
+#ifdef __GNUC__
+#define CODE_ATTR(x) __attribute__(x)
+#else
+#define CODE_ATTR(x)
+#endif
+
+/* length of textual representation of UUID, including trailing \0 */
+#define UUID_STR_LEN 37
+
+/* length of binary representation of UUID */
+#define UUID_LEN (sizeof(uuid_t))
+
+/* server loop control structure */
+struct uuidd_cxt_t {
+ int timeout;
+ unsigned int debug: 1,
+ quiet: 1,
+ no_fork: 1,
+ no_sock: 1;
+};
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fputs(_("\nUsage:\n"), out);
+ fprintf(out,
+ _(" %s [options]\n"), program_invocation_short_name);
+
+ fputs(_("\nOptions:\n"), out);
+ fputs(_(" -p, --pid <path> path to pid file\n"
+ " -s, --socket <path> path to socket\n"
+ " -T, --timeout <sec> specify inactivity timeout\n"
+ " -k, --kill kill running daemon\n"
+ " -r, --random test random-based generation\n"
+ " -t, --time test time-based generation\n"
+ " -n, --uuids <num> request number of uuids\n"
+ " -P, --no-pid do not create pid file\n"
+ " -F, --no-fork do not daemonize using double-fork\n"
+ " -S, --socket-activation do not create listening socket\n"
+ " -d, --debug run in debugging mode\n"
+ " -q, --quiet turn on quiet mode\n"
+ " -V, --version output version information and exit\n"
+ " -h, --help display this help and exit\n\n"), out);
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static void create_daemon(void)
+{
+ uid_t euid;
+
+ if (daemon(0, 0))
+ err(EXIT_FAILURE, "daemon");
+
+ euid = geteuid();
+ if (setreuid(euid, euid) < 0)
+ err(EXIT_FAILURE, "setreuid");
+}
+
+static const char *cleanup_pidfile, *cleanup_socket;
+
+static void terminate_intr(int signo CODE_ATTR((unused)))
+{
+ if (cleanup_pidfile)
+ unlink(cleanup_pidfile);
+ if (cleanup_socket)
+ unlink(cleanup_socket);
+ exit(EXIT_SUCCESS);
+}
+
+static int call_daemon(const char *socket_path, int op, char *buf,
+ size_t buflen, int *num, const char **err_context)
+{
+ char op_buf[8];
+ int op_len;
+ int s;
+ ssize_t ret;
+ int32_t reply_len = 0;
+ struct sockaddr_un srv_addr;
+
+ if (((op == UUIDD_OP_BULK_TIME_UUID) ||
+ (op == UUIDD_OP_BULK_RANDOM_UUID)) && !num) {
+ if (err_context)
+ *err_context = _("bad arguments");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ if (err_context)
+ *err_context = _("socket");
+ return -1;
+ }
+
+ srv_addr.sun_family = AF_UNIX;
+ strncpy(srv_addr.sun_path, socket_path, sizeof(srv_addr.sun_path));
+ srv_addr.sun_path[sizeof(srv_addr.sun_path) - 1] = '\0';
+
+ if (connect(s, (const struct sockaddr *) &srv_addr,
+ sizeof(struct sockaddr_un)) < 0) {
+ if (err_context)
+ *err_context = _("connect");
+ close(s);
+ return -1;
+ }
+
+ if (op == UUIDD_OP_BULK_RANDOM_UUID) {
+ if ((*num) * UUID_LEN > buflen - 4)
+ *num = (buflen - 4) / UUID_LEN;
+ }
+ op_buf[0] = op;
+ op_len = 1;
+ if ((op == UUIDD_OP_BULK_TIME_UUID) ||
+ (op == UUIDD_OP_BULK_RANDOM_UUID)) {
+ memcpy(op_buf + 1, num, sizeof(int));
+ op_len += sizeof(int);
+ }
+
+ ret = write_all(s, op_buf, op_len);
+ if (ret < 0) {
+ if (err_context)
+ *err_context = _("write");
+ close(s);
+ return -1;
+ }
+
+ ret = read_all(s, (char *) &reply_len, sizeof(reply_len));
+ if (ret < 0) {
+ if (err_context)
+ *err_context = _("read count");
+ close(s);
+ return -1;
+ }
+ if (reply_len < 0 || (size_t) reply_len > buflen) {
+ if (err_context)
+ *err_context = _("bad response length");
+ close(s);
+ return -1;
+ }
+ ret = read_all(s, (char *) buf, reply_len);
+
+ if ((ret > 0) && (op == UUIDD_OP_BULK_TIME_UUID)) {
+ if (reply_len >= (int) (UUID_LEN + sizeof(int)))
+ memcpy(buf + UUID_LEN, num, sizeof(int));
+ else
+ *num = -1;
+ }
+ if ((ret > 0) && (op == UUIDD_OP_BULK_RANDOM_UUID)) {
+ if (reply_len >= (int) sizeof(int))
+ memcpy(buf, num, sizeof(int));
+ else
+ *num = -1;
+ }
+
+ close(s);
+
+ return ret;
+}
+
+/*
+ * Exclusively create and open a pid file with path @pidfile_path
+ *
+ * Set cleanup_pidfile global variable for the cleanup
+ * handler. @pidfile_path must not be NULL.
+ *
+ * Return file descriptor of the created pid_file.
+ */
+static int create_pidfile(const char *pidfile_path, int quiet)
+{
+ int fd_pidfile;
+ struct flock fl;
+
+ fd_pidfile = open(pidfile_path, O_CREAT | O_RDWR, 0664);
+ if (fd_pidfile < 0) {
+ if (!quiet)
+ fprintf(stderr, _("Failed to open/create %s: %m\n"),
+ pidfile_path);
+ exit(EXIT_FAILURE);
+ }
+ cleanup_pidfile = pidfile_path;
+
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_pid = 0;
+ while (fcntl(fd_pidfile, F_SETLKW, &fl) < 0) {
+ if ((errno == EAGAIN) || (errno == EINTR))
+ continue;
+ if (!quiet)
+ fprintf(stderr, _("Failed to lock %s: %m\n"), pidfile_path);
+ exit(EXIT_FAILURE);
+ }
+
+ return fd_pidfile;
+}
+
+/*
+ * Create AF_UNIX, SOCK_STREAM socket and bind to @socket_path
+ *
+ * If @will_fork is true, then make sure the descriptor
+ * of the socket is >2, so that it wont be later closed
+ * during create_daemon().
+ *
+ * Return file descriptor corresponding to created socket.
+ */
+static int create_socket(const char *socket_path, int will_fork, int quiet)
+{
+ struct sockaddr_un my_addr;
+ mode_t save_umask;
+ int s;
+
+ if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ if (!quiet)
+ fprintf(stderr, _("Couldn't create unix stream socket: %m"));
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Make sure the socket isn't using fd numbers 0-2 to avoid it
+ * getting closed by create_daemon()
+ */
+ while (will_fork && s <= 2) {
+ s = dup(s);
+ if (s < 0)
+ err(EXIT_FAILURE, "dup");
+ }
+
+ /*
+ * Create the address we will be binding to.
+ */
+ my_addr.sun_family = AF_UNIX;
+ strncpy(my_addr.sun_path, socket_path, sizeof(my_addr.sun_path));
+ my_addr.sun_path[sizeof(my_addr.sun_path) - 1] = '\0';
+ unlink(socket_path);
+ save_umask = umask(0);
+ if (bind(s, (const struct sockaddr *) &my_addr,
+ sizeof(struct sockaddr_un)) < 0) {
+ if (!quiet)
+ fprintf(stderr,
+ _("Couldn't bind unix socket %s: %m\n"), socket_path);
+ exit(EXIT_FAILURE);
+ }
+ umask(save_umask);
+ cleanup_socket = socket_path;
+
+ return s;
+}
+
+static void server_loop(const char *socket_path, const char *pidfile_path,
+ const struct uuidd_cxt_t *uuidd_cxt)
+{
+ struct sockaddr_un from_addr;
+ socklen_t fromlen;
+ int32_t reply_len = 0;
+ uuid_t uu;
+ char reply_buf[1024], *cp;
+ char op, str[UUID_STR_LEN];
+ int i, ns, len, num;
+ int s = 0;
+ int fd_pidfile = -1;
+ int ret;
+
+#ifdef USE_SOCKET_ACTIVATION
+ if (!uuidd_cxt->no_sock) /* no_sock implies no_fork and no_pid */
+#endif
+ {
+
+ signal(SIGALRM, terminate_intr);
+ alarm(30);
+ if (pidfile_path)
+ fd_pidfile = create_pidfile(pidfile_path, uuidd_cxt->quiet);
+
+ ret = call_daemon(socket_path, UUIDD_OP_GETPID, reply_buf,
+ sizeof(reply_buf), 0, NULL);
+ if (ret > 0) {
+ if (!uuidd_cxt->quiet)
+ fprintf(stderr,
+ _("uuidd daemon already running at pid %s\n"),
+ reply_buf);
+ exit(EXIT_FAILURE);
+ }
+ alarm(0);
+
+ s = create_socket(socket_path,
+ (!uuidd_cxt->debug || !uuidd_cxt->no_fork),
+ uuidd_cxt->quiet);
+ if (listen(s, SOMAXCONN) < 0) {
+ if (!uuidd_cxt->quiet)
+ fprintf(stderr, _("Couldn't listen on unix "
+ "socket %s: %m\n"), socket_path);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!uuidd_cxt->debug && !uuidd_cxt->no_fork)
+ create_daemon();
+
+ if (pidfile_path) {
+ sprintf(reply_buf, "%8d\n", getpid());
+ ignore_result( ftruncate(fd_pidfile, 0) );
+ write_all(fd_pidfile, reply_buf, strlen(reply_buf));
+ if (fd_pidfile > 1)
+ close(fd_pidfile); /* Unlock the pid file */
+ }
+
+ }
+
+ signal(SIGHUP, terminate_intr);
+ signal(SIGINT, terminate_intr);
+ signal(SIGTERM, terminate_intr);
+ signal(SIGALRM, terminate_intr);
+ signal(SIGPIPE, SIG_IGN);
+
+#ifdef USE_SOCKET_ACTIVATION
+ if (uuidd_cxt->no_sock) {
+ if (sd_listen_fds(0) != 1) {
+ fprintf(stderr, _("No or too many file descriptors received.\n"));
+ exit(EXIT_FAILURE);
+ }
+
+ s = SD_LISTEN_FDS_START + 0;
+ }
+#endif
+
+ while (1) {
+ fromlen = sizeof(from_addr);
+ if (uuidd_cxt->timeout > 0)
+ alarm(uuidd_cxt->timeout);
+ ns = accept(s, (struct sockaddr *) &from_addr, &fromlen);
+ alarm(0);
+ if (ns < 0) {
+ if ((errno == EAGAIN) || (errno == EINTR))
+ continue;
+ else
+ err(EXIT_FAILURE, "accept");
+ }
+ len = read(ns, &op, 1);
+ if (len != 1) {
+ if (len < 0)
+ perror("read");
+ else
+ fprintf(stderr, _("Error reading from client, "
+ "len = %d\n"), len);
+ goto shutdown_socket;
+ }
+ if ((op == UUIDD_OP_BULK_TIME_UUID) ||
+ (op == UUIDD_OP_BULK_RANDOM_UUID)) {
+ if (read_all(ns, (char *) &num, sizeof(num)) != 4)
+ goto shutdown_socket;
+ if (uuidd_cxt->debug)
+ fprintf(stderr, _("operation %d, incoming num = %d\n"),
+ op, num);
+ } else if (uuidd_cxt->debug)
+ fprintf(stderr, _("operation %d\n"), op);
+
+ switch (op) {
+ case UUIDD_OP_GETPID:
+ sprintf(reply_buf, "%d", getpid());
+ reply_len = strlen(reply_buf) + 1;
+ break;
+ case UUIDD_OP_GET_MAXOP:
+ sprintf(reply_buf, "%d", UUIDD_MAX_OP);
+ reply_len = strlen(reply_buf) + 1;
+ break;
+ case UUIDD_OP_TIME_UUID:
+ num = 1;
+ __uuid_generate_time(uu, &num);
+ if (uuidd_cxt->debug) {
+ uuid_unparse(uu, str);
+ fprintf(stderr, _("Generated time UUID: %s\n"), str);
+ }
+ memcpy(reply_buf, uu, sizeof(uu));
+ reply_len = sizeof(uu);
+ break;
+ case UUIDD_OP_RANDOM_UUID:
+ num = 1;
+ __uuid_generate_random(uu, &num);
+ if (uuidd_cxt->debug) {
+ uuid_unparse(uu, str);
+ fprintf(stderr, _("Generated random UUID: %s\n"), str);
+ }
+ memcpy(reply_buf, uu, sizeof(uu));
+ reply_len = sizeof(uu);
+ break;
+ case UUIDD_OP_BULK_TIME_UUID:
+ __uuid_generate_time(uu, &num);
+ if (uuidd_cxt->debug) {
+ uuid_unparse(uu, str);
+ fprintf(stderr, P_("Generated time UUID %s "
+ "and %d following\n",
+ "Generated time UUID %s "
+ "and %d following\n", num - 1),
+ str, num - 1);
+ }
+ memcpy(reply_buf, uu, sizeof(uu));
+ reply_len = sizeof(uu);
+ memcpy(reply_buf + reply_len, &num, sizeof(num));
+ reply_len += sizeof(num);
+ break;
+ case UUIDD_OP_BULK_RANDOM_UUID:
+ if (num < 0)
+ num = 1;
+ if (num > 1000)
+ num = 1000;
+ if (num * UUID_LEN > (int) (sizeof(reply_buf) - sizeof(num)))
+ num = (sizeof(reply_buf) - sizeof(num)) / UUID_LEN;
+ __uuid_generate_random((unsigned char *) reply_buf +
+ sizeof(num), &num);
+ if (uuidd_cxt->debug) {
+ fprintf(stderr, P_("Generated %d UUID:\n",
+ "Generated %d UUIDs:\n", num), num);
+ for (i = 0, cp = reply_buf + sizeof(num);
+ i < num;
+ i++, cp += UUID_LEN) {
+ uuid_unparse((unsigned char *)cp, str);
+ fprintf(stderr, "\t%s\n", str);
+ }
+ }
+ reply_len = (num * UUID_LEN) + sizeof(num);
+ memcpy(reply_buf, &num, sizeof(num));
+ break;
+ default:
+ if (uuidd_cxt->debug)
+ fprintf(stderr, _("Invalid operation %d\n"), op);
+ goto shutdown_socket;
+ }
+ write_all(ns, (char *) &reply_len, sizeof(reply_len));
+ write_all(ns, reply_buf, reply_len);
+ shutdown_socket:
+ close(ns);
+ }
+}
+
+static void __attribute__ ((__noreturn__)) unexpected_size(int size)
+{
+ errx(EXIT_FAILURE, _("Unexpected reply length from server %d"), size);
+}
+
+int main(int argc, char **argv)
+{
+ const char *socket_path = UUIDD_SOCKET_PATH;
+ const char *pidfile_path = NULL;
+ const char *pidfile_path_param = NULL;
+ const char *err_context;
+ char buf[1024], *cp;
+ char str[UUID_STR_LEN], *tmp;
+ uuid_t uu;
+ int i, c, ret;
+ int do_type = 0, do_kill = 0, num = 0;
+ int no_pid = 0;
+ int s_flag = 0;
+
+ struct uuidd_cxt_t uuidd_cxt = { .timeout = 0 };
+
+ static const struct option longopts[] = {
+ {"pid", required_argument, NULL, 'p'},
+ {"socket", required_argument, NULL, 's'},
+ {"timeout", required_argument, NULL, 'T'},
+ {"kill", no_argument, NULL, 'k'},
+ {"random", no_argument, NULL, 'r'},
+ {"time", no_argument, NULL, 't'},
+ {"uuids", required_argument, NULL, 'n'},
+ {"no-pid", no_argument, NULL, 'P'},
+ {"no-fork", no_argument, NULL, 'F'},
+ {"socket-activation", no_argument, NULL, 'S'},
+ {"debug", no_argument, NULL, 'd'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while ((c =
+ getopt_long(argc, argv, "p:s:T:krtn:PFSdqVh", longopts,
+ NULL)) != -1) {
+ switch (c) {
+ case 'd':
+ uuidd_cxt.debug = 1;
+ break;
+ case 'k':
+ do_kill++;
+ break;
+ case 'n':
+ num = strtol(optarg, &tmp, 0);
+ if ((num < 1) || *tmp) {
+ fprintf(stderr, _("Bad number: %s\n"), optarg);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'p':
+ pidfile_path_param = optarg;
+ break;
+ case 'P':
+ no_pid = 1;
+ break;
+ case 'F':
+ uuidd_cxt.no_fork = 1;
+ break;
+ case 'S':
+#ifdef USE_SOCKET_ACTIVATION
+ uuidd_cxt.no_sock = 1;
+ uuidd_cxt.no_fork = 1;
+ no_pid = 1;
+#else
+ fprintf(stderr,
+ _("uuidd has been built without support for socket activation.\n"));
+ return EXIT_FAILURE;
+#endif
+ break;
+ case 'q':
+ uuidd_cxt.quiet = 1;
+ break;
+ case 'r':
+ do_type = UUIDD_OP_RANDOM_UUID;
+ break;
+ case 's':
+ socket_path = optarg;
+ s_flag = 1;
+ break;
+ case 't':
+ do_type = UUIDD_OP_TIME_UUID;
+ break;
+ case 'T':
+ uuidd_cxt.timeout = strtol(optarg, &tmp, 0);
+ if (uuidd_cxt.timeout < 0 || *tmp) {
+ fprintf(stderr, _("Bad number: %s\n"), optarg);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'V':
+ printf(_("%s from %s\n"),
+ program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+ }
+
+ if (no_pid && pidfile_path_param && !uuidd_cxt.quiet)
+ fprintf(stderr, _("Both --pid and --no-pid specified. "
+ "Ignoring --no-pid.\n"));
+
+ if (!no_pid && !pidfile_path_param)
+ pidfile_path = UUIDD_PIDFILE_PATH;
+ else if (pidfile_path_param)
+ pidfile_path = pidfile_path_param;
+
+ /* custom socket path and socket-activation make no sense */
+ if (s_flag && uuidd_cxt.no_sock && !uuidd_cxt.quiet)
+ fprintf(stderr, _("Both --socket-activation and --socket specified. "
+ "Ignoring --socket\n"));
+
+ if (num && do_type) {
+ ret = call_daemon(socket_path, do_type + 2, buf,
+ sizeof(buf), &num, &err_context);
+ if (ret < 0) {
+ printf(_("Error calling uuidd daemon (%s): %m\n"), err_context);
+ return EXIT_FAILURE;
+ }
+ if (do_type == UUIDD_OP_TIME_UUID) {
+ if (ret != sizeof(uu) + sizeof(num))
+ unexpected_size(ret);
+
+ uuid_unparse((unsigned char *) buf, str);
+
+ printf(P_("%s and %d subsequent UUID\n",
+ "%s and %d subsequent UUIDs\n", num - 1),
+ str, num - 1);
+ } else {
+ printf(_("List of UUIDs:\n"));
+ cp = buf + 4;
+ if (ret != (int) (sizeof(num) + num * sizeof(uu)))
+ unexpected_size(ret);
+ for (i = 0; i < num; i++, cp += UUID_LEN) {
+ uuid_unparse((unsigned char *) cp, str);
+ printf("\t%s\n", str);
+ }
+ }
+ return EXIT_SUCCESS;
+ }
+ if (do_type) {
+ ret = call_daemon(socket_path, do_type, (char *) &uu,
+ sizeof(uu), 0, &err_context);
+ if (ret < 0) {
+ printf(_("Error calling uuidd daemon (%s): %m\n"), err_context);
+ return EXIT_FAILURE;
+ }
+ if (ret != sizeof(uu))
+ unexpected_size(ret);
+
+ uuid_unparse(uu, str);
+
+ printf("%s\n", str);
+ return EXIT_SUCCESS;
+ }
+
+ if (do_kill) {
+ ret = call_daemon(socket_path, UUIDD_OP_GETPID, buf, sizeof(buf), 0, NULL);
+ if ((ret > 0) && ((do_kill = atoi((char *) buf)) > 0)) {
+ ret = kill(do_kill, SIGTERM);
+ if (ret < 0) {
+ if (!uuidd_cxt.quiet)
+ fprintf(stderr,
+ _("Couldn't kill uuidd running "
+ "at pid %d: %m\n"), do_kill);
+ return EXIT_FAILURE;
+ }
+ if (!uuidd_cxt.quiet)
+ printf(_("Killed uuidd running at pid %d\n"),
+ do_kill);
+ }
+ return EXIT_SUCCESS;
+ }
+
+ server_loop(socket_path, pidfile_path, &uuidd_cxt);
+ return EXIT_SUCCESS;
+}
diff --git a/misc-utils/uuidd.rc.in b/misc-utils/uuidd.rc.in
new file mode 100644
index 0000000..27b75c6
--- /dev/null
+++ b/misc-utils/uuidd.rc.in
@@ -0,0 +1,71 @@
+#! /bin/sh -e
+### BEGIN INIT INFO
+# Provides: uuidd
+# Required-Start: $time $local_fs
+# Required-Stop: $time $local_fs
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: uuidd daemon
+# Description: Init script for the uuid generation daemon
+### END INIT INFO
+#
+# Author: "Theodore Ts'o" <tytso@mit.edu>
+#
+set -e
+
+# libuuid is able to execute the uuid daemon on-demand -- in such a case
+# the daemon binary must be setuid to an unprivileged user (e.g. uuidd:uuidd).
+# [-- kzak Jun 2009]
+UUIDD_ON_DEMAND_ONLY="no"
+
+PATH=/bin:/usr/bin:/sbin:/usr/sbin
+DAEMON=/usr/sbin/uuidd
+UUIDD_USER=uuidd
+UUIDD_GROUP=uuidd
+UUIDD_DIR=@localstatedir@/uuidd
+PIDFILE=$UUIDD_DIR/uuidd.pid
+
+test -x $DAEMON || exit 0
+
+. /lib/lsb/init-functions
+
+case "$1" in
+ start)
+ log_daemon_msg "Starting uuid generator" "uuidd"
+ if ! test -d $UUIDD_DIR; then
+ mkdir -p $UUIDD_DIR
+ chown -R $UUIDD_USER:$UUIDD_GROUP $UUIDD_DIR
+ fi
+ if test "$UUIDD_ON_DEMAND_ONLY" = yes; then
+ echo -n "(on demand only)"
+ else
+ start_daemon -p $PIDFILE $DAEMON
+ fi
+ log_end_msg $?
+ ;;
+ stop)
+ log_daemon_msg "Stopping uuidd generator" "uuidd"
+ killproc -p $PIDFILE $DAEMON
+ log_end_msg $?
+ ;;
+ status)
+ if pidofproc -p $PIDFILE $DAEMON >/dev/null 2>&1; then
+ echo "$DAEMON is running";
+ exit 0;
+ else
+ echo "$DAEMON is NOT running";
+ if test -f $PIDFILE; then exit 2; fi
+ exit 3;
+ fi
+ ;;
+ force-reload|restart)
+ $0 stop
+ $0 start
+ ;;
+ *)
+ echo "Usage: /etc/init.d/uuidd {start|stop|restart|force-reload}"
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/misc-utils/uuidd.service.in b/misc-utils/uuidd.service.in
new file mode 100644
index 0000000..89e0eac
--- /dev/null
+++ b/misc-utils/uuidd.service.in
@@ -0,0 +1,12 @@
+[Unit]
+Description=Daemon for generating UUIDs
+Requires=uuidd.socket
+
+[Service]
+ExecStart=@usrsbin_execdir@/uuidd --socket-activation --timeout 60
+Restart=no
+User=uuidd
+Group=uuidd
+
+[Install]
+Also=uuidd.socket
diff --git a/misc-utils/uuidd.socket.in b/misc-utils/uuidd.socket.in
new file mode 100644
index 0000000..d8be72f
--- /dev/null
+++ b/misc-utils/uuidd.socket.in
@@ -0,0 +1,8 @@
+[Unit]
+Description=UUID daemon activation socket
+
+[Socket]
+ListenStream=@localstatedir@/uuidd/request
+
+[Install]
+WantedBy=sockets.target
diff --git a/misc-utils/uuidgen.1 b/misc-utils/uuidgen.1
new file mode 100644
index 0000000..ad409b9
--- /dev/null
+++ b/misc-utils/uuidgen.1
@@ -0,0 +1,63 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" This man page was created for libuuid.so.1.1 from e2fsprogs-1.14.
+.\"
+.\" This file may be copied under the terms of the GNU Public License.
+.\"
+.\" Created Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUIDGEN 1 "June 2011" "util-linux" "User Commands"
+.SH NAME
+uuidgen \- create a new UUID value
+.SH SYNOPSIS
+.B uuidgen
+[\fIoptions\fR]
+.SH DESCRIPTION
+The
+.B uuidgen
+program creates (and prints)
+a new universally unique identifier (UUID) using the
+.BR libuuid (3)
+library. The new UUID can reasonably be considered unique among
+all UUIDs created on the local system,
+and among UUIDs created on other systems in the past
+and in the future.
+.PP
+There are two types of UUIDs which
+.B uuidgen
+can generate: time-based UUIDs and random-based UUIDs. By default
+.B uuidgen
+will generate a random-based UUID if a high-quality random number
+generator is present. Otherwise, it will choose a time-based UUID.
+It is possible to force the generation of one of these two
+UUID types by using the
+.B \-r
+or
+.B \-t
+options.
+.SH OPTIONS
+.TP
+.BR \-r , " \-\-random"
+Generate a random-based UUID. This method creates a UUID consisting mostly
+of random bits. It requires that the operating system have a high
+quality random number generator, such as
+.IR /dev/random .
+.TP
+.BR \-t , " \-\-time"
+Generate a time-based UUID. This method creates a UUID based on the system
+clock plus the system's ethernet hardware address, if present.
+.TP
+.BR \-h , " \-\-help"
+Display help text and exit.
+.TP
+.BR \-V , " \-\-version"
+Display version information and exit.
+.SH "CONFORMING TO"
+OSF DCE 1.1
+.SH AUTHOR
+.B uuidgen
+was written by Andreas Dilger for libuuid.
+.SH AVAILABILITY
+The uuidgen command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.SH "SEE ALSO"
+.BR libuuid (3)
diff --git a/misc-utils/uuidgen.c b/misc-utils/uuidgen.c
new file mode 100644
index 0000000..219be8b
--- /dev/null
+++ b/misc-utils/uuidgen.c
@@ -0,0 +1,104 @@
+/*
+ * gen_uuid.c --- generate a DCE-compatible uuid
+ *
+ * Copyright (C) 1999, Andreas Dilger and Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+extern int getopt(int argc, char * const argv[], const char *optstring);
+extern char *optarg;
+extern int optind;
+#endif
+
+#include "uuid.h"
+#include "nls.h"
+#include "c.h"
+#include "closestream.h"
+
+#define DO_TYPE_TIME 1
+#define DO_TYPE_RANDOM 2
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fputs(_("\nUsage:\n"), out);
+ fprintf(out,
+ _(" %s [options]\n"), program_invocation_short_name);
+
+ fputs(_("\nOptions:\n"), out);
+ fputs(_(" -r, --random generate random-based uuid\n"
+ " -t, --time generate time-based uuid\n"
+ " -V, --version output version information and exit\n"
+ " -h, --help display this help and exit\n\n"), out);
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int
+main (int argc, char *argv[])
+{
+ int c;
+ int do_type = 0;
+ char str[37];
+ uuid_t uu;
+
+ static const struct option longopts[] = {
+ {"random", required_argument, NULL, 'r'},
+ {"time", no_argument, NULL, 't'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while ((c = getopt_long(argc, argv, "rtVh", longopts, NULL)) != -1)
+ switch (c) {
+ case 't':
+ do_type = DO_TYPE_TIME;
+ break;
+ case 'r':
+ do_type = DO_TYPE_RANDOM;
+ break;
+ case 'V':
+ printf(_("%s from %s\n"),
+ program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+
+ switch (do_type) {
+ case DO_TYPE_TIME:
+ uuid_generate_time(uu);
+ break;
+ case DO_TYPE_RANDOM:
+ uuid_generate_random(uu);
+ break;
+ default:
+ uuid_generate(uu);
+ break;
+ }
+
+ uuid_unparse(uu, str);
+
+ printf("%s\n", str);
+
+ return EXIT_SUCCESS;
+}
diff --git a/misc-utils/whereis.1 b/misc-utils/whereis.1
new file mode 100644
index 0000000..988462f
--- /dev/null
+++ b/misc-utils/whereis.1
@@ -0,0 +1,140 @@
+.\" Copyright (c) 1980, 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)whereis.1 from UCB 4.2
+.TH WHEREIS 1 "June 2012" "util-linux" "User Commands"
+.SH NAME
+whereis \- locate the binary, source, and manual page files for a command
+.SH SYNOPSIS
+.B whereis
+.RB [ options ]
+.RB [ \-BMS
+.IR directory ...
+.BR \-f ]
+.IR filename ...
+.SH DESCRIPTION
+.B whereis
+locates source/binary and manuals sections for specified files.
+The supplied names are first stripped of leading pathname components
+and any (single) trailing extension of the form
+.BI . ext\fR,\fP
+for example,
+.BR .c .
+Prefixes of
+.B s.
+resulting from use of source code control are also dealt with.
+.B whereis
+then attempts to locate the desired program in
+a list of standard Linux places.
+.SH OPTIONS
+.TP
+.IP "\fB\-b\fP"
+Search only for binaries.
+.IP "\fB\-m\fP"
+Search only for manual sections.
+.IP "\fB\-s\fP"
+Search only for sources.
+.IP "\fB\-u\fP"
+Search for unusual entries. A file is said to be unusual if it does
+not have one entry of each requested type. Thus
+.RB ` "whereis\ \ \-m\ \ \-u\ \ *" '
+asks for those files in the current
+directory which have no documentation.
+.IP "\fB\-B \fIlist\fP"
+Change or otherwise limit the places where
+.B whereis
+searches for binaries by white-space separated list of directories.
+.IP "\fB\-M \fIlist\fP"
+Change or otherwise limit the places where
+.B whereis
+searches for manual sections by white-space separated list of directories.
+.IP "\fB\-S \fIlist\fP"
+.B \-S
+Change or otherwise limit the places where
+.B whereis
+searches for sources white-space separated list of directories.
+.IP "\fB\-f\fP"
+Terminate the last directory list and signals the start of file names,
+and
+.I must
+be used when any of the
+.BR \-B ,
+.BR \-M ,
+or
+.BR \-S
+options are used.
+.SH EXAMPLE
+Find all files in
+.B /usr/bin
+which are not documented
+in
+.B /usr/man/man1
+with source in
+.BR /usr/src :
+.IP
+.nf
+.ft B
+$ cd /usr/bin
+$ whereis \-u \-M /usr/man/man1 \-S /usr/src \-f *
+.fi
+.ft R
+.SH FILES
+.B whereis
+has basic set of hard-coded paths (see below). If the option
+.B \-B
+is not specified then also follows
+.B $PATH
+environment variable (since version 2.21).
+
+.TP 20
+/{bin,sbin,etc}
+.TP
+/usr/{lib,bin,old,new,local,games,include,etc,src,man,sbin,X386,TeX,g++-include}
+.TP
+/usr/local/{X386,TeX,X11,include,lib,man,etc,bin,games,emacs}
+.SH "SEE ALSO"
+.BR chdir (2V)
+.SH BUGS
+Since
+.B whereis
+uses
+.BR chdir (2V)
+to run faster, pathnames given with the
+.BR \-M ,
+.BR \-S ,
+or
+.B \-B
+must be full; that is, they must begin with a
+.RB ` / '.
+.PP
+.SH AVAILABILITY
+The whereis command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/misc-utils/whereis.c b/misc-utils/whereis.c
new file mode 100644
index 0000000..05af8e0
--- /dev/null
+++ b/misc-utils/whereis.c
@@ -0,0 +1,485 @@
+/*-
+ * Copyright (c) 1980 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* *:aeb */
+
+/* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ */
+
+/* 2011-08-12 Davidlohr Bueso <dave@gnu.org>
+ * - added $PATH lookup
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "xalloc.h"
+#include "nls.h"
+#include "c.h"
+#include "closestream.h"
+
+static char *bindirs[] = {
+ "/bin",
+ "/usr/bin",
+ "/sbin",
+ "/usr/sbin",
+ "/etc",
+ "/usr/etc",
+ "/lib",
+ "/usr/lib",
+ "/lib64",
+ "/usr/lib64",
+ "/usr/games",
+ "/usr/games/bin",
+ "/usr/games/lib",
+ "/usr/emacs/etc",
+ "/usr/lib/emacs/*/etc",
+ "/usr/TeX/bin",
+ "/usr/tex/bin",
+ "/usr/interviews/bin/LINUX",
+
+ "/usr/X11R6/bin",
+ "/usr/X386/bin",
+ "/usr/bin/X11",
+ "/usr/X11/bin",
+ "/usr/X11R5/bin",
+
+ "/usr/local/bin",
+ "/usr/local/sbin",
+ "/usr/local/etc",
+ "/usr/local/lib",
+ "/usr/local/games",
+ "/usr/local/games/bin",
+ "/usr/local/emacs/etc",
+ "/usr/local/TeX/bin",
+ "/usr/local/tex/bin",
+ "/usr/local/bin/X11",
+
+ "/usr/contrib",
+ "/usr/hosts",
+ "/usr/include",
+
+ "/usr/g++-include",
+
+ "/usr/ucb",
+ "/usr/old",
+ "/usr/new",
+ "/usr/local",
+ "/usr/libexec",
+ "/usr/share",
+
+ "/opt/*/bin",
+
+ 0
+};
+
+static char *mandirs[] = {
+ "/usr/man/*",
+ "/usr/share/man/*",
+ "/usr/X386/man/*",
+ "/usr/X11/man/*",
+ "/usr/TeX/man/*",
+ "/usr/interviews/man/mann",
+ 0
+};
+
+static char *srcdirs[] = {
+ "/usr/src/*",
+ "/usr/src/lib/libc/*",
+ "/usr/src/lib/libc/net/*",
+ "/usr/src/ucb/pascal",
+ "/usr/src/ucb/pascal/utilities",
+ "/usr/src/undoc",
+ 0
+};
+
+static char sflag = 1, bflag = 1, mflag = 1, uflag;
+static char **Sflag, **Bflag, **Mflag, **pathdir, **pathdir_p;
+static int Scnt, Bcnt, Mcnt, count, print;
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fputs(_("\nUsage:\n"), out);
+ fprintf(out,
+ _(" %s [options] file\n"), program_invocation_short_name);
+
+ fputs(_("\nOptions:\n"), out);
+ fputs(_(" -f <file> define search scope\n"
+ " -b search only binaries\n"
+ " -B <dirs> define binaries lookup path\n"
+ " -m search only manual paths\n"
+ " -M <dirs> define man lookup path\n"
+ " -s search only sources path\n"
+ " -S <dirs> define sources lookup path\n"
+ " -u search from unusual entities\n"
+ " -V output version information and exit\n"
+ " -h display this help and exit\n\n"), out);
+
+ fputs(_("See how to use file and dirs arguments from whereis(1) manual.\n"), out);
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static int
+itsit(char *cp, char *dp)
+{
+ int i = strlen(dp);
+
+ if (dp[0] == 's' && dp[1] == '.' && itsit(cp, dp + 2))
+ return 1;
+ if (!strcmp(dp + i - 2, ".Z"))
+ i -= 2;
+ else if (!strcmp(dp + i - 3, ".gz"))
+ i -= 3;
+ else if (!strcmp(dp + i - 4, ".bz2"))
+ i -= 4;
+ while (*cp && *dp && *cp == *dp)
+ cp++, dp++, i--;
+ if (*cp == 0 && *dp == 0)
+ return 1;
+ while (isdigit(*dp))
+ dp++;
+ if (*cp == 0 && *dp++ == '.') {
+ --i;
+ while (i > 0 && *dp)
+ if (--i, *dp++ == '.')
+ return (*dp++ == 'C' && *dp++ == 0);
+ return 1;
+ }
+ return 0;
+}
+
+static void
+findin(char *dir, char *cp)
+{
+ DIR *dirp;
+ struct dirent *dp;
+ char *d, *dd;
+ size_t l;
+ char dirbuf[1024];
+ struct stat statbuf;
+
+ dd = strchr(dir, '*');
+ if (!dd) {
+ dirp = opendir(dir);
+ if (dirp == NULL)
+ return;
+ while ((dp = readdir(dirp)) != NULL) {
+ if (itsit(cp, dp->d_name)) {
+ count++;
+ if (print)
+ printf(" %s/%s", dir, dp->d_name);
+ }
+ }
+ closedir(dirp);
+ return;
+ }
+
+ l = strlen(dir);
+ if (l < sizeof(dirbuf)) {
+ /* refuse excessively long names */
+ strcpy(dirbuf, dir);
+ d = strchr(dirbuf, '*');
+ *d = 0;
+ dirp = opendir(dirbuf);
+ if (dirp == NULL)
+ return;
+ while ((dp = readdir(dirp)) != NULL) {
+ if (!strcmp(dp->d_name, ".") ||
+ !strcmp(dp->d_name, ".."))
+ continue;
+ if (strlen(dp->d_name) + l > sizeof(dirbuf))
+ continue;
+ sprintf(d, "%s", dp->d_name);
+ if (stat(dirbuf, &statbuf))
+ continue;
+ if (!S_ISDIR(statbuf.st_mode))
+ continue;
+ strcat(d, dd + 1);
+ findin(dirbuf, cp);
+ }
+ closedir(dirp);
+ }
+ return;
+
+}
+
+static int inpath(const char *str)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(bindirs) - 1 ; i++)
+ if (!strcmp(bindirs[i], str))
+ return 1;
+
+ for (i = 0; i < ARRAY_SIZE(mandirs) - 1; i++)
+ if (!strcmp(mandirs[i], str))
+ return 1;
+
+ for (i = 0; i < ARRAY_SIZE(srcdirs) - 1; i++)
+ if (!strcmp(srcdirs[i], str))
+ return 1;
+
+ return 0;
+}
+
+static void fillpath(void)
+{
+ char *key=NULL, *tok=NULL, *pathcp, *path = getenv("PATH");
+ int i = 0;
+
+
+ if (!path)
+ return;
+ pathcp = xstrdup(path);
+
+ for (tok = strtok_r(pathcp, ":", &key); tok;
+ tok = strtok_r(NULL, ":", &key)) {
+
+ /* make sure we don't repeat the search path */
+ if (inpath(tok))
+ continue;
+
+ pathdir = xrealloc(pathdir, (i + 1) * sizeof(char *));
+ pathdir[i++] = xstrdup(tok);
+ }
+
+ pathdir = xrealloc(pathdir, (i + 1) * sizeof(char *));
+ pathdir[i] = NULL;
+
+ pathdir_p = pathdir;
+ free(pathcp);
+}
+
+static void freepath(void)
+{
+ free(pathdir);
+}
+
+static void
+findv(char **dirv, int dirc, char *cp)
+{
+
+ while (dirc > 0)
+ findin(*dirv++, cp), dirc--;
+}
+
+static void
+looksrc(char *cp)
+{
+ if (Sflag == 0)
+ findv(srcdirs, ARRAY_SIZE(srcdirs)-1, cp);
+ else
+ findv(Sflag, Scnt, cp);
+}
+
+static void
+lookbin(char *cp)
+{
+ if (Bflag == 0) {
+ findv(bindirs, ARRAY_SIZE(bindirs)-1, cp);
+ while (*pathdir_p)
+ findin(*pathdir_p++, cp); /* look $PATH */
+ } else
+ findv(Bflag, Bcnt, cp);
+}
+
+static void
+lookman(char *cp)
+{
+ if (Mflag == 0)
+ findv(mandirs, ARRAY_SIZE(mandirs)-1, cp);
+ else
+ findv(Mflag, Mcnt, cp);
+}
+
+static void
+getlist(int *argcp, char ***argvp, char ***flagp, int *cntp)
+{
+ (*argvp)++;
+ *flagp = *argvp;
+ *cntp = 0;
+ for ((*argcp)--; *argcp > 0 && (*argvp)[0][0] != '-'; (*argcp)--)
+ (*cntp)++, (*argvp)++;
+ (*argcp)++;
+ (*argvp)--;
+}
+
+static void
+zerof(void)
+{
+ if (sflag && bflag && mflag)
+ sflag = bflag = mflag = 0;
+}
+
+static int
+print_again(char *cp)
+{
+ if (print)
+ printf("%s:", cp);
+ if (sflag) {
+ looksrc(cp);
+ if (uflag && print == 0 && count != 1) {
+ print = 1;
+ return 1;
+ }
+ }
+ count = 0;
+ if (bflag) {
+ lookbin(cp);
+ if (uflag && print == 0 && count != 1) {
+ print = 1;
+ return 1;
+ }
+ }
+ count = 0;
+ if (mflag) {
+ lookman(cp);
+ if (uflag && print == 0 && count != 1) {
+ print = 1;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void
+lookup(char *cp)
+{
+ register char *dp;
+
+ for (dp = cp; *dp; dp++)
+ continue;
+ for (; dp > cp; dp--) {
+ if (*dp == '.') {
+ *dp = 0;
+ break;
+ }
+ }
+ for (dp = cp; *dp; dp++)
+ if (*dp == '/')
+ cp = dp + 1;
+ if (uflag) {
+ print = 0;
+ count = 0;
+ } else
+ print = 1;
+
+ while (print_again(cp))
+ /* all in print_again() */ ;
+
+ if (print)
+ printf("\n");
+}
+
+/*
+ * whereis name
+ * look for source, documentation and binaries
+ */
+int
+main(int argc, char **argv)
+{
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ argc--, argv++;
+ if (argc == 0)
+ usage(stderr);
+
+ do
+ if (argv[0][0] == '-') {
+ register char *cp = argv[0] + 1;
+ while (*cp) switch (*cp++) {
+
+ case 'f':
+ break;
+
+ case 'S':
+ getlist(&argc, &argv, &Sflag, &Scnt);
+ break;
+
+ case 'B':
+ getlist(&argc, &argv, &Bflag, &Bcnt);
+ break;
+
+ case 'M':
+ getlist(&argc, &argv, &Mflag, &Mcnt);
+ break;
+
+ case 's':
+ zerof();
+ sflag++;
+ continue;
+
+ case 'u':
+ uflag++;
+ continue;
+
+ case 'b':
+ zerof();
+ bflag++;
+ continue;
+
+ case 'm':
+ zerof();
+ mflag++;
+ continue;
+ case 'V':
+ printf(_("%s from %s\n"),
+ program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+ argv++;
+ } else {
+ if (Bcnt == 0 && pathdir == NULL)
+ fillpath();
+ lookup(*argv++);
+ }
+ while (--argc > 0);
+
+ freepath();
+ return EXIT_SUCCESS;
+}
diff --git a/misc-utils/wipefs.8 b/misc-utils/wipefs.8
new file mode 100644
index 0000000..cecdc44
--- /dev/null
+++ b/misc-utils/wipefs.8
@@ -0,0 +1,74 @@
+.\" -*- nroff -*-
+.\" Copyright 2009 by Karel Zak. All Rights Reserved.
+.\" This file may be copied under the terms of the GNU Public License.
+.\"
+.TH WIPEFS 8 "October 2009" "util-linux" "System Administration"
+.SH NAME
+wipefs \- wipe a signature from a device
+.SH SYNOPSIS
+.B wipefs
+.RB [ \-ahnptV ]
+.RB [ \-o
+.IR offset ]
+.I device...
+.SH DESCRIPTION
+.B wipefs
+can erase filesystem, raid or partition table signatures (magic strings) from
+the specified
+.I device
+to make the signature invisible for libblkid.
+
+.B wipefs
+does not erase the filesystem itself nor any other data from the device.
+When used without options \fB-a\fR or \fB-o\fR, it lists all visible filesystems
+and the offsets of their basic signatures.
+
+Note that some filesystems or some partition tables store more magic strings on
+the devices. The
+.B wipefs
+lists the first offset where a magic string has been detected. The device is
+not scanned for additional magic strings for the same filesystem. It's possible
+that after \fBwipefs -o <offset>\fR will be the same filesystem or partition
+table visible by another magic string on another offset.
+
+When used with option \fB-a\fR then all for libblkid visible magic strings are
+erased.
+
+.SH OPTIONS
+.IP "\fB\-a, \-\-all\fP"
+Erase all available signatures. This set of erased signatures could be
+restricted by \fB\-t <list>\fP option.
+.IP "\fB\-h, \-\-help\fP"
+Print help and exit.
+.IP "\fB\-n, \-\-no\-act\fP"
+Causes everything to be done except for the write() call.
+.IP "\fB\-o, \-\-offset\fP \fIoffset\fP
+Specify the location (in bytes) of the signature which should be erased from the
+device. The \fIoffset\fR number may include a "0x" prefix; then the number will be
+interpreted as a hex value. It is possible to specify multiple \fB-o\fR options.
+
+The \fIoffset\fR argument may be followed by the multiplicative
+suffixes KiB=1024, MiB=1024*1024, and so on for GiB, TiB, PiB, EiB, ZiB and YiB
+(the "iB" is optional, e.g. "K" has the same meaning as "KiB") or the suffixes
+KB=1000, MB=1000*1000, and so on for GB, PB, EB, ZB and YB.
+.IP "\fB\-p, \-\-parsable\fP"
+Print out in parsable instead of printable format. Encode all potentially unsafe
+characters of a string to the corresponding hex value prefixed by '\\x'.
+.IP "\fB\-q, \-\-quiet\fP"
+Suppress output messages after successful signature wipe.
+.IP "\fB\-t, \-\-types\fP \fIlist\fP
+Used to limit the set of printed or erased signatures. More than one type may
+be specified in a comma-separated list. The list of types or individual types
+can be prefixed with 'no' to specify the types on which no action should be
+taken. For more details see mount(8).
+.IP "\fB\-V, \-\-version\fP"
+Output version information and exit.
+.SH AUTHOR
+Karel Zak <kzak@redhat.com>.
+.SH AVAILABILITY
+The wipefs command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.SH SEE ALSO
+.BR blkid (8)
+.BR findfs (8)
+
diff --git a/misc-utils/wipefs.c b/misc-utils/wipefs.c
new file mode 100644
index 0000000..cddad8a
--- /dev/null
+++ b/misc-utils/wipefs.c
@@ -0,0 +1,472 @@
+/*
+ * wipefs - utility to wipe filesystems from device
+ *
+ * Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+ * Written by Karel Zak <kzak@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include <limits.h>
+
+#include <blkid.h>
+
+#include "nls.h"
+#include "xalloc.h"
+#include "strutils.h"
+#include "all-io.h"
+#include "match.h"
+#include "c.h"
+#include "closestream.h"
+#include "optutils.h"
+
+struct wipe_desc {
+ loff_t offset; /* magic string offset */
+ size_t len; /* length of magic string */
+ unsigned char *magic; /* magic string */
+
+ int zap; /* zap this offset? */
+ char *usage; /* raid, filesystem, ... */
+ char *type; /* FS type */
+ char *label; /* FS label */
+ char *uuid; /* FS uuid */
+
+ int on_disk;
+
+ struct wipe_desc *next;
+};
+
+enum {
+ WP_MODE_PRETTY, /* default */
+ WP_MODE_PARSABLE
+};
+
+static const char *type_pattern;
+
+static void
+print_pretty(struct wipe_desc *wp, int line)
+{
+ if (!line) {
+ printf("offset type\n");
+ printf("----------------------------------------------------------------\n");
+ }
+
+ printf("0x%-17jx %s [%s]", wp->offset, wp->type, wp->usage);
+
+ if (wp->label && *wp->label)
+ printf("\n%27s %s", "LABEL:", wp->label);
+ if (wp->uuid)
+ printf("\n%27s %s", "UUID: ", wp->uuid);
+ puts("\n");
+}
+
+static void
+print_parsable(struct wipe_desc *wp, int line)
+{
+ char enc[256];
+
+ if (!line)
+ printf("# offset,uuid,label,type\n");
+
+ printf("0x%jx,", wp->offset);
+
+ if (wp->uuid) {
+ blkid_encode_string(wp->uuid, enc, sizeof(enc));
+ printf("%s,", enc);
+ } else
+ fputc(',', stdout);
+
+ if (wp->label) {
+ blkid_encode_string(wp->label, enc, sizeof(enc));
+ printf("%s,", enc);
+ } else
+ fputc(',', stdout);
+
+ blkid_encode_string(wp->type, enc, sizeof(enc));
+ printf("%s\n", enc);
+}
+
+static void
+print_all(struct wipe_desc *wp, int mode)
+{
+ int n = 0;
+
+ while (wp) {
+ switch (mode) {
+ case WP_MODE_PRETTY:
+ print_pretty(wp, n++);
+ break;
+ case WP_MODE_PARSABLE:
+ print_parsable(wp, n++);
+ break;
+ default:
+ abort();
+ }
+ wp = wp->next;
+ }
+}
+
+static struct wipe_desc *
+add_offset(struct wipe_desc *wp0, loff_t offset, int zap)
+{
+ struct wipe_desc *wp = wp0;
+
+ while (wp) {
+ if (wp->offset == offset)
+ return wp;
+ wp = wp->next;
+ }
+
+ wp = xcalloc(1, sizeof(struct wipe_desc));
+ wp->offset = offset;
+ wp->next = wp0;
+ wp->zap = zap;
+ return wp;
+}
+
+static struct wipe_desc *
+clone_offset(struct wipe_desc *wp0)
+{
+ struct wipe_desc *wp = NULL;
+
+ while(wp0) {
+ wp = add_offset(wp, wp0->offset, wp0->zap);
+ wp0 = wp0->next;
+ }
+
+ return wp;
+}
+
+static struct wipe_desc *
+get_desc_for_probe(struct wipe_desc *wp, blkid_probe pr)
+{
+ const char *off, *type, *mag, *p, *usage = NULL;
+ size_t len;
+ loff_t offset;
+ int rc;
+
+ /* superblocks */
+ if (blkid_probe_lookup_value(pr, "TYPE", &type, NULL) == 0) {
+ rc = blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &off, NULL);
+ if (!rc)
+ rc = blkid_probe_lookup_value(pr, "SBMAGIC", &mag, &len);
+ if (rc)
+ return wp;
+
+ /* partitions */
+ } else if (blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL) == 0) {
+ rc = blkid_probe_lookup_value(pr, "PTMAGIC_OFFSET", &off, NULL);
+ if (!rc)
+ rc = blkid_probe_lookup_value(pr, "PTMAGIC", &mag, &len);
+ if (rc)
+ return wp;
+ usage = "partition table";
+ } else
+ return wp;
+
+ if (type_pattern && !match_fstype(type, type_pattern))
+ return wp;
+
+ offset = strtoll(off, NULL, 10);
+
+ wp = add_offset(wp, offset, 0);
+ if (!wp)
+ return NULL;
+
+ if (usage || blkid_probe_lookup_value(pr, "USAGE", &usage, NULL) == 0)
+ wp->usage = xstrdup(usage);
+
+ wp->type = xstrdup(type);
+ wp->on_disk = 1;
+
+ wp->magic = xmalloc(len);
+ memcpy(wp->magic, mag, len);
+ wp->len = len;
+
+ if (blkid_probe_lookup_value(pr, "LABEL", &p, NULL) == 0)
+ wp->label = xstrdup(p);
+
+ if (blkid_probe_lookup_value(pr, "UUID", &p, NULL) == 0)
+ wp->uuid = xstrdup(p);
+
+ return wp;
+}
+
+static blkid_probe
+new_probe(const char *devname, int mode)
+{
+ blkid_probe pr;
+
+ if (!devname)
+ return NULL;
+
+ if (mode) {
+ int fd = open(devname, mode);
+ if (fd < 0)
+ goto error;
+
+ pr = blkid_new_probe();
+ if (pr && blkid_probe_set_device(pr, fd, 0, 0))
+ goto error;
+ } else
+ pr = blkid_new_probe_from_filename(devname);
+
+ if (!pr)
+ goto error;
+
+ blkid_probe_enable_superblocks(pr, 1);
+ blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC |
+ BLKID_SUBLKS_TYPE | BLKID_SUBLKS_USAGE |
+ BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID);
+
+ blkid_probe_enable_partitions(pr, 1);
+ blkid_probe_set_partitions_flags(pr, BLKID_PARTS_MAGIC);
+
+ return pr;
+error:
+ err(EXIT_FAILURE, _("error: %s: probing initialization failed"), devname);
+ return NULL;
+}
+
+static struct wipe_desc *
+read_offsets(struct wipe_desc *wp, const char *devname)
+{
+ blkid_probe pr = new_probe(devname, 0);
+
+ if (!pr)
+ return NULL;
+
+ while (blkid_do_probe(pr) == 0) {
+ wp = get_desc_for_probe(wp, pr);
+ if (!wp)
+ break;
+ }
+
+ blkid_free_probe(pr);
+ return wp;
+}
+
+static void
+free_wipe(struct wipe_desc *wp)
+{
+ while (wp) {
+ struct wipe_desc *next = wp->next;
+
+ free(wp->usage);
+ free(wp->type);
+ free(wp->magic);
+ free(wp->label);
+ free(wp->uuid);
+ free(wp);
+
+ wp = next;
+ }
+}
+
+static void do_wipe_real(blkid_probe pr, const char *devname, struct wipe_desc *w, int noact, int quiet)
+{
+ size_t i;
+
+ if (blkid_do_wipe(pr, noact))
+ warn(_("%s: failed to erase %s magic string at offset 0x%08jx"),
+ devname, w->type, w->offset);
+
+ if (quiet)
+ return;
+
+ printf(_("%s: %zd bytes were erased at offset 0x%08jx (%s): "),
+ devname, w->len, w->offset, w->type);
+
+ for (i = 0; i < w->len; i++) {
+ printf("%02x", w->magic[i]);
+ if (i + 1 < w->len)
+ fputc(' ', stdout);
+ }
+ putchar('\n');
+}
+
+static struct wipe_desc *
+do_wipe(struct wipe_desc *wp, const char *devname, int noact, int all, int quiet)
+{
+ blkid_probe pr = new_probe(devname, O_RDWR);
+ struct wipe_desc *w, *wp0 = clone_offset(wp);
+ int zap = all ? 1 : wp->zap;
+
+ if (!pr)
+ return NULL;
+
+ while (blkid_do_probe(pr) == 0) {
+ wp = get_desc_for_probe(wp, pr);
+ if (!wp)
+ break;
+
+ /* Check if offset is in provided list */
+ w = wp0;
+ while(w && w->offset != wp->offset)
+ w = w->next;
+ if (wp0 && !w)
+ continue;
+
+ /* Mark done if found in provided list */
+ if (w)
+ w->on_disk = wp->on_disk;
+
+ if (!wp->on_disk)
+ continue;
+
+ if (zap)
+ do_wipe_real(pr, devname, wp, noact, quiet);
+ }
+
+ for (w = wp0; w != NULL; w = w->next) {
+ if (!w->on_disk && !quiet)
+ warnx(_("%s: offset 0x%jx not found"), devname, w->offset);
+ }
+
+ fsync(blkid_probe_get_fd(pr));
+ close(blkid_probe_get_fd(pr));
+ blkid_free_probe(pr);
+ free_wipe(wp0);
+
+ return wp;
+}
+
+
+static void __attribute__((__noreturn__))
+usage(FILE *out)
+{
+ fputs(_("\nUsage:\n"), out);
+ fprintf(out,
+ _(" %s [options] <device>\n"), program_invocation_short_name);
+
+ fputs(_("\nOptions:\n"), out);
+ fputs(_(" -a, --all wipe all magic strings (BE CAREFUL!)\n"
+ " -h, --help show this help text\n"
+ " -n, --no-act do everything except the actual write() call\n"
+ " -o, --offset <num> offset to erase, in bytes\n"
+ " -p, --parsable print out in parsable instead of printable format\n"
+ " -q, --quiet suppress output messages\n"
+ " -t, --types <list> limit the set of filesystem, RAIDs or partition tables\n"
+ " -V, --version output version information and exit\n"), out);
+
+ fprintf(out, _("\nFor more information see wipefs(8).\n"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ struct wipe_desc *wp0 = NULL, *wp;
+ int c, all = 0, has_offset = 0, noact = 0, quiet = 0;
+ int mode = WP_MODE_PRETTY;
+
+ static const struct option longopts[] = {
+ { "all", 0, 0, 'a' },
+ { "help", 0, 0, 'h' },
+ { "no-act", 0, 0, 'n' },
+ { "offset", 1, 0, 'o' },
+ { "parsable", 0, 0, 'p' },
+ { "quiet", 0, 0, 'q' },
+ { "types", 1, 0, 't' },
+ { "version", 0, 0, 'V' },
+ { NULL, 0, 0, 0 }
+ };
+
+ static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */
+ { 'a','o' },
+ { 0 }
+ };
+ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while ((c = getopt_long(argc, argv, "ahno:pqt:V", longopts, NULL)) != -1) {
+
+ err_exclusive_options(c, longopts, excl, excl_st);
+
+ switch(c) {
+ case 'a':
+ all++;
+ break;
+ case 'h':
+ usage(stdout);
+ break;
+ case 'n':
+ noact++;
+ break;
+ case 'o':
+ wp0 = add_offset(wp0, strtosize_or_err(optarg,
+ _("invalid offset argument")), 1);
+ has_offset++;
+ break;
+ case 'p':
+ mode = WP_MODE_PARSABLE;
+ break;
+ case 'q':
+ quiet++;
+ break;
+ case 't':
+ type_pattern = optarg;
+ break;
+ case 'V':
+ printf(_("%s from %s\n"), program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ default:
+ usage(stderr);
+ break;
+ }
+ }
+
+ if (optind == argc)
+ usage(stderr);
+
+ if (!all && !has_offset) {
+ /*
+ * Print only
+ */
+ while (optind < argc) {
+ wp0 = read_offsets(NULL, argv[optind++]);
+ if (wp0)
+ print_all(wp0, mode);
+ free_wipe(wp0);
+ }
+ } else {
+ /*
+ * Erase
+ */
+ while (optind < argc) {
+ wp = clone_offset(wp0);
+ wp = do_wipe(wp, argv[optind++], noact, all, quiet);
+ free_wipe(wp);
+ }
+ }
+
+ return EXIT_SUCCESS;
+}