summaryrefslogtreecommitdiff
path: root/sys-utils
diff options
context:
space:
mode:
Diffstat (limited to 'sys-utils')
-rw-r--r--sys-utils/Makemodule.am302
-rw-r--r--sys-utils/arch.140
-rw-r--r--sys-utils/arch.c84
-rw-r--r--sys-utils/chcpu.896
-rw-r--r--sys-utils/chcpu.c335
-rw-r--r--sys-utils/ctrlaltdel.851
-rw-r--r--sys-utils/ctrlaltdel.c69
-rw-r--r--sys-utils/cyclades.h16
-rw-r--r--sys-utils/cytune.8194
-rw-r--r--sys-utils/cytune.c461
-rw-r--r--sys-utils/dmesg.1162
-rw-r--r--sys-utils/dmesg.c1214
-rw-r--r--sys-utils/eject.1165
-rw-r--r--sys-utils/eject.c1132
-rw-r--r--sys-utils/fallocate.162
-rw-r--r--sys-utils/fallocate.c173
-rw-r--r--sys-utils/flock.1166
-rw-r--r--sys-utils/flock.c343
-rw-r--r--sys-utils/fsfreeze.873
-rw-r--r--sys-utils/fsfreeze.c134
-rw-r--r--sys-utils/fstab.5271
-rw-r--r--sys-utils/fstrim.885
-rw-r--r--sys-utils/fstrim.c153
-rw-r--r--sys-utils/hwclock-cmos.c677
-rw-r--r--sys-utils/hwclock-kd.c182
-rw-r--r--sys-utils/hwclock-rtc.c509
-rw-r--r--sys-utils/hwclock.8661
-rw-r--r--sys-utils/hwclock.c1870
-rw-r--r--sys-utils/hwclock.h47
-rw-r--r--sys-utils/ipcmk.151
-rw-r--r--sys-utils/ipcmk.c160
-rw-r--r--sys-utils/ipcrm.1111
-rw-r--r--sys-utils/ipcrm.c410
-rw-r--r--sys-utils/ipcs.1103
-rw-r--r--sys-utils/ipcs.c714
-rw-r--r--sys-utils/ldattach.8144
-rw-r--r--sys-utils/ldattach.c373
-rw-r--r--sys-utils/losetup.8174
-rw-r--r--sys-utils/losetup.c465
-rw-r--r--sys-utils/lp.h83
-rw-r--r--sys-utils/lscpu.1149
-rw-r--r--sys-utils/lscpu.c1400
-rw-r--r--sys-utils/mount.82935
-rw-r--r--sys-utils/mount.c995
-rw-r--r--sys-utils/mountpoint.143
-rw-r--r--sys-utils/mountpoint.c212
-rw-r--r--sys-utils/pivot_root.875
-rw-r--r--sys-utils/pivot_root.c75
-rw-r--r--sys-utils/prlimit.1124
-rw-r--r--sys-utils/prlimit.c643
-rw-r--r--sys-utils/readprofile.8152
-rw-r--r--sys-utils/readprofile.c405
-rw-r--r--sys-utils/renice.1128
-rw-r--r--sys-utils/renice.c185
-rw-r--r--sys-utils/rtcwake.8146
-rw-r--r--sys-utils/rtcwake.c624
-rw-r--r--sys-utils/setarch.885
-rw-r--r--sys-utils/setarch.c351
-rw-r--r--sys-utils/setsid.124
-rw-r--r--sys-utils/setsid.c98
-rw-r--r--sys-utils/swapoff.81
-rw-r--r--sys-utils/swapoff.c196
-rw-r--r--sys-utils/swapon-common.c106
-rw-r--r--sys-utils/swapon-common.h26
-rw-r--r--sys-utils/swapon.8241
-rw-r--r--sys-utils/swapon.c823
-rw-r--r--sys-utils/switch_root.861
-rw-r--r--sys-utils/switch_root.c225
-rw-r--r--sys-utils/tunelp.8147
-rw-r--r--sys-utils/tunelp.c335
-rw-r--r--sys-utils/umount.8175
-rw-r--r--sys-utils/umount.c399
-rw-r--r--sys-utils/unshare.162
-rw-r--r--sys-utils/unshare.c137
-rw-r--r--sys-utils/wdctl.861
-rw-r--r--sys-utils/wdctl.c577
76 files changed, 24936 insertions, 0 deletions
diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am
new file mode 100644
index 0000000..d376f04
--- /dev/null
+++ b/sys-utils/Makemodule.am
@@ -0,0 +1,302 @@
+
+usrbin_exec_PROGRAMS += flock
+dist_man_MANS += sys-utils/flock.1
+flock_SOURCES = sys-utils/flock.c
+flock_LDADD = $(LDADD) libcommon.la
+
+usrbin_exec_PROGRAMS += ipcmk
+dist_man_MANS += sys-utils/ipcmk.1
+ipcmk_SOURCES = sys-utils/ipcmk.c
+ipcmk_LDADD = $(LDADD) libcommon.la
+
+usrbin_exec_PROGRAMS += ipcrm
+dist_man_MANS += sys-utils/ipcrm.1
+ipcrm_SOURCES = sys-utils/ipcrm.c
+ipcrm_LDADD = $(LDADD) libcommon.la
+
+usrbin_exec_PROGRAMS += ipcs
+dist_man_MANS += sys-utils/ipcs.1
+ipcs_SOURCES = sys-utils/ipcs.c
+
+usrbin_exec_PROGRAMS += renice
+dist_man_MANS += sys-utils/renice.1
+renice_SOURCES = sys-utils/renice.c
+
+usrbin_exec_PROGRAMS += setsid
+dist_man_MANS += sys-utils/setsid.1
+setsid_SOURCES = sys-utils/setsid.c
+
+usrsbin_exec_PROGRAMS += readprofile
+dist_man_MANS += sys-utils/readprofile.8
+readprofile_SOURCES = sys-utils/readprofile.c
+
+
+if LINUX
+#
+# Linux-only utils with no another dependencies. All another dependencies have
+# to be resolved in configure.ac end exported to makefiles by BUILD_*.
+#
+bin_PROGRAMS += dmesg
+dist_man_MANS += sys-utils/dmesg.1
+dmesg_SOURCES = sys-utils/dmesg.c
+dmesg_LDADD = $(LDADD) libcommon.la
+
+sbin_PROGRAMS += ctrlaltdel
+dist_man_MANS += sys-utils/ctrlaltdel.8
+ctrlaltdel_SOURCES = sys-utils/ctrlaltdel.c
+
+sbin_PROGRAMS += fsfreeze
+dist_man_MANS += sys-utils/fsfreeze.8
+fsfreeze_SOURCES = sys-utils/fsfreeze.c
+
+sbin_PROGRAMS += fstrim
+dist_man_MANS += sys-utils/fstrim.8
+fstrim_SOURCES = sys-utils/fstrim.c
+fstrim_LDADD = $(LDADD) libcommon.la
+
+usrbin_exec_PROGRAMS += cytune
+dist_man_MANS += sys-utils/cytune.8
+cytune_SOURCES = sys-utils/cytune.c sys-utils/cyclades.h
+cytune_LDADD = $(LDADD) libcommon.la
+
+usrsbin_exec_PROGRAMS += ldattach
+dist_man_MANS += sys-utils/ldattach.8
+ldattach_SOURCES = sys-utils/ldattach.c
+ldattach_LDADD = $(LDADD) libcommon.la
+
+usrsbin_exec_PROGRAMS += tunelp
+dist_man_MANS += sys-utils/tunelp.8
+tunelp_SOURCES = sys-utils/tunelp.c sys-utils/lp.h
+
+usrsbin_exec_PROGRAMS += rtcwake
+dist_man_MANS += sys-utils/rtcwake.8
+rtcwake_SOURCES = sys-utils/rtcwake.c
+rtcwake_LDADD = $(LDADD) libcommon.la
+
+usrbin_exec_PROGRAMS += setarch
+dist_man_MANS += sys-utils/setarch.8
+setarch_SOURCES = sys-utils/setarch.c
+
+SETARCH_LINKS = linux32 linux64
+
+if ARCH_S390
+SETARCH_LINKS += s390 s390x
+endif
+if ARCH_I86
+SETARCH_LINKS += i386
+endif
+if ARCH_86_64
+SETARCH_LINKS += i386 x86_64
+endif
+if ARCH_PPC
+SETARCH_LINKS += ppc ppc64 ppc32
+endif
+if ARCH_SPARC
+SETARCH_LINKS += sparc sparc64 sparc32 sparc32bash
+endif
+if ARCH_MIPS
+SETARCH_LINKS += mips mips64 mips32
+endif
+if ARCH_IA64
+SETARCH_LINKS += i386 ia64
+endif
+if ARCH_HPPA
+SETARCH_LINKS += parisc parisc64 parisc32
+endif
+
+SETARCH_MAN_LINKS = $(addprefix sys-utils/,$(SETARCH_LINKS:=.8))
+man_MANS += $(SETARCH_MAN_LINKS)
+CLEANFILES += $(SETARCH_MAN_LINKS)
+
+$(SETARCH_MAN_LINKS):
+ $(AM_V_GEN)echo ".so man8/setarch.8" > $@
+
+install-exec-hook-setarch:
+ for I in $(SETARCH_LINKS); do \
+ cd $(DESTDIR)$(usrbin_execdir) && ln -sf setarch $$I ; \
+ done
+
+uninstall-hook-setarch:
+ for I in $(SETARCH_LINKS); do \
+ rm -f $(DESTDIR)$(usrbin_execdir)/$$I ; \
+ done
+
+INSTALL_EXEC_HOOKS += install-exec-hook-setarch
+UNINSTALL_HOOKS += uninstall-hook-setarch
+
+endif # LINUX
+
+
+if BUILD_EJECT
+usrbin_exec_PROGRAMS += eject
+eject_SOURCES = sys-utils/eject.c
+eject_LDADD = $(LDADD) libmount.la libcommon.la
+eject_CFLAGS = $(AM_CFLAGS) -I$(ul_libmount_incdir)
+dist_man_MANS += sys-utils/eject.1
+endif
+
+
+if BUILD_LOSETUP
+sbin_PROGRAMS += losetup
+dist_man_MANS += sys-utils/losetup.8
+losetup_SOURCES = sys-utils/losetup.c
+losetup_LDADD = $(LDADD) libcommon.la
+
+if HAVE_STATIC_LOSETUP
+bin_PROGRAMS += losetup.static
+losetup_static_SOURCES = $(losetup_SOURCES)
+losetup_static_LDFLAGS = -all-static
+losetup_static_LDADD = $(losetup_LDADD)
+endif
+endif # BUILD_LOSETUP
+
+
+if BUILD_PRLIMIT
+usrbin_exec_PROGRAMS += prlimit
+dist_man_MANS += sys-utils/prlimit.1
+prlimit_SOURCES = sys-utils/prlimit.c
+prlimit_LDADD = $(LDADD) libcommon.la
+endif
+
+
+if BUILD_MOUNT
+#
+# The original mount is in mount/ directory
+# -- temporary we share some man pages
+#
+bin_PROGRAMS += mount umount
+dist_man_MANS += \
+ sys-utils/mount.8 \
+ sys-utils/fstab.5 \
+ sys-utils/umount.8
+mount_SOURCES = sys-utils/mount.c
+mount_LDADD = $(LDADD) libcommon.la libmount.la $(SELINUX_LIBS)
+mount_CFLAGS = $(SUID_CFLAGS) $(AM_CFLAGS) -I$(ul_libmount_incdir)
+mount_LDFLAGS = $(SUID_LDFLAGS) $(AM_LDFLAGS)
+
+umount_SOURCES = sys-utils/umount.c
+umount_LDADD = $(LDADD) libcommon.la libmount.la
+umount_CFLAGS = $(AM_CFLAGS) $(SUID_CFLAGS) -I$(ul_libmount_incdir)
+umount_LDFLAGS = $(SUID_LDFLAGS) $(AM_LDFLAGS)
+
+if HAVE_STATIC_MOUNT
+bin_PROGRAMS += mount.static
+mount_static_SOURCES = $(mount_SOURCES)
+mount_static_CFLAGS = $(mount_CFLAGS)
+mount_static_LDFLAGS = $(mount_LDFLAGS) -all-static
+mount_static_LDADD = $(mount_LDADD) $(SELINUX_LIBS_STATIC)
+endif
+
+if HAVE_STATIC_UMOUNT
+bin_PROGRAMS += umount.static
+umount_static_SOURCES = $(umount_SOURCES)
+umount_static_CFLAGS = $(umount_CFLAGS)
+umount_static_LDFLAGS = $(umount_LDFLAGS) -all-static
+umount_static_LDADD = $(umount_LDADD)
+endif
+
+if MAKEINSTALL_DO_SETUID
+install-exec-hook-mount:
+ chmod 4755 $(DESTDIR)$(bindir)/mount
+ chmod 4755 $(DESTDIR)$(bindir)/umount
+
+INSTALL_EXEC_HOOKS += install-exec-hook-mount
+endif
+endif # BUILD_MOUNT
+
+
+if BUILD_SWAPON
+sbin_PROGRAMS += swapon swapoff
+dist_man_MANS += \
+ sys-utils/swapoff.8 \
+ sys-utils/swapon.8
+
+swapon_SOURCES = \
+ sys-utils/swapon.c \
+ sys-utils/swapon-common.c \
+ sys-utils/swapon-common.h
+
+swapon_CFLAGS = $(AM_CFLAGS) -I$(ul_libmount_incdir)
+swapon_LDADD = $(LDADD) libcommon.la libmount.la
+
+swapoff_SOURCES = sys-utils/swapoff.c sys-utils/swapon-common.c
+swapoff_CFLAGS = $(AM_CFLAGS) -I$(ul_libmount_incdir)
+swapoff_LDADD = $(LDADD) libmount.la
+endif
+
+if BUILD_LSCPU
+usrbin_exec_PROGRAMS += lscpu
+lscpu_SOURCES = sys-utils/lscpu.c
+lscpu_LDADD = $(LDADD) libcommon.la
+dist_man_MANS += sys-utils/lscpu.1
+endif
+
+if BUILD_CHCPU
+sbin_PROGRAMS += chcpu
+chcpu_SOURCES = sys-utils/chcpu.c
+chcpu_LDADD = $(LDADD) libcommon.la
+dist_man_MANS += sys-utils/chcpu.8
+endif
+
+if BUILD_WDCTL
+bin_PROGRAMS += wdctl
+dist_man_MANS += sys-utils/wdctl.8
+wdctl_SOURCES = sys-utils/wdctl.c
+wdctl_LDADD = $(LDADD) libcommon.la
+endif
+
+if BUILD_MOUNTPOINT
+bin_PROGRAMS += mountpoint
+mountpoint_LDADD = $(LDADD) libmount.la
+mountpoint_CFLAGS = $(AM_CFLAGS) -I$(ul_libmount_incdir)
+dist_man_MANS += sys-utils/mountpoint.1
+mountpoint_SOURCES = sys-utils/mountpoint.c
+endif
+
+if BUILD_FALLOCATE
+usrbin_exec_PROGRAMS += fallocate
+fallocate_SOURCES = sys-utils/fallocate.c
+fallocate_LDADD = $(LDADD) libcommon.la
+dist_man_MANS += sys-utils/fallocate.1
+endif
+
+if BUILD_PIVOT_ROOT
+sbin_PROGRAMS += pivot_root
+dist_man_MANS += sys-utils/pivot_root.8
+pivot_root_SOURCES = sys-utils/pivot_root.c
+endif
+
+if BUILD_SWITCH_ROOT
+sbin_PROGRAMS += switch_root
+dist_man_MANS += sys-utils/switch_root.8
+switch_root_SOURCES = sys-utils/switch_root.c
+endif
+
+if BUILD_UNSHARE
+usrbin_exec_PROGRAMS += unshare
+dist_man_MANS += sys-utils/unshare.1
+unshare_SOURCES = sys-utils/unshare.c
+endif
+
+if BUILD_ARCH
+bin_PROGRAMS += arch
+dist_man_MANS += sys-utils/arch.1
+arch_SOURCES = sys-utils/arch.c
+endif
+
+if BUILD_HWCLOCK
+sbin_PROGRAMS += hwclock
+dist_man_MANS += sys-utils/hwclock.8
+hwclock_SOURCES = \
+ sys-utils/hwclock.c \
+ sys-utils/hwclock.h \
+ sys-utils/hwclock-cmos.c \
+ sys-utils/hwclock-kd.c
+if LINUX
+hwclock_SOURCES += sys-utils/hwclock-rtc.c
+endif
+hwclock_LDADD = $(LDADD) libcommon.la
+if HAVE_AUDIT
+hwclock_LDADD += -laudit
+endif
+endif # BUILD_HWCLOCK
diff --git a/sys-utils/arch.1 b/sys-utils/arch.1
new file mode 100644
index 0000000..96cbc0a
--- /dev/null
+++ b/sys-utils/arch.1
@@ -0,0 +1,40 @@
+.\" arch.1 --
+.\" Copyright 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" Public domain: may be freely distributed.
+.TH ARCH 1 "July 1997" "util-linux" "User Commands"
+.SH NAME
+arch \- print machine architecture
+.SH SYNOPSIS
+.B arch
+.SH DESCRIPTION
+.B arch
+is a deprecated command since util-linux 2.13. Use
+.B "uname -m"
+or use
+.B arch
+from the GNU coreutils package.
+
+On current Linux systems,
+.B arch
+prints things such as "i386", "i486", "i586", "alpha", "sparc",
+"arm", "m68k", "mips", "ppc".
+.SH SEE ALSO
+.BR uname (1),
+.BR uname (2)
+.SH AVAILABILITY
+The arch command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.\"
+.\" Details:
+.\" arch prints the machine part of the system_utsname struct
+.\" This struct is defined in version.c, and this field is
+.\" initialized with UTS_MACHINE, which is defined as $ARCH
+.\" in the main Makefile.
+.\" That gives the possibilities
+.\" alpha arm i386 m68k mips ppc sparc sparc64
+.\"
+.\" If Makefile is not edited, ARCH is guessed by
+.\" ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
+.\" Then how come we get these i586 values?
+.\" Well, the routine check_bugs() does system_utsname.machine[1] = '0' + x86;
+.\" (called in init/main.c, defined in ./include/asm-i386/bugs.h)
diff --git a/sys-utils/arch.c b/sys-utils/arch.c
new file mode 100644
index 0000000..a730589
--- /dev/null
+++ b/sys-utils/arch.c
@@ -0,0 +1,84 @@
+/* arch -- print machine architecture information
+ * Created: Mon Dec 20 12:27:15 1993 by faith@cs.unc.edu
+ * Revised: Mon Dec 20 12:29:23 1993 by faith@cs.unc.edu
+ * Copyright 1993 Rickard E. Faith (faith@cs.unc.edu)
+ *
+ * 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, 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.
+ */
+
+/*
+ * This command is deprecated. The utility is in maintenance mode,
+ * meaning we keep them in source tree for backward compatibility
+ * only. Do not waste time making this command better, unless the
+ * fix is about security or other very critical issue.
+ *
+ * See Documentation/deprecated.txt for more information.
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <sys/utsname.h>
+
+#include "c.h"
+#include "closestream.h"
+#include "nls.h"
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fprintf(out, USAGE_HEADER);
+ /* Synopsis */
+ fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
+ fprintf(out, USAGE_OPTIONS);
+ fprintf(out, USAGE_HELP);
+ fprintf(out, USAGE_VERSION);
+ fprintf(out, USAGE_MAN_TAIL("arch(1)"));
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ struct utsname utsbuf;
+ int ch;
+ static const struct option longopts[] = {
+ {"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 ((ch = getopt_long(argc, argv, "Vh", longopts, NULL)) != -1)
+ switch (ch) {
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+
+ if (uname(&utsbuf))
+ err(EXIT_FAILURE, _("uname failed"));
+
+ printf("%s\n", utsbuf.machine);
+
+ return EXIT_SUCCESS;
+}
diff --git a/sys-utils/chcpu.8 b/sys-utils/chcpu.8
new file mode 100644
index 0000000..d016b86
--- /dev/null
+++ b/sys-utils/chcpu.8
@@ -0,0 +1,96 @@
+.TH CHCPU "8" "June 2012" Linux "User Manuals"
+.SH NAME
+chcpu \- configure CPUs
+.SH SYNOPSIS
+chcpu
+[\-e] [\-d] [\-c] [\-g]
+cpu-list
+.br
+chcpu [\-p] mode
+.br
+chcpu
+\-r
+.br
+chcpu [\-h] [\-V]
+.br
+.SH DESCRIPTION
+.B chcpu
+can modify the state of CPUs. It can enable or disable CPUs, scan for new
+CPUs, change the CPU dispatching
+.I mode
+of the underlying hypervisor, and request CPUs from the hypervisor
+(configure) or return CPUs to the hypervisor (deconfigure).
+.PP
+Some options have a
+.I cpu-list
+argument. Use this argument to specify a comma-separated list of CPUs. The
+list can contain individual CPU addresses or ranges of addresses. For
+example,
+.B 0,5,7,9-11
+makes the command applicable to the CPUs with the addresses 0, 5, 7, 9, 10,
+and 11.
+.SH OPTIONS
+.TP
+.BR \-r , " \-\-rescan"
+Trigger a rescan of CPUs. Use this option on systems that do not
+automatically detect newly attached CPUs. The Linux kernel then recognizes
+the new CPUs.
+.TP
+.BR \-c , " \-\-configure " \fIcpu-list\fP
+Configure all specified CPUs. Configuring a CPU means that the hypervisor
+takes a CPU from the CPU pool and assigns it to the virtual hardware on which
+your kernel runs.
+.TP
+.BR \-e , " \-\-enable " \fIcpu-list\fP
+Enable all specified CPUs. Enabling a CPU means that the kernel sets it
+online. A CPU must be configured, see
+.BR -c ,
+before it can be enabled.
+.TP
+.BR \-p , " \-\-dispatch " \fImode\fP
+Set the CPU dispatching
+.I mode
+(polarization). This option has an effect only if your hardware architecture
+and hypervisor support CPU polarization. Available
+.I modes
+are:
+.RS 14
+.TP 12
+.PD 0
+.B horizontal
+The workload is spread across all available CPUs.
+.TP 12
+.B vertical
+The workload is concentrated on few CPUs.
+.RE
+.PD 1
+.TP
+.BR \-d , " \-\-disable " \fIcpu-list\fP
+Disable all specified CPUs. Disabling a CPU means that the kernel sets it
+offline.
+.TP
+.BR \-g , " \-\-deconfigure " \fIcpu-list\fP
+Deconfigure all specified CPUs. Deconfiguring a CPU means that the
+hypervisor removes the CPU from the virtual hardware on which the Linux
+instance runs and returns it to the CPU pool. A CPU must be offline, see
+\-d, before it can be deconfigured.
+.TP
+.BR \-h , " \-\-help"
+Display help information and exit.
+.TP
+.BR \-V , " \-\-version"
+Display version information and exit.
+.SH AUTHOR
+.MT heiko.carstens@de.ibm.com
+Heiko Carstens
+.ME
+.SH COPYRIGHT
+Copyright IBM Corp. 2011
+.br
+.SH "SEE ALSO"
+.BR lscpu (1)
+.SH AVAILABILITY
+The chcpu 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/sys-utils/chcpu.c b/sys-utils/chcpu.c
new file mode 100644
index 0000000..0209fe9
--- /dev/null
+++ b/sys-utils/chcpu.c
@@ -0,0 +1,335 @@
+/*
+ * chcpu - CPU configuration tool
+ *
+ * Copyright IBM Corp. 2011
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.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 <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "cpuset.h"
+#include "nls.h"
+#include "xalloc.h"
+#include "c.h"
+#include "strutils.h"
+#include "bitops.h"
+#include "path.h"
+#include "closestream.h"
+#include "optutils.h"
+
+#define EXCL_ERROR "--{configure,deconfigure,disable,dispatch,enable}"
+
+#define _PATH_SYS_CPU "/sys/devices/system/cpu"
+#define _PATH_SYS_CPU_ONLINE _PATH_SYS_CPU "/online"
+#define _PATH_SYS_CPU_RESCAN _PATH_SYS_CPU "/rescan"
+#define _PATH_SYS_CPU_DISPATCH _PATH_SYS_CPU "/dispatching"
+
+static cpu_set_t *onlinecpus;
+static int maxcpus;
+
+#define is_cpu_online(cpu) (CPU_ISSET_S((cpu), CPU_ALLOC_SIZE(maxcpus), onlinecpus))
+#define num_online_cpus() (CPU_COUNT_S(CPU_ALLOC_SIZE(maxcpus), onlinecpus))
+
+enum {
+ CMD_CPU_ENABLE = 0,
+ CMD_CPU_DISABLE,
+ CMD_CPU_CONFIGURE,
+ CMD_CPU_DECONFIGURE,
+ CMD_CPU_RESCAN,
+ CMD_CPU_DISPATCH_HORIZONTAL,
+ CMD_CPU_DISPATCH_VERTICAL,
+};
+
+static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable)
+{
+ unsigned int cpu;
+ int online, rc;
+ int configured = -1;
+
+ for (cpu = 0; cpu < setsize; cpu++) {
+ if (!CPU_ISSET(cpu, cpu_set))
+ continue;
+ if (!path_exist(_PATH_SYS_CPU "/cpu%d", cpu)) {
+ printf(_("CPU %d does not exist\n"), cpu);
+ continue;
+ }
+ if (!path_exist(_PATH_SYS_CPU "/cpu%d/online", cpu)) {
+ printf(_("CPU %d is not hot pluggable\n"), cpu);
+ continue;
+ }
+ online = path_getnum(_PATH_SYS_CPU "/cpu%d/online", cpu);
+ if ((online == 1) && (enable == 1)) {
+ printf(_("CPU %d is already enabled\n"), cpu);
+ continue;
+ }
+ if ((online == 0) && (enable == 0)) {
+ printf(_("CPU %d is already disabled\n"), cpu);
+ continue;
+ }
+ if (path_exist(_PATH_SYS_CPU "/cpu%d/configure", cpu))
+ configured = path_getnum(_PATH_SYS_CPU "/cpu%d/configure", cpu);
+ if (enable) {
+ rc = path_writestr("1", _PATH_SYS_CPU "/cpu%d/online", cpu);
+ if ((rc == -1) && (configured == 0))
+ printf(_("CPU %d enable failed "
+ "(CPU is deconfigured)\n"), cpu);
+ else if (rc == -1)
+ printf(_("CPU %d enable failed (%m)\n"), cpu);
+ else
+ printf(_("CPU %d enabled\n"), cpu);
+ } else {
+ if (onlinecpus && num_online_cpus() == 1) {
+ printf(_("CPU %d disable failed "
+ "(last enabled CPU)\n"), cpu);
+ continue;
+ }
+ rc = path_writestr("0", _PATH_SYS_CPU "/cpu%d/online", cpu);
+ if (rc == -1)
+ printf(_("CPU %d disable failed (%m)\n"), cpu);
+ else {
+ printf(_("CPU %d disabled\n"), cpu);
+ if (onlinecpus)
+ CPU_CLR(cpu, onlinecpus);
+ }
+ }
+ }
+ return EXIT_SUCCESS;
+}
+
+static int cpu_rescan(void)
+{
+ if (!path_exist(_PATH_SYS_CPU_RESCAN))
+ errx(EXIT_FAILURE, _("This system does not support rescanning of CPUs"));
+ if (path_writestr("1", _PATH_SYS_CPU_RESCAN) == -1)
+ err(EXIT_FAILURE, _("Failed to trigger rescan of CPUs"));
+ printf(_("Triggered rescan of CPUs\n"));
+ return EXIT_SUCCESS;
+}
+
+static int cpu_set_dispatch(int mode)
+{
+ if (!path_exist(_PATH_SYS_CPU_DISPATCH))
+ errx(EXIT_FAILURE, _("This system does not support setting "
+ "the dispatching mode of CPUs"));
+ if (mode == 0) {
+ if (path_writestr("0", _PATH_SYS_CPU_DISPATCH) == -1)
+ err(EXIT_FAILURE, _("Failed to set horizontal dispatch mode"));
+ printf(_("Successfully set horizontal dispatching mode\n"));
+ } else {
+ if (path_writestr("1", _PATH_SYS_CPU_DISPATCH) == -1)
+ err(EXIT_FAILURE, _("Failed to set vertical dispatch mode"));
+ printf(_("Successfully set vertical dispatching mode\n"));
+ }
+ return EXIT_SUCCESS;
+}
+
+static int cpu_configure(cpu_set_t *cpu_set, size_t setsize, int configure)
+{
+ unsigned int cpu;
+ int rc, current;
+
+ for (cpu = 0; cpu < setsize; cpu++) {
+ if (!CPU_ISSET(cpu, cpu_set))
+ continue;
+ if (!path_exist(_PATH_SYS_CPU "/cpu%d", cpu)) {
+ printf(_("CPU %d does not exist\n"), cpu);
+ continue;
+ }
+ if (!path_exist(_PATH_SYS_CPU "/cpu%d/configure", cpu)) {
+ printf(_("CPU %d is not configurable\n"), cpu);
+ continue;
+ }
+ current = path_getnum(_PATH_SYS_CPU "/cpu%d/configure", cpu);
+ if ((current == 1) && (configure == 1)) {
+ printf(_("CPU %d is already configured\n"), cpu);
+ continue;
+ }
+ if ((current == 0) && (configure == 0)) {
+ printf(_("CPU %d is already deconfigured\n"), cpu);
+ continue;
+ }
+ if ((current == 1) && (configure == 0) && onlinecpus &&
+ is_cpu_online(cpu)) {
+ printf(_("CPU %d deconfigure failed "
+ "(CPU is enabled)\n"), cpu);
+ continue;
+ }
+ if (configure) {
+ rc = path_writestr("1", _PATH_SYS_CPU "/cpu%d/configure", cpu);
+ if (rc == -1)
+ printf(_("CPU %d configure failed (%m)\n"), cpu);
+ else
+ printf(_("CPU %d configured\n"), cpu);
+ } else {
+ rc = path_writestr("0", _PATH_SYS_CPU "/cpu%d/configure", cpu);
+ if (rc == -1)
+ printf(_("CPU %d deconfigure failed (%m)\n"), cpu);
+ else
+ printf(_("CPU %d deconfigured\n"), cpu);
+ }
+ }
+ return EXIT_SUCCESS;
+}
+
+static void cpu_parse(char *cpu_string, cpu_set_t *cpu_set, size_t setsize)
+{
+ int rc;
+
+ rc = cpulist_parse(cpu_string, cpu_set, setsize, 1);
+ if (rc == 0)
+ return;
+ if (rc == 2)
+ errx(EXIT_FAILURE, _("invalid CPU number in CPU list: %s"), cpu_string);
+ errx(EXIT_FAILURE, _("failed to parse CPU list: %s"), cpu_string);
+}
+
+static void __attribute__((__noreturn__)) usage(FILE *out)
+{
+ fprintf(out, _(
+ "\nUsage:\n"
+ " %s [options]\n"), program_invocation_short_name);
+
+ puts(_( "\nOptions:\n"
+ " -h, --help print this help\n"
+ " -e, --enable <cpu-list> enable cpus\n"
+ " -d, --disable <cpu-list> disable cpus\n"
+ " -c, --configure <cpu-list> configure cpus\n"
+ " -g, --deconfigure <cpu-list> deconfigure cpus\n"
+ " -p, --dispatch <mode> set dispatching mode\n"
+ " -r, --rescan trigger rescan of cpus\n"
+ " -V, --version output version information and exit\n"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+ cpu_set_t *cpu_set;
+ size_t setsize;
+ int cmd = -1;
+ int c;
+
+ static const struct option longopts[] = {
+ { "configure", required_argument, 0, 'c' },
+ { "deconfigure",required_argument, 0, 'g' },
+ { "disable", required_argument, 0, 'd' },
+ { "dispatch", required_argument, 0, 'p' },
+ { "enable", required_argument, 0, 'e' },
+ { "help", no_argument, 0, 'h' },
+ { "rescan", no_argument, 0, 'r' },
+ { "version", no_argument, 0, 'V' },
+ { NULL, 0, 0, 0 }
+ };
+
+ static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */
+ { 'c','d','e','g','p' },
+ { 0 }
+ };
+ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ maxcpus = get_max_number_of_cpus();
+ if (maxcpus < 1)
+ errx(EXIT_FAILURE, _("cannot determine NR_CPUS; aborting"));
+ if (path_exist(_PATH_SYS_CPU_ONLINE))
+ onlinecpus = path_cpulist(maxcpus, _PATH_SYS_CPU_ONLINE);
+ setsize = CPU_ALLOC_SIZE(maxcpus);
+ cpu_set = CPU_ALLOC(maxcpus);
+ if (!cpu_set)
+ err(EXIT_FAILURE, _("cpuset_alloc failed"));
+
+ while ((c = getopt_long(argc, argv, "c:d:e:g:hp:rV", longopts, NULL)) != -1) {
+
+ err_exclusive_options(c, longopts, excl, excl_st);
+
+ switch (c) {
+ case 'c':
+ cmd = CMD_CPU_CONFIGURE;
+ cpu_parse(argv[optind - 1], cpu_set, setsize);
+ break;
+ case 'd':
+ cmd = CMD_CPU_DISABLE;
+ cpu_parse(argv[optind - 1], cpu_set, setsize);
+ break;
+ case 'e':
+ cmd = CMD_CPU_ENABLE;
+ cpu_parse(argv[optind - 1], cpu_set, setsize);
+ break;
+ case 'g':
+ cmd = CMD_CPU_DECONFIGURE;
+ cpu_parse(argv[optind - 1], cpu_set, setsize);
+ break;
+ case 'h':
+ usage(stdout);
+ case 'p':
+ if (strcmp("horizontal", argv[optind - 1]) == 0)
+ cmd = CMD_CPU_DISPATCH_HORIZONTAL;
+ else if (strcmp("vertical", argv[optind - 1]) == 0)
+ cmd = CMD_CPU_DISPATCH_VERTICAL;
+ else
+ errx(EXIT_FAILURE, _("unsupported argument: %s"),
+ argv[optind -1 ]);
+ break;
+ case 'r':
+ cmd = CMD_CPU_RESCAN;
+ break;
+ case 'V':
+ printf(_("%s from %s\n"), program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ default:
+ usage(stderr);
+ }
+ }
+
+ if ((argc == 1) || (argc != optind))
+ usage(stderr);
+
+ switch (cmd) {
+ case CMD_CPU_ENABLE:
+ return cpu_enable(cpu_set, maxcpus, 1);
+ case CMD_CPU_DISABLE:
+ return cpu_enable(cpu_set, maxcpus, 0);
+ case CMD_CPU_CONFIGURE:
+ return cpu_configure(cpu_set, maxcpus, 1);
+ case CMD_CPU_DECONFIGURE:
+ return cpu_configure(cpu_set, maxcpus, 0);
+ case CMD_CPU_RESCAN:
+ return cpu_rescan();
+ case CMD_CPU_DISPATCH_HORIZONTAL:
+ return cpu_set_dispatch(0);
+ case CMD_CPU_DISPATCH_VERTICAL:
+ return cpu_set_dispatch(1);
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/sys-utils/ctrlaltdel.8 b/sys-utils/ctrlaltdel.8
new file mode 100644
index 0000000..14cf3dd
--- /dev/null
+++ b/sys-utils/ctrlaltdel.8
@@ -0,0 +1,51 @@
+.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH CTRLALTDEL 8 "August 2011" "util-linux" "System Administration"
+.SH NAME
+ctrlaltdel \- set the function of the Ctrl-Alt-Del combination
+.SH SYNOPSIS
+.BR "ctrlaltdel hard" | soft
+.SH DESCRIPTION
+Based on examination of the
+.I linux/kernel/sys.c
+code, it is clear that there are two supported functions that the
+Ctrl-Alt-Del sequence can perform: a
+.I hard
+reset, which immediately reboots the computer without calling
+.BR sync (2)
+and without any other preparation; and a
+.I soft
+reset, which sends the SIGINT (interrupt) signal to the
+.B init
+process (this is always the process with PID 1). If this option is used,
+the
+.BR init (8)
+program must support this feature. Since there are now several
+.BR init (8)
+programs in the Linux community, please consult the documentation for the
+version that you are currently using.
+.PP
+.B ctrlaltdel
+is usually used in the
+.I /etc/rc.local
+file.
+.SH OPTIONS
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Output version information and exit.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display help and exit.
+.SH FILES
+.I /etc/rc.local
+.SH "SEE ALSO"
+.BR init (8)
+.SH AUTHOR
+.UR poe@daimi.aau.dk
+Peter Orbaek
+.UE
+.SH AVAILABILITY
+The ctrlaltdel 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/sys-utils/ctrlaltdel.c b/sys-utils/ctrlaltdel.c
new file mode 100644
index 0000000..2ad56a1
--- /dev/null
+++ b/sys-utils/ctrlaltdel.c
@@ -0,0 +1,69 @@
+/*
+ * ctrlaltdel.c - Set the function of the Ctrl-Alt-Del combination
+ * Created 4-Jul-92 by Peter Orbaek <poe@daimi.aau.dk>
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ */
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "linux_reboot.h"
+#include "nls.h"
+#include "c.h"
+#include "closestream.h"
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fprintf(out, USAGE_HEADER);
+ fprintf(out, _(" %s <hard|soft>\n"), program_invocation_short_name);
+ fprintf(out, USAGE_OPTIONS);
+ fprintf(out, USAGE_HELP);
+ fprintf(out, USAGE_VERSION);
+ fprintf(out, USAGE_MAN_TAIL("ctrlaltdel(8)"));
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ int ch;
+ static const struct option longopts[] = {
+ {"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 ((ch = getopt_long(argc, argv, "Vh", longopts, NULL)) != -1)
+ switch (ch) {
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+
+ if (geteuid())
+ errx(EXIT_FAILURE,
+ _("You must be root to set the Ctrl-Alt-Del behaviour"));
+
+ if (argc == 2 && !strcmp("hard", argv[1])) {
+ if (my_reboot(LINUX_REBOOT_CMD_CAD_ON) < 0)
+ err(EXIT_FAILURE, "reboot");
+ } else if (argc == 2 && !strcmp("soft", argv[1])) {
+ if (my_reboot(LINUX_REBOOT_CMD_CAD_OFF) < 0)
+ err(EXIT_FAILURE, "reboot");
+ } else {
+ usage(stderr);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/sys-utils/cyclades.h b/sys-utils/cyclades.h
new file mode 100644
index 0000000..afcf600
--- /dev/null
+++ b/sys-utils/cyclades.h
@@ -0,0 +1,16 @@
+struct cyclades_monitor {
+ unsigned long int_count;
+ unsigned long char_count;
+ unsigned long char_max;
+ unsigned long char_last;
+};
+
+#define CYGETMON 0x435901
+#define CYGETTHRESH 0x435902
+#define CYSETTHRESH 0x435903
+#define CYGETDEFTHRESH 0x435904
+#define CYSETDEFTHRESH 0x435905
+#define CYGETTIMEOUT 0x435906
+#define CYSETTIMEOUT 0x435907
+#define CYGETDEFTIMEOUT 0x435908
+#define CYSETDEFTIMEOUT 0x435909
diff --git a/sys-utils/cytune.8 b/sys-utils/cytune.8
new file mode 100644
index 0000000..fd82391
--- /dev/null
+++ b/sys-utils/cytune.8
@@ -0,0 +1,194 @@
+.\" cytune.8 --
+.\" Created: Sat Mar 4 17:44:53 1995 by faith@cs.unc.edu
+.\" Update: Sat Mar 4 18:22:24 1995 by faith@cs.unc.edu
+.\" Update: Sun Mar 5 06:40:12 1995 by njs@scifi.emi.net
+.\" 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 CYTUNE 8 "September 2011" "util-linux" "System Administration"
+.SH NAME
+cytune \- tune driver parameters for Cyclades-Z multiport serial card
+.SH SYNOPSIS
+.B cytune
+[options] tty [...]
+.SH DESCRIPTION
+.B cytune
+queries and modifies the interruption threshold for the Cyclades driver.
+Each serial line on a Cyclades card has a 12-byte FIFO for input (and another
+12-byte FIFO for output). The "threshold" specifies how many input
+characters must be present in the FIFO before an interruption is raised.
+When a Cyclades tty is opened, this threshold is set to a default value based
+on baud rate:
+.sp
+.RS
+Baud Threshold
+.sp
+50-4800 10
+.br
+9600 8
+.br
+19200 4
+.br
+38400 2
+.br
+57600-150000 1
+.RE
+.PP
+If the threshold is set too low, the large number of interruptions can load
+the machine and decrease overall system throughput. If the threshold is set
+too high, the FIFO buffer can overflow, and characters will be lost. Slower
+machines, however, may not be able to deal with the interrupt load, and will
+require that the threshold be adjusted upwards.
+.PP
+If the cyclades driver was compiled with
+.B ENABLE_MONITORING
+defined, the cytune command can be used with the
+.B \-q
+option to report interrupts over the monitoring interval and characters
+transferred over the monitoring interval. It will also report the state of
+the FIFO. The maximum number of characters in the FIFO when an interrupt
+occurred, the instantaneous count of characters in the FIFO, and how many
+characters are now in the FIFO are reported. This output might look like
+this:
+.sp
+.RS
+/dev/cubC0: 830 ints, 9130 chars; fifo: 11 threshold, 11 max, 11 now
+.br
+ 166.259866 interrupts/second, 1828.858521 characters/second
+.RE
+.PP
+This output indicates that for this monitoring period, the interrupts were
+always being handled within one character time, because
+.B max
+never rose above
+.BR threshold .
+This is good, and you can probably run this way, provided that a large number
+of samples come out this way. You will lose characters if you overrun the
+FIFO, as the Cyclades hardware does not seem to support the RTS RS-232 signal
+line for hardware flow control from the DCE to the DTE.
+.PP
+In query mode
+.B cytune
+will produce a summary report when ended with a SIGINT or when the threshold
+or timeout is changed.
+.PP
+There may be a responsiveness vs. throughput tradeoff. The Cyclades card, at
+the higher speeds, is capable of putting a very high interrupt load on the
+system. This will reduce the amount of CPU time available for other tasks on
+your system. However, the time it takes to respond to a single character may
+be increased if you increase the threshold. This might be noticed by
+monitoring
+.BR ping (8)
+times on a SLIP link controlled by a Cyclades card. If your SLIP link is
+generally used for interactive work such as
+.BR telnet (1),
+you may want to leave the threshold low, so that characters are responded to
+as quickly as possible. If your SLIP link is generally used for file
+transfer, WWW, and the like, setting the FIFO to a high value is likely to
+reduce the load on your system while not significantly affecting throughput.
+Alternatively, see the
+.B \-t
+or
+.B \-T
+options to adjust the time that the cyclades waits before flushing its
+buffer. Units are 5ms.
+.PP
+If you are running a mouse on a Cyclades port, it is likely that you would
+want to maintain the threshold and timeout at a low value.
+.PP
+.SH OPTIONS
+.TP
+\fB\-s\fR, \fB\-\-set\-threshold\fR \fIvalue\fR
+Set the current threshold to
+.I value
+characters. Note that if the
+.I tty
+is not being held open by another process, the threshold will be reset on the
+next open. Only values between 1 and 12, inclusive, are permitted.
+.TP
+\fB\-t\fR, \fB\-\-set\-flush\fR \fIvalue\fR
+Set the current flush timeout to
+.I value
+units. Note that if the
+.I tty
+is not being held open by another process, the threshold will be reset on the
+next open. Only values between 0 and 255, inclusive, are permitted. Setting
+.I value
+to zero forces the default, currently 0x20 (160ms), but soon to be 0x02
+(10ms). Units are 5 ms.
+.TP
+\fB\-g\fR, \fB\-\-get\-threshold\fR
+Get the current threshold and timeout.
+.TP
+\fB\-S\fR, \fB\-\-set\-default\-threshold\fR \fIvalue\fR
+Set the default threshold to
+.I value
+characters. When the
+.I tty
+is next opened, this value will be used instead of the default. Only values
+between 1 and 12, inclusive, are permitted.
+.TP
+\fB\-T\fR, \fB\-\-set\-default\-flush\fR \fIvalue\fR
+Set the default flush timeout to
+.I value
+units. When the
+.I tty
+is next opened, this value will be used instead of the default. If
+.I value
+is zero, then the the value will default to 0x20 (160ms), soon to be 0x02
+(10ms).
+.TP
+\fB\-G\fR, \fB\-\-get\-glush\fR
+Get the default threshold and flush timeout values.
+.TP
+\fB\-q\fR, \fB\-\-stats\fR
+Gather statistics about the
+.IR tty .
+The results are only valid if the Cyclades driver has been compiled with
+.B ENABLE_MONITORING
+defined. This is probably not the default.
+.TP
+\fB\-i\fR, \fB\-\-interval\fR \fIinterval\fR
+Statistics will be gathered every
+.I interval
+seconds.
+.SH BUGS
+If you run two copies of
+.B cytune
+at the same time to report statistics about the same port, the 'ints', 'chars',
+and 'max' value will be reset and not reported correctly.
+.B cytune
+should prevent this, but does not.
+.\" .SH AUTHOR
+.\" Nick Simicich (njs@scifi.emi.net), with modifications by
+.\" Rik Faith (faith@cs.unc.edu)
+.SH FILES
+.I /dev/ttyC[0-8]
+.br
+.I /dev/cubC[0-8]
+.SH "SEE ALSO"
+.BR setserial (8)
+.SH AVAILABILITY
+The cytune 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/sys-utils/cytune.c b/sys-utils/cytune.c
new file mode 100644
index 0000000..9b39726
--- /dev/null
+++ b/sys-utils/cytune.c
@@ -0,0 +1,461 @@
+/* cytune.c -- Tune Cyclades driver
+ *
+ * Copyright 1995 Nick Simicich (njs@scifi.emi.net)
+ * Modifications by Rik Faith (faith@cs.unc.edu)
+ *
+ * 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 Nick Simicich
+ * 4. Neither the name of the Nick Simicich 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 NICK SIMICICH 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 NICK SIMICICH 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 <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <termios.h>
+
+#include "c.h"
+#include "cyclades.h"
+#include "closestream.h"
+#include "strutils.h"
+
+#if 0
+# ifndef XMIT
+# include <linux/version.h>
+# if LINUX_VERSION_CODE > 66056
+# define XMIT
+# endif
+# endif
+#endif
+
+#include "xalloc.h"
+#include "nls.h"
+/* Until it gets put in the kernel, toggle by hand. */
+#undef XMIT
+
+struct cyclades_control {
+ struct cyclades_monitor c;
+ int cfile;
+ int maxmax;
+ double maxtran;
+ double maxxmit;
+ unsigned long threshold_value;
+ unsigned long timeout_value;
+};
+struct cyclades_control *cmon;
+int cmon_index;
+
+static int global_argc, global_optind;
+static char ***global_argv;
+
+#define mvtime(tvpto, tvpfrom) (((tvpto)->tv_sec = (tvpfrom)->tv_sec),(tvpto)->tv_usec = (tvpfrom)->tv_usec)
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fprintf(out, USAGE_HEADER);
+ fprintf(out, _(" %s [options] <tty> [...]\n"), program_invocation_short_name);
+ fprintf(out, USAGE_OPTIONS);
+ fprintf(out, _(" -s, --set-threshold <num> set interruption threshold value\n"));
+ fprintf(out, _(" -g, --get-threshold display current threshold value\n"));
+ fprintf(out, _(" -S, --set-default-threshold <num> set default threshold value\n"));
+ fprintf(out, _(" -t, --set-flush <num> set flush timeout to value\n"));
+ fprintf(out, _(" -G, --get-glush display default flush timeout value\n"));
+ fprintf(out, _(" -T, --set-default-flush <num> set the default flush timeout to value\n"));
+ fprintf(out, _(" -q, --stats display statistics about the tty\n"));
+ fprintf(out, _(" -i, --interval <seconds> gather statistics every <seconds> interval\n"));
+ fprintf(out, USAGE_SEPARATOR);
+ fprintf(out, USAGE_HELP);
+ fprintf(out, USAGE_VERSION);
+ fprintf(out, USAGE_MAN_TAIL("cytune(8)"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static inline double dtime(struct timeval *tvpnew, struct timeval *tvpold)
+{
+ double diff;
+ diff = (double)tvpnew->tv_sec - (double)tvpold->tv_sec;
+ diff += ((double)tvpnew->tv_usec - (double)tvpold->tv_usec) / 1000000;
+ return diff;
+}
+
+static void summary(int sig)
+{
+ struct cyclades_control *cc;
+ int argc, local_optind;
+ char **argv;
+ int i;
+
+ argc = global_argc;
+ argv = *global_argv;
+ local_optind = global_optind;
+
+ if (sig > 0) {
+ for (i = local_optind; i < argc; i++) {
+ cc = &cmon[cmon_index];
+ warnx(_("File %s, For threshold value %lu, Maximum characters in fifo were %d,\n"
+ "and the maximum transfer rate in characters/second was %f"),
+ argv[i], cc->threshold_value, cc->maxmax,
+ cc->maxtran);
+ }
+ exit(EXIT_SUCCESS);
+ }
+ cc = &cmon[cmon_index];
+ if (cc->threshold_value > 0 && sig != -1) {
+ warnx(_("File %s, For threshold value %lu and timeout value %lu, Maximum characters in fifo were %d,\n"
+ "and the maximum transfer rate in characters/second was %f"),
+ argv[cmon_index + local_optind], cc->threshold_value,
+ cc->timeout_value, cc->maxmax, cc->maxtran);
+ }
+ cc->maxmax = 0;
+ cc->maxtran = 0.0;
+ cc->threshold_value = 0;
+ cc->timeout_value = 0;
+}
+
+static void query_tty_stats(int argc, char **argv, int interval, int numfiles,
+ unsigned long *threshold_value,
+ unsigned long *timeout_value)
+{
+ struct cyclades_monitor cywork;
+ struct timeval lasttime, thistime;
+ struct timezone tz = { 0, 0 };
+ int i;
+ double diff;
+ double xfer_rate;
+#ifdef XMIT
+ double xmit_rate;
+#endif
+
+ cmon = xmalloc(sizeof(struct cyclades_control) * numfiles);
+
+ if (signal(SIGINT, summary) ||
+ signal(SIGQUIT, summary) || signal(SIGTERM, summary))
+ err(EXIT_FAILURE, _("cannot set signal handler"));
+ if (gettimeofday(&lasttime, &tz))
+ err(EXIT_FAILURE, _("gettimeofday failed"));
+
+ for (i = optind; i < argc; i++) {
+ cmon_index = i - optind;
+ cmon[cmon_index].cfile = open(argv[i], O_RDONLY);
+ if (cmon[cmon_index].cfile == -1)
+ err(EXIT_FAILURE, _("cannot open %s"), argv[i]);
+ if (ioctl
+ (cmon[cmon_index].cfile, CYGETMON, &cmon[cmon_index].c))
+ err(EXIT_FAILURE, _("cannot issue CYGETMON on %s"),
+ argv[i]);
+ summary(-1);
+ if (ioctl
+ (cmon[cmon_index].cfile, CYGETTHRESH, &threshold_value))
+ err(EXIT_FAILURE, _("cannot get threshold for %s"),
+ argv[i]);
+ if (ioctl(cmon[cmon_index].cfile, CYGETTIMEOUT, &timeout_value))
+ err(EXIT_FAILURE, _("cannot get timeout for %s"),
+ argv[i]);
+ }
+ while (1) {
+ sleep(interval);
+
+ if (gettimeofday(&thistime, &tz))
+ err(EXIT_FAILURE, _("gettimeofday failed"));
+ diff = dtime(&thistime, &lasttime);
+ mvtime(&lasttime, &thistime);
+
+ for (i = optind; i < argc; i++) {
+ cmon_index = i - optind;
+ if (ioctl(cmon[cmon_index].cfile, CYGETMON, &cywork))
+ err(EXIT_FAILURE,
+ _("cannot issue CYGETMON on %s"), argv[i]);
+ if (ioctl
+ (cmon[cmon_index].cfile, CYGETTHRESH,
+ &threshold_value))
+ err(EXIT_FAILURE,
+ _("cannot get threshold for %s"), argv[i]);
+ if (ioctl
+ (cmon[cmon_index].cfile, CYGETTIMEOUT,
+ &timeout_value))
+ err(EXIT_FAILURE,
+ _("cannot get timeout for %s"), argv[i]);
+
+ xfer_rate = cywork.char_count / diff;
+#ifdef XMIT
+ xmit_rate = cywork.send_count / diff;
+#endif
+ if ((*threshold_value) !=
+ cmon[cmon_index].threshold_value
+ || (*timeout_value) !=
+ cmon[cmon_index].timeout_value) {
+ summary(-2);
+ /* Note that the summary must come before the
+ * setting of threshold_value */
+ cmon[cmon_index].threshold_value =
+ (*threshold_value);
+ cmon[cmon_index].timeout_value =
+ (*timeout_value);
+ } else {
+ /* Don't record this first cycle after change */
+ if (xfer_rate > cmon[cmon_index].maxtran)
+ cmon[cmon_index].maxtran = xfer_rate;
+#ifdef XMIT
+ if (xmit_rate > cmon[cmon_index].maxxmit)
+ cmon[cmon_index].maxxmit = xmit_rate;
+#endif
+ if (cmon[cmon_index].maxmax < 0 ||
+ cywork.char_max >
+ (unsigned long)cmon[cmon_index].maxmax)
+ cmon[cmon_index].maxmax =
+ cywork.char_max;
+ }
+
+#ifdef XMIT
+ printf(_("%s: %lu ints, %lu/%lu chars; fifo: %lu thresh, %lu tmout, "
+ "%lu max, %lu now\n"), argv[i],
+ cywork.int_count, cywork.char_count,
+ cywork.send_count, *threshold_value,
+ *timeout_value, cywork.char_max,
+ cywork.char_last);
+ printf(_(" %f int/sec; %f rec, %f send (char/sec)\n"),
+ cywork.int_count / diff, xfer_rate, xmit_rate);
+#else
+ printf(_("%s: %lu ints, %lu chars; fifo: %lu thresh, %lu tmout, "
+ "%lu max, %lu now\n"), argv[i],
+ cywork.int_count, cywork.char_count,
+ *threshold_value, *timeout_value, cywork.char_max,
+ cywork.char_last);
+ printf(_(" %f int/sec; %f rec (char/sec)\n"),
+ cywork.int_count / diff, xfer_rate);
+#endif
+ memcpy(&cmon[cmon_index].c, &cywork,
+ sizeof(struct cyclades_monitor));
+ }
+ }
+
+ free(cmon);
+ return;
+}
+
+int main(int argc, char **argv)
+{
+ int query = 0;
+ int interval = 1;
+ int set = 0;
+ int set_val = -1;
+ int get = 0;
+ int set_def = 0;
+ int set_def_val = -1;
+ int get_def = 0;
+ int set_time = 0;
+ int set_time_val = -1;
+ int set_def_time = 0;
+ int set_def_time_val = -1;
+ int errflg = 0;
+ int file;
+ int numfiles;
+ int i;
+ unsigned long threshold_value;
+ unsigned long timeout_value;
+
+ static const struct option longopts[] = {
+ {"set-threshold", required_argument, NULL, 's'},
+ {"get-threshold", no_argument, NULL, 'g'},
+ {"set-default-threshold", required_argument, NULL, 'S'},
+ {"set-flush", required_argument, NULL, 't'},
+ {"get-flush", no_argument, NULL, 'G'},
+ {"set-default-flush", required_argument, NULL, 'T'},
+ {"stats", no_argument, NULL, 'q'},
+ {"interval", required_argument, NULL, 'i'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ /* For signal routine. */
+ global_argc = argc;
+ global_argv = &argv;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while ((i =
+ getopt_long(argc, argv, "qs:S:t:T:gGi:Vh", longopts,
+ NULL)) != -1) {
+ switch (i) {
+ case 'q':
+ query = 1;
+ break;
+ case 'i':
+ interval = strtou32_or_err(optarg,
+ _("Invalid interval value"));
+ if (interval < 1) {
+ warnx(_("Invalid interval value: %d"),
+ interval);
+ errflg++;
+ }
+ break;
+ case 's':
+ ++set;
+ set_val = strtou32_or_err(optarg, _("Invalid set value"));
+ if (set_val < 1 || 12 < set_val) {
+ warnx(_("Invalid set value: %d"), set_val);
+ errflg++;
+ }
+ break;
+ case 'S':
+ ++set_def;
+ set_def_val = strtou32_or_err(optarg,
+ _("Invalid default value"));
+ if (set_def_val < 0 || 12 < set_def_val) {
+ warnx(_("Invalid default value: %d"),
+ set_def_val);
+ errflg++;
+ }
+ break;
+ case 't':
+ ++set_time;
+ set_time_val = strtou32_or_err(optarg,
+ _("Invalid set time value"));
+ if (set_time_val < 1 || 255 < set_time_val) {
+ warnx(_("Invalid set time value: %d"),
+ set_time_val);
+ errflg++;
+ }
+ break;
+ case 'T':
+ ++set_def_time;
+ set_def_time_val = strtou32_or_err(optarg,
+ _("Invalid default time value"));
+ if (set_def_time_val < 0 || 255 < set_def_time_val) {
+ warnx(_("Invalid default time value: %d"),
+ set_def_time_val);
+ errflg++;
+ }
+ break;
+ case 'g':
+ ++get;
+ break;
+ case 'G':
+ ++get_def;
+ break;
+ case 'V':
+ printf(_("%s from %s\n"), program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+ }
+ numfiles = argc - optind;
+
+ if (errflg
+ || (numfiles == 0)
+ || (!query && !set && !set_def && !get && !get_def && !set_time && !set_def_time)
+ || (set && set_def)
+ || (set_time && set_def_time)
+ || (get && get_def))
+ usage(stderr);
+
+ /* For signal routine. */
+ global_optind = optind;
+
+ if (set || set_def) {
+ for (i = optind; i < argc; i++) {
+ file = open(argv[i], O_RDONLY);
+ if (file == -1)
+ err(EXIT_FAILURE, _("cannot open %s"), argv[i]);
+ if (ioctl(file,
+ set ? CYSETTHRESH : CYSETDEFTHRESH,
+ set ? set_val : set_def_val))
+ err(EXIT_FAILURE,
+ _("cannot set %s to threshold %d"), argv[i],
+ set ? set_val : set_def_val);
+ close(file);
+ }
+ }
+ if (set_time || set_def_time) {
+ for (i = optind; i < argc; i++) {
+ file = open(argv[i], O_RDONLY);
+ if (file == -1)
+ err(EXIT_FAILURE, _("cannot open %s"), argv[i]);
+ if (ioctl(file,
+ set_time ? CYSETTIMEOUT : CYSETDEFTIMEOUT,
+ set_time ? set_time_val : set_def_time_val))
+ err(EXIT_FAILURE,
+ _("cannot set %s to time threshold %d"),
+ argv[i],
+ set_time ? set_time_val : set_def_time_val);
+ close(file);
+ }
+ }
+
+ if (get || get_def) {
+ for (i = optind; i < argc; i++) {
+ file = open(argv[i], O_RDONLY);
+ if (file == -1)
+ err(EXIT_FAILURE, _("cannot open %s"), argv[i]);
+ if (ioctl
+ (file, get ? CYGETTHRESH : CYGETDEFTHRESH,
+ &threshold_value))
+ err(EXIT_FAILURE,
+ _("cannot get threshold for %s"), argv[i]);
+ if (ioctl
+ (file, get ? CYGETTIMEOUT : CYGETDEFTIMEOUT,
+ &timeout_value))
+ err(EXIT_FAILURE,
+ _("cannot get timeout for %s"), argv[i]);
+ close(file);
+ if (get)
+ printf(_("%s: %ld current threshold and %ld current timeout\n"),
+ argv[i], threshold_value, timeout_value);
+ else
+ printf(_("%s: %ld default threshold and %ld default timeout\n"),
+ argv[i], threshold_value, timeout_value);
+ }
+ }
+
+ if (!query)
+ return EXIT_SUCCESS;
+
+ query_tty_stats(argc, argv, interval, numfiles, &threshold_value, &timeout_value);
+
+ return EXIT_SUCCESS;
+}
diff --git a/sys-utils/dmesg.1 b/sys-utils/dmesg.1
new file mode 100644
index 0000000..6a8374a
--- /dev/null
+++ b/sys-utils/dmesg.1
@@ -0,0 +1,162 @@
+.\" Copyright 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH DMESG "1" "July 2012" "util-linux" "User Commands"
+.SH NAME
+dmesg \- print or control the kernel ring buffer
+.SH SYNOPSIS
+.B dmesg
+.RB [ options ]
+.sp
+dmesg \-\-clear
+.br
+dmesg \-\-read-clear [options]
+.br
+dmesg \-\-console-level level
+.br
+dmesg \-\-console-on
+.br
+dmesg \-\-console-off
+.SH DESCRIPTION
+.B dmesg
+is used to examine or control the kernel ring buffer.
+.PP
+The default action is to read all messages from kernel ring buffer.
+.SH OPTIONS
+The \-\-clear, \-\-read-clear, \-\-console-on, \-\-console-off and
+\-\-console-level options are mutually exclusive.
+.PP
+.IP "\fB\-C\fR, \fB\-\-clear\fR"
+Clear the ring buffer.
+.IP "\fB\-c\fR, \fB\-\-read-clear\fR"
+Clear the ring buffer contents after printing.
+.IP "\fB\-D\fR, \fB\-\-console-off\fR"
+Disable printing messages to the console.
+.IP "\fB\-d\fR, \fB\-\-show-delta\fR"
+Display the timestamp and time delta spent between messages. If used
+together with
+.B \-\-notime
+then only the time delta without the timestamp is printed.
+.IP "\fB\-e\fR, \fB\-\-reltime\fR"
+Display the local time and delta in human readable format.
+.IP "\fB\-E\fR, \fB\-\-console-on\fR"
+Enable printing messages to the console.
+.IP "\fB\-F\fR, \fB\-\-file \fIfile\fR"
+Read log from
+.IR file .
+.IP "\fB\-f\fR, \fB\-\-facility \fIlist\fR"
+Restrict output to defined (comma separated)
+.I list
+of facilities. For example
+.PP
+.RS 14
+dmesg \-\-facility=daemon
+.RE
+.IP
+will print messages from system daemons only. For all supported facilities
+see
+.B dmesg \-\-help
+output.
+.IP "\fB\-h\fR, \fB\-\-help\fR"
+Print a help text and exit.
+.IP "\fB\-k\fR, \fB\-\-kernel\fR"
+Print kernel messages.
+.IP "\fB\-l\fR, \fB\-\-level \fIlist\fR"
+Restrict output to defined (comma separated)
+.I list
+of levels. For example
+.PP
+.RS 14
+dmesg \-\-level=err,warn
+.RE
+.IP
+will print error and warning messages only. For all supported levels see
+.B dmesg \-\-help
+output.
+.IP "\fB\-n\fR, \fB\-\-console-level \fIlevel\fR
+Set the
+.I level
+at which logging of messages is done to the console. The
+.I level
+is a level number or abbreviation of the level name. For all supported
+levels see
+.B dmesg \-\-help
+output.
+.sp
+For example,
+.B \-n 1
+or
+.B \-n alert
+prevents all messages, except emergency (panic) messages, from appearing on
+the console. All levels of messages are still written to
+.IR /proc/kmsg ,
+so
+.BR syslogd (8)
+can still be used to control exactly where kernel messages appear. When the
+.B \-n
+option is used,
+.B dmesg
+will
+.I not
+print or clear the kernel ring buffer.
+.IP "\fB\-r\fR, \fB\-\-raw\fR"
+Print the raw message buffer, i.e., do not strip the log level prefixes.
+
+Note that the real raw format depends on method how
+.BR dmesg (1)
+reads kernel messages. The /dev/kmsg uses different format than
+.BR syslog (2) .
+For backward compatibility
+.BR dmesg (1)
+returns data always in
+.BR syslog (2)
+format. The real raw data from /dev/kmsg is possible to read for example by
+command 'dd if=/dev/kmsg iflag=nonblock'.
+.IP "\fB\-S\fR, \fB\-\-syslog\fR"
+Force to use
+.BR syslog (2)
+kernel interface to read kernel messages. The default is to use /dev/kmsg rather
+than
+.BR syslog (2)
+since kernel 3.5.0.
+.IP "\fB\-s\fR, \fB\-\-buffer-size \fIsize\fR
+Use a buffer of
+.I size
+to query the kernel ring buffer. This is 16392 by default. (The default
+kernel syslog buffer size was 4096 at first, 8192 since 1.3.54, 16384 since
+2.1.113.) If you have set the kernel buffer to be larger than the default
+then this option can be used to view the entire buffer.
+.IP "\fB\-T\fR, \fB\-\-ctime\fR"
+Print human readable timestamps. The timestamp could be inaccurate!
+.IP
+The
+.B time
+source used for the logs is
+.B not updated after
+system
+.BR SUSPEND / RESUME .
+.IP "\fB\-t\fR, \fB\-\-notime\fR"
+Do not print kernel's timestamps.
+.IP "\fB\-u\fR, \fB\-\-userspace\fR"
+Print userspace messages.
+.IP "\fB\-V\fR, \fB\-\-version\fR"
+Output version information and exit.
+.IP "\fB\-w\fR, \fB\-\-follow\fR"
+Wait for new messages. This feature is supported on systems with readable
+/dev/kmsg only (since kernel 3.5.0).
+.IP "\fB\-x\fR, \fB\-\-decode\fR"
+Decode facility and level (priority) number to human readable prefixes.
+.SH SEE ALSO
+.BR syslogd (8)
+.SH AUTHORS
+.MT kzak@redhat.com
+Karel Zak
+.ME
+.br
+.MT tytso@athena.mit.edu
+Theodore Ts'o
+.ME
+.SH AVAILABILITY
+The dmesg 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/sys-utils/dmesg.c b/sys-utils/dmesg.c
new file mode 100644
index 0000000..0ee03ee
--- /dev/null
+++ b/sys-utils/dmesg.c
@@ -0,0 +1,1214 @@
+/*
+ * dmesg.c -- Print out the contents of the kernel ring buffer
+ *
+ * Copyright (C) 1993 Theodore Ts'o <tytso@athena.mit.edu>
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ *
+ * This program comes with ABSOLUTELY NO WARRANTY.
+ */
+#include <linux/unistd.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <sys/klog.h>
+#include <sys/syslog.h>
+#include <sys/time.h>
+#include <sys/sysinfo.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "xalloc.h"
+#include "widechar.h"
+#include "all-io.h"
+#include "bitops.h"
+#include "closestream.h"
+#include "optutils.h"
+#include "mangle.h"
+
+/* Close the log. Currently a NOP. */
+#define SYSLOG_ACTION_CLOSE 0
+/* Open the log. Currently a NOP. */
+#define SYSLOG_ACTION_OPEN 1
+/* Read from the log. */
+#define SYSLOG_ACTION_READ 2
+/* Read all messages remaining in the ring buffer. (allowed for non-root) */
+#define SYSLOG_ACTION_READ_ALL 3
+/* Read and clear all messages remaining in the ring buffer */
+#define SYSLOG_ACTION_READ_CLEAR 4
+/* Clear ring buffer. */
+#define SYSLOG_ACTION_CLEAR 5
+/* Disable printk's to console */
+#define SYSLOG_ACTION_CONSOLE_OFF 6
+/* Enable printk's to console */
+#define SYSLOG_ACTION_CONSOLE_ON 7
+/* Set level of messages printed to console */
+#define SYSLOG_ACTION_CONSOLE_LEVEL 8
+/* Return number of unread characters in the log buffer */
+#define SYSLOG_ACTION_SIZE_UNREAD 9
+/* Return size of the log buffer */
+#define SYSLOG_ACTION_SIZE_BUFFER 10
+
+/*
+ * Priority and facility names
+ */
+struct dmesg_name {
+ const char *name;
+ const char *help;
+};
+
+/*
+ * Priority names -- based on sys/syslog.h
+ */
+static const struct dmesg_name level_names[] =
+{
+ [LOG_EMERG] = { "emerg", N_("system is unusable") },
+ [LOG_ALERT] = { "alert", N_("action must be taken immediately") },
+ [LOG_CRIT] = { "crit", N_("critical conditions") },
+ [LOG_ERR] = { "err", N_("error conditions") },
+ [LOG_WARNING] = { "warn", N_("warning conditions") },
+ [LOG_NOTICE] = { "notice",N_("normal but significant condition") },
+ [LOG_INFO] = { "info", N_("informational") },
+ [LOG_DEBUG] = { "debug", N_("debug-level messages") }
+};
+
+/*
+ * sys/syslog.h uses (f << 3) for all facility codes.
+ * We want to use the codes as array idexes, so shift back...
+ *
+ * Note that libc LOG_FAC() macro returns the base codes, not the
+ * shifted code :-)
+ */
+#define FAC_BASE(f) ((f) >> 3)
+
+static const struct dmesg_name facility_names[] =
+{
+ [FAC_BASE(LOG_KERN)] = { "kern", N_("kernel messages") },
+ [FAC_BASE(LOG_USER)] = { "user", N_("random user-level messages") },
+ [FAC_BASE(LOG_MAIL)] = { "mail", N_("mail system") },
+ [FAC_BASE(LOG_DAEMON)] = { "daemon", N_("system daemons") },
+ [FAC_BASE(LOG_AUTH)] = { "auth", N_("security/authorization messages") },
+ [FAC_BASE(LOG_SYSLOG)] = { "syslog", N_("messages generated internally by syslogd") },
+ [FAC_BASE(LOG_LPR)] = { "lpr", N_("line printer subsystem") },
+ [FAC_BASE(LOG_NEWS)] = { "news", N_("network news subsystem") },
+ [FAC_BASE(LOG_UUCP)] = { "uucp", N_("UUCP subsystem") },
+ [FAC_BASE(LOG_CRON)] = { "cron", N_("clock daemon") },
+ [FAC_BASE(LOG_AUTHPRIV)] = { "authpriv", N_("security/authorization messages (private)") },
+ [FAC_BASE(LOG_FTP)] = { "ftp", N_("ftp daemon") },
+};
+
+/* supported methods to read message buffer
+ */
+enum {
+ DMESG_METHOD_KMSG, /* read messages from /dev/kmsg (default) */
+ DMESG_METHOD_SYSLOG, /* klogctl() buffer */
+ DMESG_METHOD_MMAP /* mmap file with records (see --file) */
+};
+
+struct dmesg_control {
+ /* bit arrays -- see include/bitops.h */
+ char levels[ARRAY_SIZE(level_names) / NBBY + 1];
+ char facilities[ARRAY_SIZE(facility_names) / NBBY + 1];
+
+ struct timeval lasttime; /* last printed timestamp */
+ struct tm lasttm; /* last localtime */
+ time_t boot_time; /* system boot time */
+
+ int action; /* SYSLOG_ACTION_* */
+ int method; /* DMESG_METHOD_* */
+
+ size_t bufsize; /* size of syslog buffer */
+
+ int kmsg; /* /dev/kmsg file descriptor */
+ ssize_t kmsg_first_read;/* initial read() return code */
+ char kmsg_buf[BUFSIZ];/* buffer to read kmsg data */
+
+ /*
+ * For the --file option we mmap whole file. The unnecessary (already
+ * printed) pages are always unmapped. The result is that we have in
+ * memory only the currently used page(s).
+ */
+ char *filename;
+ char *mmap_buff;
+ size_t pagesize;
+
+ unsigned int follow:1, /* wait for new messages */
+ raw:1, /* raw mode */
+ fltr_lev:1, /* filter out by levels[] */
+ fltr_fac:1, /* filter out by facilities[] */
+ decode:1, /* use "facility: level: " prefix */
+ notime:1, /* don't print timestamp */
+ delta:1, /* show time deltas */
+ reltime:1, /* show human readable relative times */
+ ctime:1; /* show human readable time */
+};
+
+struct dmesg_record {
+ const char *mesg;
+ size_t mesg_size;
+
+ int level;
+ int facility;
+ struct timeval tv;
+
+ const char *next; /* buffer with next unparsed record */
+ size_t next_size; /* size of the next buffer */
+};
+
+#define INIT_DMESG_RECORD(_r) do { \
+ (_r)->mesg = NULL; \
+ (_r)->mesg_size = 0; \
+ (_r)->facility = -1; \
+ (_r)->level = -1; \
+ (_r)->tv.tv_sec = 0; \
+ (_r)->tv.tv_usec = 0; \
+ } while (0)
+
+static int read_kmsg(struct dmesg_control *ctl);
+
+
+static void __attribute__((__noreturn__)) usage(FILE *out)
+{
+ size_t i;
+
+ fputs(_("\nUsage:\n"), out);
+ fprintf(out,
+ _(" %s [options]\n"), program_invocation_short_name);
+
+ fputs(_("\nOptions:\n"), out);
+ fputs(_(" -C, --clear clear the kernel ring buffer\n"
+ " -c, --read-clear read and clear all messages\n"
+ " -D, --console-off disable printing messages to console\n"
+ " -d, --show-delta show time delta between printed messages\n"
+ " -e, --reltime show local time and time delta in readable format\n"
+ " -E, --console-on enable printing messages to console\n"
+ " -F, --file <file> use the file instead of the kernel log buffer\n"
+ " -f, --facility <list> restrict output to defined facilities\n"
+ " -h, --help display this help and exit\n"
+ " -k, --kernel display kernel messages\n"
+ " -l, --level <list> restrict output to defined levels\n"
+ " -n, --console-level <level> set level of messages printed to console\n"
+ " -r, --raw print the raw message buffer\n"
+ " -S, --syslog force to use syslog(2) rather than /dev/kmsg\n"
+ " -s, --buffer-size <size> buffer size to query the kernel ring buffer\n"
+ " -T, --ctime show human readable timestamp (could be \n"
+ " inaccurate if you have used SUSPEND/RESUME)\n"
+ " -t, --notime don't print messages timestamp\n"
+ " -u, --userspace display userspace messages\n"
+ " -V, --version output version information and exit\n"
+ " -w, --follow wait for new messages\n"
+ " -x, --decode decode facility and level to readable string\n"), out);
+
+ fputs(_("\nSupported log facilities:\n"), out);
+ for (i = 0; i < ARRAY_SIZE(level_names); i++) {
+ fprintf(stderr, " %7s - %s\n",
+ facility_names[i].name,
+ _(facility_names[i].help));
+ }
+
+ fputs(_("\nSupported log levels (priorities):\n"), out);
+ for (i = 0; i < ARRAY_SIZE(level_names); i++) {
+ fprintf(stderr, " %7s - %s\n",
+ level_names[i].name,
+ _(level_names[i].help));
+ }
+
+ fputc('\n', out);
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+/*
+ * LEVEL ::= <number> | <name>
+ * <number> ::= number in range <0..N>, where N < ARRAY_SIZE(level_names)
+ * <name> ::= case-insensitive text
+ */
+static int parse_level(const char *str, size_t len)
+{
+ if (!str)
+ return -1;
+ if (!len)
+ len = strlen(str);
+ errno = 0;
+
+ if (isdigit(*str)) {
+ char *end = NULL;
+ long x = strtol(str, &end, 10);
+
+ if (!errno && end && end > str && (size_t) (end - str) == len &&
+ x >= 0 && (size_t) x < ARRAY_SIZE(level_names))
+ return x;
+ } else {
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(level_names); i++) {
+ const char *n = level_names[i].name;
+
+ if (strncasecmp(str, n, len) == 0 && *(n + len) == '\0')
+ return i;
+ }
+ }
+
+ if (errno)
+ err(EXIT_FAILURE, _("failed to parse level '%s'"), str);
+
+ errx(EXIT_FAILURE, _("unknown level '%s'"), str);
+ return -1;
+}
+
+/*
+ * FACILITY ::= <number> | <name>
+ * <number> ::= number in range <0..N>, where N < ARRAY_SIZE(facility_names)
+ * <name> ::= case-insensitive text
+ */
+static int parse_facility(const char *str, size_t len)
+{
+ if (!str)
+ return -1;
+ if (!len)
+ len = strlen(str);
+ errno = 0;
+
+ if (isdigit(*str)) {
+ char *end = NULL;
+ long x = strtol(str, &end, 10);
+
+ if (!errno && end && end > str && (size_t) (end - str) == len &&
+ x >= 0 && (size_t) x < ARRAY_SIZE(facility_names))
+ return x;
+ } else {
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(facility_names); i++) {
+ const char *n = facility_names[i].name;
+
+ if (strncasecmp(str, n, len) == 0 && *(n + len) == '\0')
+ return i;
+ }
+ }
+
+ if (errno)
+ err(EXIT_FAILURE, _("failed to parse facility '%s'"), str);
+
+ errx(EXIT_FAILURE, _("unknown facility '%s'"), str);
+ return -1;
+}
+
+/*
+ * Parses numerical prefix used for all messages in kernel ring buffer.
+ *
+ * Priorities/facilities are encoded into a single 32-bit quantity, where the
+ * bottom 3 bits are the priority (0-7) and the top 28 bits are the facility
+ * (0-big number).
+ *
+ * Note that the number has to end with '>' or ',' char.
+ */
+static const char *parse_faclev(const char *str, int *fac, int *lev)
+{
+ long num;
+ char *end = NULL;
+
+ if (!str)
+ return str;
+
+ errno = 0;
+ num = strtol(str, &end, 10);
+
+ if (!errno && end && end > str) {
+ *fac = LOG_FAC(num);
+ *lev = LOG_PRI(num);
+
+ if (*lev < 0 || (size_t) *lev > ARRAY_SIZE(level_names))
+ *lev = -1;
+ if (*fac < 0 || (size_t) *fac > ARRAY_SIZE(facility_names))
+ *fac = -1;
+ return end + 1; /* skip '<' or ',' */
+ }
+
+ return str;
+}
+
+/*
+ * Parses timestamp from syslog message prefix, expected format:
+ *
+ * seconds.microseconds]
+ *
+ * the ']' is the timestamp field terminator.
+ */
+static const char *parse_syslog_timestamp(const char *str0, struct timeval *tv)
+{
+ const char *str = str0;
+ char *end = NULL;
+
+ if (!str0)
+ return str0;
+
+ errno = 0;
+ tv->tv_sec = strtol(str, &end, 10);
+
+ if (!errno && end && *end == '.' && *(end + 1)) {
+ str = end + 1;
+ end = NULL;
+ tv->tv_usec = strtol(str, &end, 10);
+ }
+ if (errno || !end || end == str || *end != ']')
+ return str0;
+
+ return end + 1; /* skip ']' */
+}
+
+/*
+ * Parses timestamp from /dev/kmsg, expected formats:
+ *
+ * microseconds,
+ * microseconds;
+ *
+ * the ',' is fields separators and ';' items terminator (for the last item)
+ */
+static const char *parse_kmsg_timestamp(const char *str0, struct timeval *tv)
+{
+ const char *str = str0;
+ char *end = NULL;
+ uint64_t usec;
+
+ if (!str0)
+ return str0;
+
+ errno = 0;
+ usec = strtoumax(str, &end, 10);
+
+ if (!errno && end && (*end == ';' || *end == ',')) {
+ tv->tv_usec = usec % 1000000;
+ tv->tv_sec = usec / 1000000;
+ } else
+ return str0;
+
+ return end + 1; /* skip separator */
+}
+
+
+static double time_diff(struct timeval *a, struct timeval *b)
+{
+ return (a->tv_sec - b->tv_sec) + (a->tv_usec - b->tv_usec) / 1E6;
+}
+
+static int get_syslog_buffer_size(void)
+{
+ int n = klogctl(SYSLOG_ACTION_SIZE_BUFFER, NULL, 0);
+
+ return n > 0 ? n : 0;
+}
+
+static time_t get_boot_time(void)
+{
+ struct sysinfo info;
+ struct timeval tv;
+
+ if (sysinfo(&info) != 0)
+ warn(_("sysinfo failed"));
+ else if (gettimeofday(&tv, NULL) != 0)
+ warn(_("gettimeofday failed"));
+ else
+ return tv.tv_sec -= info.uptime;
+ return 0;
+}
+
+/*
+ * Reads messages from regular file by mmap
+ */
+static ssize_t mmap_file_buffer(struct dmesg_control *ctl, char **buf)
+{
+ struct stat st;
+ int fd;
+
+ if (!ctl->filename)
+ return -1;
+
+ fd = open(ctl->filename, O_RDONLY);
+ if (fd < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), ctl->filename);
+ if (fstat(fd, &st))
+ err(EXIT_FAILURE, _("stat failed %s"), ctl->filename);
+
+ *buf = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (*buf == MAP_FAILED)
+ err(EXIT_FAILURE, _("cannot mmap: %s"), ctl->filename);
+ ctl->mmap_buff = *buf;
+ ctl->pagesize = getpagesize();
+ close(fd);
+
+ return st.st_size;
+}
+
+/*
+ * Reads messages from kernel ring buffer by klogctl()
+ */
+static ssize_t read_syslog_buffer(struct dmesg_control *ctl, char **buf)
+{
+ size_t sz;
+ int rc = -1;
+
+ if (ctl->bufsize) {
+ sz = ctl->bufsize + 8;
+ *buf = xmalloc(sz * sizeof(char));
+ rc = klogctl(ctl->action, *buf, sz);
+ } else {
+ sz = 16392;
+ while (1) {
+ *buf = xmalloc(sz * sizeof(char));
+ rc = klogctl(SYSLOG_ACTION_READ_ALL, *buf, sz);
+ if (rc < 0)
+ break;
+ if ((size_t) rc != sz || sz > (1 << 28))
+ break;
+ free(*buf);
+ *buf = NULL;
+ sz *= 4;
+ }
+
+ if (rc > 0 && ctl->action == SYSLOG_ACTION_READ_CLEAR)
+ rc = klogctl(SYSLOG_ACTION_READ_CLEAR, *buf, sz);
+ }
+
+ return rc;
+}
+
+/*
+ * Top level function to read messages
+ */
+static ssize_t read_buffer(struct dmesg_control *ctl, char **buf)
+{
+ ssize_t n = -1;
+
+ switch (ctl->method) {
+ case DMESG_METHOD_MMAP:
+ n = mmap_file_buffer(ctl, buf);
+ break;
+ case DMESG_METHOD_SYSLOG:
+ if (!ctl->bufsize)
+ ctl->bufsize = get_syslog_buffer_size();
+
+ n = read_syslog_buffer(ctl, buf);
+ break;
+ case DMESG_METHOD_KMSG:
+ /*
+ * Since kernel 3.5.0
+ */
+ n = read_kmsg(ctl);
+ if (n == 0 && ctl->action == SYSLOG_ACTION_READ_CLEAR)
+ n = klogctl(SYSLOG_ACTION_CLEAR, NULL, 0);
+ break;
+ }
+
+ return n;
+}
+
+static int fwrite_hex(const char *buf, size_t size, FILE *out)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++) {
+ int rc = fprintf(out, "\\x%02x", buf[i]);
+ if (rc < 0)
+ return rc;
+ }
+ return 0;
+}
+
+/*
+ * Prints to 'out' and non-printable chars are replaced with \x<hex> sequences.
+ */
+static void safe_fwrite(const char *buf, size_t size, FILE *out)
+{
+ size_t i;
+#ifdef HAVE_WIDECHAR
+ mbstate_t s;
+ memset(&s, 0, sizeof (s));
+#endif
+ for (i = 0; i < size; i++) {
+ const char *p = buf + i;
+ int rc, hex = 0;
+ size_t len = 1;
+
+#ifdef HAVE_WIDECHAR
+ wchar_t wc;
+ len = mbrtowc(&wc, p, size - i, &s);
+
+ if (len == 0) /* L'\0' */
+ return;
+
+ if (len == (size_t)-1 || len == (size_t)-2) { /* invalid sequence */
+ memset(&s, 0, sizeof (s));
+ len = hex = 1;
+ } else if (len > 1 && !iswprint(wc)) { /* non-printable multibyte */
+ hex = 1;
+ }
+ i += len - 1;
+#else
+ if (!isprint((unsigned int) *p) &&
+ !isspace((unsigned int) *p)) /* non-printable */
+ hex = 1;
+#endif
+ if (hex)
+ rc = fwrite_hex(p, len, out);
+ else
+ rc = fwrite(p, 1, len, out) != len;
+ if (rc != 0)
+ err(EXIT_FAILURE, _("write failed"));
+ }
+}
+
+static const char *skip_item(const char *begin, const char *end, const char *sep)
+{
+ while (begin < end) {
+ int c = *begin++;
+
+ if (c == '\0' || strchr(sep, c))
+ break;
+ }
+
+ return begin;
+}
+
+/*
+ * Parses one record from syslog(2) buffer
+ */
+static int get_next_syslog_record(struct dmesg_control *ctl,
+ struct dmesg_record *rec)
+{
+ size_t i;
+ const char *begin = NULL;
+
+ if (ctl->method != DMESG_METHOD_MMAP &&
+ ctl->method != DMESG_METHOD_SYSLOG)
+ return -1;
+
+ if (!rec->next || !rec->next_size)
+ return 1;
+
+ INIT_DMESG_RECORD(rec);
+
+ /*
+ * Unmap already printed file data from memory
+ */
+ if (ctl->mmap_buff && (size_t) (rec->next - ctl->mmap_buff) > ctl->pagesize) {
+ void *x = ctl->mmap_buff;
+
+ ctl->mmap_buff += ctl->pagesize;
+ munmap(x, ctl->pagesize);
+ }
+
+ for (i = 0; i < rec->next_size; i++) {
+ const char *p = rec->next + i;
+ const char *end = NULL;
+
+ if (!begin)
+ begin = p;
+ if (i + 1 == rec->next_size) {
+ end = p + 1;
+ i++;
+ } else if (*p == '\n' && *(p + 1) == '<')
+ end = p;
+
+ if (begin && !*begin)
+ begin = NULL; /* zero(s) at the end of the buffer? */
+ if (!begin || !end)
+ continue;
+ if (end <= begin)
+ continue; /* error or empty line? */
+
+ if (*begin == '<') {
+ if (ctl->fltr_lev || ctl->fltr_fac || ctl->decode)
+ begin = parse_faclev(begin + 1, &rec->facility,
+ &rec->level);
+ else
+ begin = skip_item(begin, end, ">");
+ }
+
+ if (*begin == '[' && (*(begin + 1) == ' ' ||
+ isdigit(*(begin + 1)))) {
+ if (ctl->delta || ctl->ctime || ctl->reltime)
+ begin = parse_syslog_timestamp(begin + 1, &rec->tv);
+ else if (ctl->notime)
+ begin = skip_item(begin, end, "]");
+
+ if (begin < end && *begin == ' ')
+ begin++;
+ }
+
+ rec->mesg = begin;
+ rec->mesg_size = end - begin;
+
+ rec->next_size -= end - rec->next;
+ rec->next = rec->next_size > 0 ? end + 1 : NULL;
+ if (rec->next_size > 0)
+ rec->next_size--;
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static int accept_record(struct dmesg_control *ctl, struct dmesg_record *rec)
+{
+ if (ctl->fltr_lev && (rec->facility < 0 ||
+ !isset(ctl->levels, rec->level)))
+ return 0;
+
+ if (ctl->fltr_fac && (rec->facility < 0 ||
+ !isset(ctl->facilities, rec->facility)))
+ return 0;
+
+ return 1;
+}
+
+static void raw_print(struct dmesg_control *ctl, const char *buf, size_t size)
+{
+ int lastc = '\n';
+
+ if (!ctl->mmap_buff) {
+ /*
+ * Print whole ring buffer
+ */
+ safe_fwrite(buf, size, stdout);
+ lastc = buf[size - 1];
+ } else {
+ /*
+ * Print file in small chunks to save memory
+ */
+ while (size) {
+ size_t sz = size > ctl->pagesize ? ctl->pagesize : size;
+ char *x = ctl->mmap_buff;
+
+ safe_fwrite(x, sz, stdout);
+ lastc = x[sz - 1];
+ size -= sz;
+ ctl->mmap_buff += sz;
+ munmap(x, sz);
+ }
+ }
+
+ if (lastc != '\n')
+ putchar('\n');
+}
+
+static struct tm *record_localtime(struct dmesg_control *ctl,
+ struct dmesg_record *rec,
+ struct tm *tm)
+{
+ time_t t = ctl->boot_time + rec->tv.tv_sec;
+ return localtime_r(&t, tm);
+}
+
+static char *record_ctime(struct dmesg_control *ctl,
+ struct dmesg_record *rec,
+ char *buf, size_t bufsiz)
+{
+ struct tm tm;
+
+ record_localtime(ctl, rec, &tm);
+
+ if (strftime(buf, bufsiz, "%a %b %e %H:%M:%S %Y", &tm) == 0)
+ *buf = '\0';
+ return buf;
+}
+
+static char *short_ctime(struct tm *tm, char *buf, size_t bufsiz)
+{
+ if (strftime(buf, bufsiz, "%b%e %H:%M", tm) == 0)
+ *buf = '\0';
+ return buf;
+}
+
+static double record_count_delta(struct dmesg_control *ctl,
+ struct dmesg_record *rec)
+{
+ double delta = 0;
+
+ if (timerisset(&ctl->lasttime))
+ delta = time_diff(&rec->tv, &ctl->lasttime);
+
+ ctl->lasttime = rec->tv;
+ return delta;
+}
+
+static void print_record(struct dmesg_control *ctl,
+ struct dmesg_record *rec)
+{
+ char buf[256];
+
+ if (!accept_record(ctl, rec))
+ return;
+
+ if (!rec->mesg_size) {
+ putchar('\n');
+ return;
+ }
+
+ /*
+ * compose syslog(2) compatible raw output -- used for /dev/kmsg for
+ * backward compatibility with syslog(2) buffers only
+ */
+ if (ctl->raw) {
+ printf("<%d>[%5d.%06d] ",
+ LOG_MAKEPRI(rec->facility, rec->level),
+ (int) rec->tv.tv_sec,
+ (int) rec->tv.tv_usec);
+
+ goto mesg;
+ }
+
+ /*
+ * facility : priority :
+ */
+ if (ctl->decode && rec->level >= 0 && rec->facility >= 0)
+ printf("%-6s:%-6s: ", facility_names[rec->facility].name,
+ level_names[rec->level].name);
+
+ /*
+ * [sec.usec <delta>] or [ctime <delta>]
+ */
+ if (ctl->delta) {
+ if (ctl->ctime)
+ printf("[%s ", record_ctime(ctl, rec, buf, sizeof(buf)));
+ else if (ctl->notime)
+ putchar('[');
+ else
+ printf("[%5d.%06d ", (int) rec->tv.tv_sec,
+ (int) rec->tv.tv_usec);
+ printf("<%12.06f>] ", record_count_delta(ctl, rec));
+
+ /*
+ * [ctime]
+ */
+ } else if (ctl->ctime)
+ printf("[%s] ", record_ctime(ctl, rec, buf, sizeof(buf)));
+
+ /*
+ * [reltime]
+ */
+ else if (ctl->reltime) {
+ double delta;
+ struct tm cur;
+
+ record_localtime(ctl, rec, &cur);
+ delta = record_count_delta(ctl, rec);
+
+ if (cur.tm_min != ctl->lasttm.tm_min ||
+ cur.tm_hour != ctl->lasttm.tm_hour ||
+ cur.tm_yday != ctl->lasttm.tm_yday)
+ printf("[%s] ", short_ctime(&cur, buf, sizeof(buf)));
+ else if (delta < 10)
+ printf("[ %+8.06f] ", delta);
+ else
+ printf("[ %+9.06f] ", delta);
+
+ ctl->lasttm = cur;
+ }
+
+ /*
+ * In syslog output the timestamp is part of the message and we don't
+ * parse the timestamp by default. We parse the timestamp only if
+ * --show-delta or --ctime is specified.
+ *
+ * In kmsg output we always parse the timesptamp, so we have to compose
+ * the [sec.usec] string.
+ */
+ if (ctl->method == DMESG_METHOD_KMSG &&
+ !ctl->notime && !ctl->delta && !ctl->ctime && !ctl->reltime)
+ printf("[%5d.%06d] ", (int) rec->tv.tv_sec, (int) rec->tv.tv_usec);
+
+mesg:
+ safe_fwrite(rec->mesg, rec->mesg_size, stdout);
+
+ if (*(rec->mesg + rec->mesg_size - 1) != '\n')
+ putchar('\n');
+}
+
+/*
+ * Prints the 'buf' kernel ring buffer; the messages are filtered out according
+ * to 'levels' and 'facilities' bitarrays.
+ */
+static void print_buffer(struct dmesg_control *ctl,
+ const char *buf, size_t size)
+{
+ struct dmesg_record rec = { .next = buf, .next_size = size };
+
+ if (ctl->raw) {
+ raw_print(ctl, buf, size);
+ return;
+ }
+
+ while (get_next_syslog_record(ctl, &rec) == 0)
+ print_record(ctl, &rec);
+}
+
+static ssize_t read_kmsg_one(struct dmesg_control *ctl)
+{
+ ssize_t size;
+
+ /* kmsg returns EPIPE if record was modified while reading */
+ do {
+ size = read(ctl->kmsg, ctl->kmsg_buf,
+ sizeof(ctl->kmsg_buf) - 1);
+ } while (size < 0 && errno == EPIPE);
+
+ return size;
+}
+
+static int init_kmsg(struct dmesg_control *ctl)
+{
+ int mode = O_RDONLY;
+
+ if (!ctl->follow)
+ mode |= O_NONBLOCK;
+
+ ctl->kmsg = open("/dev/kmsg", mode);
+ if (ctl->kmsg < 0)
+ return -1;
+
+ /*
+ * Seek after the last record available at the time
+ * the last SYSLOG_ACTION_CLEAR was issued.
+ *
+ * ... otherwise SYSLOG_ACTION_CLEAR will have no effect for kmsg.
+ */
+ lseek(ctl->kmsg, 0, SEEK_DATA);
+
+ /*
+ * Old kernels (<3.5) allow to successfully open /dev/kmsg for
+ * read-only, but read() returns -EINVAL :-(((
+ *
+ * Let's try to read the first record. The record is later processed in
+ * read_kmsg().
+ */
+ ctl->kmsg_first_read = read_kmsg_one(ctl);
+ if (ctl->kmsg_first_read < 0) {
+ close(ctl->kmsg);
+ ctl->kmsg = -1;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * /dev/kmsg record format:
+ *
+ * faclev,seqnum,timestamp[optional, ...];messgage\n
+ * TAGNAME=value
+ * ...
+ *
+ * - fields are separated by ','
+ * - last field is terminated by ';'
+ *
+ */
+#define LAST_KMSG_FIELD(s) (!s || !*s || *(s - 1) == ';')
+
+static int parse_kmsg_record(struct dmesg_control *ctl,
+ struct dmesg_record *rec,
+ char *buf,
+ size_t sz)
+{
+ const char *p = buf, *end;
+
+ if (sz == 0 || !buf || !*buf)
+ return -1;
+
+ end = buf + (sz - 1);
+ INIT_DMESG_RECORD(rec);
+
+ while (p < end && isspace(*p))
+ p++;
+
+ /* A) priority and facility */
+ if (ctl->fltr_lev || ctl->fltr_fac || ctl->decode || ctl->raw)
+ p = parse_faclev(p, &rec->facility, &rec->level);
+ else
+ p = skip_item(p, end, ",");
+ if (LAST_KMSG_FIELD(p))
+ goto mesg;
+
+ /* B) sequence number */
+ p = skip_item(p, end, ",;");
+ if (LAST_KMSG_FIELD(p))
+ goto mesg;
+
+ /* C) timestamp */
+ if (ctl->notime)
+ p = skip_item(p, end, ",;");
+ else
+ p = parse_kmsg_timestamp(p, &rec->tv);
+ if (LAST_KMSG_FIELD(p))
+ goto mesg;
+
+ /* D) optional fields (ignore) */
+ p = skip_item(p, end, ";");
+ if (LAST_KMSG_FIELD(p))
+ goto mesg;
+
+mesg:
+ /* E) message text */
+ rec->mesg = p;
+ p = skip_item(p, end, "\n");
+
+ if (!p)
+ return -1;
+
+ rec->mesg_size = p - rec->mesg;
+
+ /*
+ * Kernel escapes non-printable characters, unfortuately kernel
+ * definition of "non-printable" is too strict. On UTF8 console we can
+ * print many chars, so let's decode from kernel.
+ */
+ unhexmangle_to_buffer(rec->mesg, (char *) rec->mesg, rec->mesg_size + 1);
+
+ /* F) message tags (ignore) */
+
+ return 0;
+}
+
+/*
+ * Note that each read() call for /dev/kmsg returns always one record. It means
+ * that we don't have to read whole message buffer before the records parsing.
+ *
+ * So this function does not compose one huge buffer (like read_syslog_buffer())
+ * and print_buffer() is unnecessary. All is done in this function.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+static int read_kmsg(struct dmesg_control *ctl)
+{
+ struct dmesg_record rec;
+ ssize_t sz;
+
+ if (ctl->method != DMESG_METHOD_KMSG || ctl->kmsg < 0)
+ return -1;
+
+ /*
+ * The very first read() call is done in kmsg_init() where we test
+ * /dev/kmsg usability. The return code from the initial read() is
+ * stored in ctl->kmsg_first_read;
+ */
+ sz = ctl->kmsg_first_read;
+
+ while (sz > 0) {
+ *(ctl->kmsg_buf + sz) = '\0'; /* for debug messages */
+
+ if (parse_kmsg_record(ctl, &rec,
+ ctl->kmsg_buf, (size_t) sz) == 0)
+ print_record(ctl, &rec);
+
+ sz = read_kmsg_one(ctl);
+ }
+
+ return 0;
+}
+
+#define EXCL_ACT_ERR "--{clear,read-clear,console-level,console-on,console-off}"
+#define EXCL_SYS_ERR "--{syslog,follow}"
+
+int main(int argc, char *argv[])
+{
+ char *buf = NULL;
+ int c;
+ int console_level = 0;
+ int klog_rc = 0;
+ ssize_t n;
+ static struct dmesg_control ctl = {
+ .filename = NULL,
+ .action = SYSLOG_ACTION_READ_ALL,
+ .method = DMESG_METHOD_KMSG,
+ .kmsg = -1,
+ };
+
+ static const struct option longopts[] = {
+ { "buffer-size", required_argument, NULL, 's' },
+ { "clear", no_argument, NULL, 'C' },
+ { "console-level", required_argument, NULL, 'n' },
+ { "console-off", no_argument, NULL, 'D' },
+ { "console-on", no_argument, NULL, 'E' },
+ { "decode", no_argument, NULL, 'x' },
+ { "file", required_argument, NULL, 'F' },
+ { "facility", required_argument, NULL, 'f' },
+ { "follow", no_argument, NULL, 'w' },
+ { "help", no_argument, NULL, 'h' },
+ { "kernel", no_argument, NULL, 'k' },
+ { "level", required_argument, NULL, 'l' },
+ { "syslog", no_argument, NULL, 'S' },
+ { "raw", no_argument, NULL, 'r' },
+ { "read-clear", no_argument, NULL, 'c' },
+ { "reltime", no_argument, NULL, 'e' },
+ { "show-delta", no_argument, NULL, 'd' },
+ { "ctime", no_argument, NULL, 'T' },
+ { "notime", no_argument, NULL, 't' },
+ { "userspace", no_argument, NULL, 'u' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */
+ { 'C','D','E','c','n' }, /* clear,off,on,read-clear,level*/
+ { 'S','w' }, /* syslog,follow */
+ { 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, "CcDdEeF:f:hkl:n:rSs:TtuVwx",
+ longopts, NULL)) != -1) {
+
+ err_exclusive_options(c, longopts, excl, excl_st);
+
+ switch (c) {
+ case 'C':
+ ctl.action = SYSLOG_ACTION_CLEAR;
+ break;
+ case 'c':
+ ctl.action = SYSLOG_ACTION_READ_CLEAR;
+ break;
+ case 'D':
+ ctl.action = SYSLOG_ACTION_CONSOLE_OFF;
+ break;
+ case 'd':
+ ctl.delta = 1;
+ break;
+ case 'E':
+ ctl.action = SYSLOG_ACTION_CONSOLE_ON;
+ break;
+ case 'e':
+ ctl.boot_time = get_boot_time();
+ if (ctl.boot_time)
+ ctl.reltime = 1;
+ break;
+ case 'F':
+ ctl.filename = optarg;
+ ctl.method = DMESG_METHOD_MMAP;
+ break;
+ case 'f':
+ ctl.fltr_fac = 1;
+ if (string_to_bitarray(optarg,
+ ctl.facilities, parse_facility) < 0)
+ return EXIT_FAILURE;
+ break;
+ case 'h':
+ usage(stdout);
+ break;
+ case 'k':
+ ctl.fltr_fac = 1;
+ setbit(ctl.facilities, FAC_BASE(LOG_KERN));
+ break;
+ case 'l':
+ ctl.fltr_lev= 1;
+ if (string_to_bitarray(optarg,
+ ctl.levels, parse_level) < 0)
+ return EXIT_FAILURE;
+ break;
+ case 'n':
+ ctl.action = SYSLOG_ACTION_CONSOLE_LEVEL;
+ console_level = parse_level(optarg, 0);
+ break;
+ case 'r':
+ ctl.raw = 1;
+ break;
+ case 'S':
+ ctl.method = DMESG_METHOD_SYSLOG;
+ break;
+ case 's':
+ ctl.bufsize = strtou32_or_err(optarg,
+ _("invalid buffer size argument"));
+ if (ctl.bufsize < 4096)
+ ctl.bufsize = 4096;
+ break;
+ case 'T':
+ ctl.boot_time = get_boot_time();
+ if (ctl.boot_time)
+ ctl.ctime = 1;
+ break;
+ case 't':
+ ctl.notime = 1;
+ break;
+ case 'u':
+ ctl.fltr_fac = 1;
+ for (n = 1; (size_t) n < ARRAY_SIZE(facility_names); n++)
+ setbit(ctl.facilities, n);
+ break;
+ case 'V':
+ printf(_("%s from %s\n"), program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ case 'w':
+ ctl.follow = 1;
+ break;
+ case 'x':
+ ctl.decode = 1;
+ break;
+ case '?':
+ default:
+ usage(stderr);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 1)
+ usage(stderr);
+
+ if (ctl.raw && (ctl.fltr_lev || ctl.fltr_fac || ctl.delta ||
+ ctl.notime || ctl.ctime || ctl.decode))
+ errx(EXIT_FAILURE, _("--raw can't be used together with level, "
+ "facility, decode, delta, ctime or notime options"));
+
+ if (ctl.notime && (ctl.ctime || ctl.reltime))
+ errx(EXIT_FAILURE, _("--notime can't be used together with --ctime or --reltime"));
+ if (ctl.reltime && ctl.ctime)
+ errx(EXIT_FAILURE, _("--reltime can't be used together with --ctime "));
+
+ switch (ctl.action) {
+ case SYSLOG_ACTION_READ_ALL:
+ case SYSLOG_ACTION_READ_CLEAR:
+ if (ctl.method == DMESG_METHOD_KMSG && init_kmsg(&ctl) != 0)
+ ctl.method = DMESG_METHOD_SYSLOG;
+
+ n = read_buffer(&ctl, &buf);
+ if (n > 0)
+ print_buffer(&ctl, buf, n);
+ if (!ctl.mmap_buff)
+ free(buf);
+ if (n < 0)
+ err(EXIT_FAILURE, _("read kernel buffer failed"));
+ if (ctl.kmsg >= 0)
+ close(ctl.kmsg);
+ break;
+ case SYSLOG_ACTION_CLEAR:
+ case SYSLOG_ACTION_CONSOLE_OFF:
+ case SYSLOG_ACTION_CONSOLE_ON:
+ klog_rc = klogctl(ctl.action, NULL, 0);
+ break;
+ case SYSLOG_ACTION_CONSOLE_LEVEL:
+ klog_rc = klogctl(ctl.action, NULL, console_level);
+ break;
+ default:
+ errx(EXIT_FAILURE, _("unsupported command"));
+ break;
+ }
+
+
+ if (klog_rc)
+ err(EXIT_FAILURE, _("klogctl failed"));
+
+ return EXIT_SUCCESS;
+}
diff --git a/sys-utils/eject.1 b/sys-utils/eject.1
new file mode 100644
index 0000000..1a23229
--- /dev/null
+++ b/sys-utils/eject.1
@@ -0,0 +1,165 @@
+.\" Copyright (C) 1994-2005 Jeff Tranter (tranter@pobox.com)
+.\" Copyright (C) 2012 Karel Zak <kzak@redhat.com>
+.\"
+.\" It may be distributed under the GNU Public License, version 2, or
+.\" any higher version. See section COPYING of the GNU Public license
+.\" for conditions under which this file may be redistributed.
+.TH EJECT 1 "April 2012" "Linux" "User Commands"
+.SH NAME
+eject \- eject removable media
+.SH SYNOPSIS
+.B eject
+.RB [ options ]
+.IR device | mountpoint
+.SH DESCRIPTION
+.B Eject
+allows removable media (typically a CD-ROM, floppy disk, tape, JAZ, ZIP or USB
+disk) to be ejected under software control. The command can also control some
+multi-disc CD-ROM changers, the auto-eject feature supported by some devices,
+and close the disc tray of some CD-ROM drives.
+.PP
+The device corresponding to \fIdevice\fP or \fImountpoint\fP is ejected. If no
+name is specified, the default name /dev/cdrom is used. The device may be
+addressed by device name (e.g. 'sda'), device path (e.g. '/dev/sda'),
+UUID=<uuid> or LABEL=<label> tags.
+.PP
+There are four different methods of ejecting, depending on whether the device
+is a CD-ROM, SCSI device, removable floppy, or tape. By default eject tries
+all four methods in order until it succeeds.
+.PP
+If device partition is specified, the whole-disk device is used. If the device
+or a device partition is currently mounted, it is unmounted before ejecting.
+.SH OPTIONS
+.IP "\fB\-a, \-\-auto \fIon|off\fP"
+This option controls the auto-eject mode, supported by some devices. When
+enabled, the drive automatically ejects when the device is closed.
+.IP "\fB\-c, \-\-changerslot \fIslot\fP"
+With this option a CD slot can be selected from an ATAPI/IDE CD-ROM changer.
+Linux 2.0 or higher is required to use this feature. The CD-ROM drive can not
+be in use (mounted data CD or playing a music CD) for a change request to work.
+Please also note that the first slot of the changer is referred to as 0, not 1.
+.IP "\fB\-d, \-\-default\fP"
+List the default device name.
+.IP "\fB\-f, \-\-floppy\fP"
+This option specifies that the drive should be ejected using a removable floppy
+disk eject command.
+.IP "\fB\-F, \-\-force\fP"
+Force eject, don't check device type.
+.IP "\fB\-h, \-\-help\fP"
+Print a help text and exit.
+.IP "\fB\-i, \-\-manualeject \fIon|off\fP"
+This option controls locking of the hardware eject button. When enabled, the
+drive will not be ejected when the button is pressed. This is useful when you
+are carrying a laptop in a bag or case and don't want it to eject if the button
+is inadvertently pressed.
+.IP "\fB\-p, \-\-proc\fP"
+This option allow you to use /proc/mounts instead /etc/mtab. It also passes the
+\-n option to umount(1).
+.IP "\fB\-q, \-\-tape\fP"
+This option specifies that the drive should be ejected using a tape drive
+offline command.
+.IP "\fB\-m, \-\-no-unmount\fP"
+The option tells eject to not try to unmount at all.
+.IP "\fB\-M, \-\-no-partitions-unmount\fP"
+The option tells eject to not try to unmount another partitions on partitioned
+devices. If another partition is mounted the program will not attempt to eject
+the media. It will attempt to unmount only mountpoint or mounted device given
+on eject command line.
+.IP "\fB\-n, \-\-noop\fP"
+With this option the selected device is displayed but no action is performed.
+.IP "\fB\-t, \-\-trayclose\fP"
+With this option the drive is given a CD-ROM tray close command. Not all
+devices support this command.
+.IP "\fB\-T, \-\-traytoggle\fP"
+With this option the drive is given a CD-ROM tray close command if it's opened,
+and a CD-ROM tray eject command if it's closed. Not all devices support this
+command, because it uses the above CD-ROM tray close command.
+.IP "\fB\-r, \-\-cdrom\fP"
+This option specifies that the drive should be ejected using a CDROM eject
+command.
+.IP "\fB\-s, \-\-scsi\fP"
+This option specifies that the drive should be ejected using SCSI commands.
+.IP "\fB\-v, \-\-verbose\fP"
+Run in verbose mode; more information is displayed about what the command is
+doing.
+.IP "\fB\-V, \-\-version\fP"
+Display program version and exit.
+.IP "\fB\-x, \-\-cdspeed \fI<speed>\fP"
+With this option the drive is given a CD-ROM select speed command. The speed
+argument is a number indicating the desired speed (e.g. 8 for 8X speed), or 0
+for maximum data rate. Not all devices support this command and you can only
+specify speeds that the drive is capable of. Every time the media is changed
+this option is cleared. This option can be used alone, or with the \-t and \-c
+options.
+.IP "\fB\-X, \-\-listspeed\fP"
+With this option the CD-ROM drive will be probed to detect the available
+speeds. The output is a list of speeds which can be used as an argument of the
+\-x option. This only works with Linux 2.6.13 or higher, on previous versions
+solely the maximum speed will be reported. Also note that some drive may not
+correctly report the speed and therefore this option does not work with them.
+.SH EXIT STATUS
+Returns 0 if operation was successful, 1 if operation failed or command syntax
+was not valid.
+.SH NOTES
+.B Eject
+only works with devices that support one or more of the four methods of
+ejecting. This includes most CD-ROM drives (IDE, SCSI, and proprietary), some
+SCSI tape drives, JAZ drives, ZIP drives (parallel port, SCSI, and IDE
+versions), and LS120 removable floppies. Users have also reported success with
+floppy drives on Sun SPARC and Apple Macintosh systems. If
+.B eject
+does not work, it is most likely a limitation of the kernel driver for the
+device and not the
+.B eject
+program itself.
+.PP
+The \-r, \-s, \-f, and \-q options allow controlling which methods are used to
+eject. More than one method can be specified. If none of these options are
+specified, it tries all four (this works fine in most cases).
+.PP
+.B Eject
+may not always be able to determine if the device is mounted (e.g. if it has
+several names). If the device name is a symbolic link,
+.B eject
+will follow the link and use the device that it points to.
+.PP
+If
+.B eject
+determines that the device can have multiple partitions, it will attempt to
+unmount all mounted partitions of the device before ejecting (see
+--no-partitions-unmount). If an unmount fails, the program will not attempt to
+eject the media.
+.PP
+You can eject an audio CD. Some CD-ROM drives will refuse to open the tray if
+the drive is empty. Some devices do not support the tray close command.
+.PP
+If the auto-eject feature is enabled, then the drive will always be ejected
+after running this command. Not all Linux kernel CD-ROM drivers support the
+auto-eject mode. There is no way to find out the state of the auto-eject mode.
+.PP
+You need appropriate privileges to access the device files. Running as root is
+required to eject some devices (e.g. SCSI devices).
+.SH AUTHORS
+.MT tranter@\:pobox.com
+Jeff Tranter
+.ME
+- original author.
+.br
+.MT kzak@\:redhat.com
+Karel Zak
+.ME
+and
+.MT mluscon@\:redhat.com
+Michal Luscon
+.ME
+- util-linux version.
+.SH SEE ALSO
+.BR lsblk (8),
+.BR findmnt (8),
+.BR mount (8),
+.BR umount (8)
+.SH AVAILABILITY
+The eject 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/sys-utils/eject.c b/sys-utils/eject.c
new file mode 100644
index 0000000..2097961
--- /dev/null
+++ b/sys-utils/eject.c
@@ -0,0 +1,1132 @@
+/*
+ * Copyright (C) 1994-2005 Jeff Tranter (tranter@pobox.com)
+ * Copyright (C) 2012 Karel Zak <kzak@redhat.com>
+ * Copyright (C) Michal Luscon <mluscon@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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <err.h>
+#include <stdarg.h>
+
+#include <getopt.h>
+#include <errno.h>
+#include <regex.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/mtio.h>
+#include <linux/cdrom.h>
+#include <linux/fd.h>
+#include <sys/mount.h>
+#include <scsi/scsi.h>
+#include <scsi/sg.h>
+#include <scsi/scsi_ioctl.h>
+#include <sys/time.h>
+
+#include <libmount.h>
+
+#include "c.h"
+#include "closestream.h"
+#include "nls.h"
+#include "strutils.h"
+#include "xalloc.h"
+#include "pathnames.h"
+#include "sysfs.h"
+
+#define EJECT_DEFAULT_DEVICE "/dev/cdrom"
+
+
+/* Used by the toggle_tray() function. If ejecting the tray takes this
+ * time or less, the tray was probably already ejected, so we close it
+ * again.
+ */
+#define TRAY_WAS_ALREADY_OPEN_USECS 200000 /* about 0.2 seconds */
+
+/* eject(1) is able to eject only 'removable' devices (attribute in /sys)
+ * _or_ devices connected by hotplug subsystem.
+ */
+static const char * const hotplug_subsystems[] = {
+ "usb",
+ "ieee1394",
+ "pcmcia",
+ "mmc",
+ "ccw"
+};
+
+/* Global Variables */
+static int a_option; /* command flags and arguments */
+static int c_option;
+static int d_option;
+static int f_option;
+static int F_option;
+static int n_option;
+static int q_option;
+static int r_option;
+static int s_option;
+static int t_option;
+static int T_option;
+static int X_option;
+static int v_option;
+static int x_option;
+static int p_option;
+static int m_option;
+static int M_option;
+static int i_option;
+static int a_arg;
+static int i_arg;
+static long int c_arg;
+static long int x_arg;
+
+struct libmnt_table *mtab;
+struct libmnt_cache *cache;
+
+static void vinfo(const char *fmt, va_list va)
+{
+ fprintf(stdout, "%s: ", program_invocation_short_name);
+ vprintf(fmt, va);
+ fputc('\n', stdout);
+}
+
+static inline void verbose(const char *fmt, ...)
+{
+ va_list va;
+
+ if (!v_option)
+ return;
+
+ va_start(va, fmt);
+ vinfo(fmt, va);
+ va_end(va);
+}
+
+static inline void info(const char *fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ vinfo(fmt, va);
+ va_end(va);
+}
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fputs(USAGE_HEADER, out);
+
+ fprintf(out,
+ _(" %s [options] [<device>|<mountpoint>]\n"), program_invocation_short_name);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -a, --auto <on|off> turn auto-eject feature on or off\n"
+ " -c, --changerslot <slot> switch discs on a CD-ROM changer\n"
+ " -d, --default display default device\n"
+ " -f, --floppy eject floppy\n"
+ " -F, --force don't care about device type\n"
+ " -i, --manualeject <on|off> toggle manual eject protection on/off\n"
+ " -m, --no-unmount do not unmount device even if it is mounted\n"
+ " -M, --no-partitions-unmount do not unmount another partitions\n"
+ " -n, --noop don't eject, just show device found\n"
+ " -p, --proc use /proc/mounts instead of /etc/mtab\n"
+ " -q, --tape eject tape\n"
+ " -r, --cdrom eject CD-ROM\n"
+ " -s, --scsi eject SCSI device\n"
+ " -t, --trayclose close tray\n"
+ " -T, --traytoggle toggle tray\n"
+ " -v, --verbose enable verbose output\n"
+ " -x, --cdspeed <speed> set CD-ROM max speed\n"
+ " -X, --listspeed list CD-ROM available speeds\n"),
+ out);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+
+ fputs(_("\nBy default tries -r, -s, -f, and -q in order until success.\n"), out);
+ fprintf(out, USAGE_MAN_TAIL("eject(1)"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+
+/* Handle command line options. */
+static void parse_args(int argc, char **argv, char **device)
+{
+ static const struct option long_opts[] =
+ {
+ {"auto", required_argument, NULL, 'a'},
+ {"cdrom", no_argument, NULL, 'r'},
+ {"cdspeed", required_argument, NULL, 'x'},
+ {"changerslot", required_argument, NULL, 'c'},
+ {"default", no_argument, NULL, 'd'},
+ {"floppy", no_argument, NULL, 'f'},
+ {"force", no_argument, NULL, 'F'},
+ {"help", no_argument, NULL, 'h'},
+ {"listspeed", no_argument, NULL, 'X'},
+ {"manualeject", required_argument, NULL, 'i'},
+ {"noop", no_argument, NULL, 'n'},
+ {"no-unmount", no_argument, NULL, 'm'},
+ {"no-partitions-unmount", no_argument, NULL, 'M' },
+ {"proc", no_argument, NULL, 'p'},
+ {"scsi", no_argument, NULL, 's'},
+ {"tape", no_argument, NULL, 'q'},
+ {"trayclose", no_argument, NULL, 't'},
+ {"traytoggle", no_argument, NULL, 'T'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"version", no_argument, NULL, 'V'},
+ {0, 0, 0, 0}
+ };
+ int c;
+
+ while ((c = getopt_long(argc, argv,
+ "a:c:i:x:dfFhnqrstTXvVpmM", long_opts, NULL)) != -1) {
+ switch (c) {
+ case 'a':
+ a_option = 1;
+ if (!strcmp(optarg, "0") || !strcmp(optarg, "off"))
+ a_arg = 0;
+ else if (!strcmp(optarg, "1") || !strcmp(optarg, "on"))
+ a_arg = 1;
+ else
+ errx(EXIT_FAILURE, _("invalid argument to --auto/-a option"));
+ break;
+ case 'c':
+ c_option = 1;
+ c_arg = strtoul_or_err(optarg, _("invalid argument to --changerslot/-c option"));
+ break;
+ case 'x':
+ x_option = 1;
+ x_arg = strtoul_or_err(optarg, _("invalid argument to --cdspeed/-x option"));
+ break;
+ case 'd':
+ d_option = 1;
+ break;
+ case 'f':
+ f_option = 1;
+ break;
+ case 'F':
+ F_option = 1;
+ break;
+ case 'h':
+ usage(stdout);
+ break;
+ case 'i':
+ i_option = 1;
+ if (!strcmp(optarg, "0") || !strcmp(optarg, "off"))
+ i_arg = 0;
+ else if (!strcmp(optarg, "1") || !strcmp(optarg, "on"))
+ i_arg = 1;
+ else
+ errx(EXIT_FAILURE, _("invalid argument to --manualeject/-i option"));
+ break;
+ case 'm':
+ m_option = 1;
+ break;
+ case 'M':
+ M_option = 1;
+ break;
+ case 'n':
+ n_option = 1;
+ break;
+ case 'p':
+ p_option = 1;
+ break;
+ case 'q':
+ q_option = 1;
+ break;
+ case 'r':
+ r_option = 1;
+ break;
+ case 's':
+ s_option = 1;
+ break;
+ case 't':
+ t_option = 1;
+ break;
+ case 'T':
+ T_option = 1;
+ break;
+ case 'X':
+ X_option = 1;
+ break;
+ case 'v':
+ v_option = 1;
+ break;
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ exit(EXIT_SUCCESS);
+ break;
+ default:
+ case '?':
+ usage(stderr);
+ break;
+ }
+ }
+
+ /* check for a single additional argument */
+ if ((argc - optind) > 1)
+ errx(EXIT_FAILURE, _("too many arguments"));
+
+ if ((argc - optind) == 1)
+ *device = xstrdup(argv[optind]);
+}
+
+/*
+ * Given name, such as foo, see if any of the following exist:
+ *
+ * foo (if foo starts with '.' or '/')
+ * /dev/foo
+ *
+ * If found, return the full path. If not found, return 0.
+ * Returns pointer to dynamically allocated string.
+ */
+static char *find_device(const char *name)
+{
+ if (!name)
+ return NULL;
+
+ if ((*name == '.' || *name == '/') && access(name, F_OK) == 0)
+ return xstrdup(name);
+ else {
+ char buf[PATH_MAX];
+
+ snprintf(buf, sizeof(buf), "/dev/%s", name);
+ if (access(buf, F_OK) == 0)
+ return xstrdup(buf);
+ }
+
+ return NULL;
+}
+
+/* Set or clear auto-eject mode. */
+static void auto_eject(int fd, int on)
+{
+ int status = -1;
+
+#if defined(CDROM_SET_OPTIONS) && defined(CDROM_CLEAR_OPTIONS)
+ if (on)
+ status = ioctl(fd, CDROM_SET_OPTIONS, CDO_AUTO_EJECT);
+ else
+ status = ioctl(fd, CDROM_CLEAR_OPTIONS, CDO_AUTO_EJECT);
+#else
+ errno = ENOSYS;
+#endif
+ if (status < 0)
+ err(EXIT_FAILURE,_("CD-ROM auto-eject command failed"));
+}
+
+/*
+ * Stops CDROM from opening on manual eject pressing the button.
+ * This can be useful when you carry your laptop
+ * in your bag while it's on and no CD inserted in it's drive.
+ * Implemented as found in Documentation/ioctl/cdrom.txt
+ *
+ * TODO: Maybe we should check this also:
+ * EDRIVE_CANT_DO_THIS Door lock function not supported.
+ * EBUSY Attempt to unlock when multiple users
+ * have the drive open and not CAP_SYS_ADMIN
+ */
+static void manual_eject(int fd, int on)
+{
+ if (ioctl(fd, CDROM_LOCKDOOR, on) < 0)
+ err(EXIT_FAILURE, _("CD-ROM lock door command failed"));
+
+ if (on)
+ info(_("CD-Drive may NOT be ejected with device button"));
+ else
+ info(_("CD-Drive may be ejected with device button"));
+}
+
+/*
+ * Changer select. CDROM_SELECT_DISC is preferred, older kernels used
+ * CDROMLOADFROMSLOT.
+ */
+static void changer_select(int fd, int slot)
+{
+#ifdef CDROM_SELECT_DISC
+ if (ioctl(fd, CDROM_SELECT_DISC, slot) < 0)
+ err(EXIT_FAILURE, _("CD-ROM select disc command failed"));
+
+#elif defined CDROMLOADFROMSLOT
+ if (ioctl(fd, CDROMLOADFROMSLOT, slot) != 0)
+ err(EXIT_FAILURE, _("CD-ROM load from slot command failed"));
+#else
+ warnx(_("IDE/ATAPI CD-ROM changer not supported by this kernel\n") );
+#endif
+}
+
+/*
+ * Close tray. Not supported by older kernels.
+ */
+static void close_tray(int fd)
+{
+ int status;
+
+#if defined(CDROMCLOSETRAY) || defined(CDIOCCLOSE)
+#if defined(CDROMCLOSETRAY)
+ status = ioctl(fd, CDROMCLOSETRAY);
+#elif defined(CDIOCCLOSE)
+ status = ioctl(fd, CDIOCCLOSE);
+#endif
+ if (status != 0)
+ err(EXIT_FAILURE, _("CD-ROM tray close command failed"));
+#else
+ warnx(_("CD-ROM tray close command not supported by this kernel\n"));
+#endif
+}
+
+/*
+ * Eject using CDROMEJECT ioctl.
+ */
+static int eject_cdrom(int fd)
+{
+#if defined(CDROMEJECT)
+ return ioctl(fd, CDROMEJECT) >= 0;
+#elif defined(CDIOCEJECT)
+ return ioctl(fd, CDIOCEJECT) >= 0;
+#else
+ warnx(_("CD-ROM eject unsupported"));
+ errno = ENOSYS;
+ return 0;
+#endif
+}
+
+/*
+ * Toggle tray.
+ *
+ * Written by Benjamin Schwenk <benjaminschwenk@yahoo.de> and
+ * Sybren Stuvel <sybren@thirdtower.com>
+ *
+ * Not supported by older kernels because it might use
+ * CloseTray().
+ *
+ */
+static void toggle_tray(int fd)
+{
+ struct timeval time_start, time_stop;
+ int time_elapsed;
+
+#ifdef CDROM_DRIVE_STATUS
+ /* First ask the CDROM for info, otherwise fall back to manual. */
+ switch (ioctl(fd, CDROM_DRIVE_STATUS)) {
+ case CDS_TRAY_OPEN:
+ close_tray(fd);
+ return;
+
+ case CDS_NO_DISC:
+ case CDS_DISC_OK:
+ if (!eject_cdrom(fd))
+ err(EXIT_FAILURE, _("CD-ROM eject command failed"));
+ return;
+ case CDS_NO_INFO:
+ warnx(_("no CD-ROM information available"));
+ return;
+ case CDS_DRIVE_NOT_READY:
+ warnx(_("CD-ROM drive is not ready"));
+ return;
+ default:
+ abort();
+ }
+#endif
+
+ /* Try to open the CDROM tray and measure the time therefor
+ * needed. In my experience the function needs less than 0.05
+ * seconds if the tray was already open, and at least 1.5 seconds
+ * if it was closed. */
+ gettimeofday(&time_start, NULL);
+
+ /* Send the CDROMEJECT command to the device. */
+ if (!eject_cdrom(fd))
+ err(EXIT_FAILURE, _("CD-ROM eject command failed"));
+
+ /* Get the second timestamp, to measure the time needed to open
+ * the tray. */
+ gettimeofday(&time_stop, NULL);
+
+ time_elapsed = (time_stop.tv_sec * 1000000 + time_stop.tv_usec) -
+ (time_start.tv_sec * 1000000 + time_start.tv_usec);
+
+ /* If the tray "opened" too fast, we can be nearly sure, that it
+ * was already open. In this case, close it now. Else the tray was
+ * closed before. This would mean that we are done. */
+ if (time_elapsed < TRAY_WAS_ALREADY_OPEN_USECS)
+ close_tray(fd);
+}
+
+/*
+ * Select Speed of CD-ROM drive.
+ * Thanks to Roland Krivanek (krivanek@fmph.uniba.sk)
+ * http://dmpc.dbp.fmph.uniba.sk/~krivanek/cdrom_speed/
+ */
+static void select_speed(int fd, int speed)
+{
+#ifdef CDROM_SELECT_SPEED
+ if (ioctl(fd, CDROM_SELECT_SPEED, speed) != 0)
+ err(EXIT_FAILURE, _("CD-ROM select speed command failed"));
+#else
+ warnx(_("CD-ROM select speed command not supported by this kernel"));
+#endif
+}
+
+/*
+ * Read Speed of CD-ROM drive. From Linux 2.6.13, the current speed
+ * is correctly reported
+ */
+static int read_speed(const char *devname)
+{
+ int drive_number = -1;
+ char *name;
+ FILE *f;
+
+ f = fopen(_PATH_PROC_CDROMINFO, "r");
+ if (!f)
+ err(EXIT_FAILURE, _("cannot open %s"), _PATH_PROC_CDROMINFO);
+
+ name = rindex(devname, '/') + 1;
+
+ while (!feof(f)) {
+ char line[512];
+ char *str;
+
+ if (!fgets(line, sizeof(line), f))
+ break;
+
+ /* find drive number in line "drive name" */
+ if (drive_number == -1) {
+ if (strncmp(line, "drive name:", 11) == 0) {
+ str = strtok(&line[11], "\t ");
+ drive_number = 0;
+ while (strncmp(name, str, strlen(name)) != 0) {
+ drive_number++;
+ str = strtok(NULL, "\t ");
+ if (!str)
+ errx(EXIT_FAILURE,
+ _("%s: failed to finding CD-ROM name"),
+ _PATH_PROC_CDROMINFO);
+ }
+ }
+ /* find line "drive speed" and read the correct speed */
+ } else {
+ if (strncmp(line, "drive speed:", 12) == 0) {
+ int i;
+
+ str = strtok(&line[12], "\t ");
+ for (i = 1; i < drive_number; i++)
+ str = strtok(NULL, "\t ");
+
+ if (!str)
+ errx(EXIT_FAILURE,
+ _("%s: failed to read speed"),
+ _PATH_PROC_CDROMINFO);
+ fclose(f);
+ return atoi(str);
+ }
+ }
+ }
+
+ errx(EXIT_FAILURE, _("failed to read speed"));
+}
+
+/*
+ * List Speed of CD-ROM drive.
+ */
+static void list_speeds(const char *name, int fd)
+{
+#ifdef CDROM_SELECT_SPEED
+ int max_speed, curr_speed = 0, prev_speed;
+
+ select_speed(fd, 0);
+ max_speed = read_speed(name);
+
+ while (curr_speed < max_speed) {
+ prev_speed = curr_speed;
+ select_speed(fd, prev_speed + 1);
+ curr_speed = read_speed(name);
+ if (curr_speed > prev_speed)
+ printf("%d ", curr_speed);
+ else
+ curr_speed = prev_speed + 1;
+ }
+
+ printf("\n");
+#else
+ warnx(_("CD-ROM select speed command not supported by this kernel"));
+#endif
+}
+
+/*
+ * Eject using SCSI SG_IO commands. Return 1 if successful, 0 otherwise.
+ */
+static int eject_scsi(int fd)
+{
+ int status, k;
+ sg_io_hdr_t io_hdr;
+ unsigned char allowRmBlk[6] = {ALLOW_MEDIUM_REMOVAL, 0, 0, 0, 0, 0};
+ unsigned char startStop1Blk[6] = {START_STOP, 0, 0, 0, 1, 0};
+ unsigned char startStop2Blk[6] = {START_STOP, 0, 0, 0, 2, 0};
+ unsigned char inqBuff[2];
+ unsigned char sense_buffer[32];
+
+ if ((ioctl(fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
+ verbose(_("not an sg device, or old sg driver"));
+ return 0;
+ }
+
+ memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+ io_hdr.interface_id = 'S';
+ io_hdr.cmd_len = 6;
+ io_hdr.mx_sb_len = sizeof(sense_buffer);
+ io_hdr.dxfer_direction = SG_DXFER_NONE;
+ io_hdr.dxfer_len = 0;
+ io_hdr.dxferp = inqBuff;
+ io_hdr.sbp = sense_buffer;
+ io_hdr.timeout = 10000;
+
+ io_hdr.cmdp = allowRmBlk;
+ status = ioctl(fd, SG_IO, (void *)&io_hdr);
+ if (status < 0)
+ return 0;
+
+ io_hdr.cmdp = startStop1Blk;
+ status = ioctl(fd, SG_IO, (void *)&io_hdr);
+ if (status < 0)
+ return 0;
+
+ io_hdr.cmdp = startStop2Blk;
+ status = ioctl(fd, SG_IO, (void *)&io_hdr);
+ if (status < 0)
+ return 0;
+
+ /* force kernel to reread partition table when new disc inserted */
+ status = ioctl(fd, BLKRRPART);
+ return 1;
+}
+
+/*
+ * Eject using FDEJECT ioctl. Return 1 if successful, 0 otherwise.
+ */
+static int eject_floppy(int fd)
+{
+ return ioctl(fd, FDEJECT) >= 0;
+}
+
+
+/*
+ * Rewind and eject using tape ioctl. Return 1 if successful, 0 otherwise.
+ */
+static int eject_tape(int fd)
+{
+ struct mtop op = { .mt_op = MTOFFL, .mt_count = 0 };
+
+ return ioctl(fd, MTIOCTOP, &op) >= 0;
+}
+
+
+/* umount a device. */
+static void umount_one(const char *name)
+{
+ int status;
+
+ if (!name)
+ return;
+
+ verbose(_("%s: unmounting"), name);
+
+ switch (fork()) {
+ case 0: /* child */
+ if (setgid(getgid()) < 0)
+ err(EXIT_FAILURE, _("cannot set group id"));
+
+ if (setuid(getuid()) < 0)
+ err(EXIT_FAILURE, _("eject: cannot set user id"));
+
+ if (p_option)
+ execl("/bin/umount", "/bin/umount", name, "-n", NULL);
+ else
+ execl("/bin/umount", "/bin/umount", name, NULL);
+
+ errx(EXIT_FAILURE, _("unable to exec /bin/umount of `%s'"), name);
+
+ case -1:
+ warn( _("unable to fork"));
+ break;
+
+ default: /* parent */
+ wait(&status);
+ if (WIFEXITED(status) == 0)
+ errx(EXIT_FAILURE,
+ _("unmount of `%s' did not exit normally"), name);
+
+ if (WEXITSTATUS(status) != 0)
+ errx(EXIT_FAILURE, _("unmount of `%s' failed\n"), name);
+ break;
+ }
+}
+
+/* Open a device file. */
+static int open_device(const char *name)
+{
+ int fd = open(name, O_RDWR|O_NONBLOCK);
+
+ if (fd < 0)
+ fd = open(name, O_RDONLY|O_NONBLOCK);
+ if (fd == -1)
+ err(EXIT_FAILURE, _("cannot open %s"), name);
+ return fd;
+}
+
+/*
+ * See if device has been mounted by looking in mount table. If so, set
+ * device name and mount point name, and return 1, otherwise return 0.
+ */
+static int device_get_mountpoint(char **devname, char **mnt)
+{
+ struct libmnt_fs *fs;
+ int rc;
+
+ *mnt = NULL;
+
+ if (!mtab) {
+ mtab = mnt_new_table();
+ if (!mtab)
+ err(EXIT_FAILURE, _("failed to initialize libmount table"));
+
+ cache = mnt_new_cache();
+ mnt_table_set_cache(mtab, cache);
+
+ if (p_option)
+ rc = mnt_table_parse_file(mtab, _PATH_PROC_MOUNTINFO);
+ else
+ rc = mnt_table_parse_mtab(mtab, NULL);
+ if (rc)
+ err(EXIT_FAILURE, _("failed to parse mount table"));
+ }
+
+ fs = mnt_table_find_source(mtab, *devname, MNT_ITER_BACKWARD);
+ if (!fs) {
+ /* maybe 'devname' is mountpoint rather than a real device */
+ fs = mnt_table_find_target(mtab, *devname, MNT_ITER_BACKWARD);
+ if (fs) {
+ free(*devname);
+ *devname = xstrdup(mnt_fs_get_source(fs));
+ }
+ }
+
+ if (fs)
+ *mnt = xstrdup(mnt_fs_get_target(fs));
+ return *mnt ? 0 : -1;
+}
+
+static char *get_disk_devname(const char *device)
+{
+ struct stat st;
+ dev_t diskno = 0;
+ char diskname[128];
+
+ if (stat(device, &st) != 0)
+ return NULL;
+
+ /* get whole-disk devno */
+ if (sysfs_devno_to_wholedisk(st.st_rdev, diskname,
+ sizeof(diskname), &diskno) != 0)
+ return NULL;
+
+ return st.st_rdev == diskno ? NULL : find_device(diskname);
+}
+
+static int umount_partitions(const char *disk, int checkonly)
+{
+ struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
+ dev_t devno;
+ DIR *dir = NULL;
+ struct dirent *d;
+ int count = 0;
+
+ devno = sysfs_devname_to_devno(disk, NULL);
+ if (sysfs_init(&cxt, devno, NULL) != 0)
+ return 0;
+
+ /* open /sys/block/<wholedisk> */
+ if (!(dir = sysfs_opendir(&cxt, NULL)))
+ goto done;
+
+ /* scan for partition subdirs */
+ while ((d = readdir(dir))) {
+ if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ continue;
+
+ if (sysfs_is_partition_dirent(dir, d, disk)) {
+ char *mnt = NULL;
+ char *dev = find_device(d->d_name);
+
+ if (dev && device_get_mountpoint(&dev, &mnt) == 0) {
+ verbose(_("%s: mounted on %s"), dev, mnt);
+ if (!checkonly)
+ umount_one(mnt);
+ count++;
+ }
+ free(dev);
+ free(mnt);
+ }
+ }
+
+done:
+ if (dir)
+ closedir(dir);
+ sysfs_deinit(&cxt);
+
+ return count;
+}
+
+static int is_hotpluggable_subsystem(const char *name)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(hotplug_subsystems); i++)
+ if (strcmp(name, hotplug_subsystems[i]) == 0)
+ return 1;
+
+ return 0;
+}
+
+#define SUBSYSTEM_LINKNAME "/subsystem"
+
+/*
+ * For example:
+ *
+ * chain: /sys/dev/block/../../devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/ \
+ * 1-1.2:1.0/host65/target65:0:0/65:0:0:0/block/sdb
+ *
+ * The function check if <chain>/subsystem symlink exists, if yes then returns
+ * basename of the readlink result, and remove the last subdirectory from the
+ * <chain> path.
+ */
+static char *get_subsystem(char *chain, char *buf, size_t bufsz)
+{
+ size_t len;
+ char *p;
+
+ if (!chain || !*chain)
+ return NULL;
+
+ len = strlen(chain);
+ if (len + sizeof(SUBSYSTEM_LINKNAME) > PATH_MAX)
+ return NULL;
+
+ do {
+ ssize_t sz;
+
+ /* append "/subsystem" to the path */
+ memcpy(chain + len, SUBSYSTEM_LINKNAME, sizeof(SUBSYSTEM_LINKNAME));
+
+ /* try if subsystem symlink exists */
+ sz = readlink(chain, buf, bufsz);
+
+ /* remove last subsystem from chain */
+ chain[len] = '\0';
+ p = strrchr(chain, '/');
+ if (p) {
+ *p = '\0';
+ len = p - chain;
+ }
+
+ if (sz > 0) {
+ /* we found symlink to subsystem, return basename */
+ buf[sz] = '\0';
+ return basename(buf);
+ }
+
+ } while (p);
+
+ return NULL;
+}
+
+static int is_hotpluggable(const char* device)
+{
+ struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
+ char devchain[PATH_MAX];
+ char subbuf[PATH_MAX];
+ dev_t devno;
+ int rc = 0;
+ ssize_t sz;
+ char *sub;
+
+ devno = sysfs_devname_to_devno(device, NULL);
+ if (sysfs_init(&cxt, devno, NULL) != 0)
+ return 0;
+
+ /* check /sys/dev/block/<maj>:<min>/removable attribute */
+ if (sysfs_read_int(&cxt, "removable", &rc) == 0 && rc == 1) {
+ verbose(_("%s: is removable device"), device);
+ goto done;
+ }
+
+ /* read /sys/dev/block/<maj>:<min> symlink */
+ sz = sysfs_readlink(&cxt, NULL, devchain, sizeof(devchain));
+ if (sz <= 0 || sz + sizeof(_PATH_SYS_DEVBLOCK "/") > sizeof(devchain))
+ goto done;
+
+ devchain[sz++] = '\0';
+
+ /* create absolute patch from the link */
+ memmove(devchain + sizeof(_PATH_SYS_DEVBLOCK "/") - 1, devchain, sz);
+ memcpy(devchain, _PATH_SYS_DEVBLOCK "/",
+ sizeof(_PATH_SYS_DEVBLOCK "/") - 1);
+
+ while ((sub = get_subsystem(devchain, subbuf, sizeof(subbuf)))) {
+ rc = is_hotpluggable_subsystem(sub);
+ if (rc) {
+ verbose(_("%s: connected by hotplug subsystem: %s"),
+ device, sub);
+ break;
+ }
+ }
+
+done:
+ sysfs_deinit(&cxt);
+ return rc;
+}
+
+
+/* handle -x option */
+static void set_device_speed(char *name)
+{
+ int fd;
+
+ if (!x_option)
+ return;
+
+ if (x_arg == 0)
+ verbose(_("setting CD-ROM speed to auto"));
+ else
+ verbose(_("setting CD-ROM speed to %ldX"), x_arg);
+
+ fd = open_device(name);
+ select_speed(fd, x_arg);
+ exit(EXIT_SUCCESS);
+}
+
+
+/* main program */
+int main(int argc, char **argv)
+{
+ char *device = NULL;
+ char *disk = NULL;
+ char *mountpoint = NULL;
+ int worked = 0; /* set to 1 when successfully ejected */
+ int fd; /* file descriptor for device */
+
+ setlocale(LC_ALL,"");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ /* parse the command line arguments */
+ parse_args(argc, argv, &device);
+
+ /* handle -d option */
+ if (d_option) {
+ info(_("default device: `%s'"), EJECT_DEFAULT_DEVICE);
+ return EXIT_SUCCESS;
+ }
+
+ if (!device) {
+ device = mnt_resolve_path(EJECT_DEFAULT_DEVICE, NULL);
+ verbose(_("using default device `%s'"), device);
+ } else {
+ char *p;
+
+ if (device[strlen(device)-1] == '/')
+ device[strlen(device)-1] = '\0';
+
+ /* figure out full device or mount point name */
+ p = find_device(device);
+ if (p)
+ free(device);
+ else
+ p = device;
+
+ device = mnt_resolve_spec(p, NULL);
+ free(p);
+ }
+
+ if (!device)
+ errx(EXIT_FAILURE, _("%s: unable to find device"), device);
+
+ verbose(_("device name is `%s'"), device);
+
+ device_get_mountpoint(&device, &mountpoint);
+ if (mountpoint)
+ verbose(_("%s: mounted on %s"), device, mountpoint);
+ else
+ verbose(_("%s: not mounted"), device);
+
+ disk = get_disk_devname(device);
+ if (disk) {
+ verbose(_("%s: disc device: %s (disk device will be used for eject)"), device, disk);
+ free(device);
+ device = disk;
+ disk = NULL;
+ } else {
+ struct stat st;
+
+ if (stat(device, &st) != 0 || !S_ISBLK(st.st_mode))
+ errx(EXIT_FAILURE, _("%s: not found mountpoint or device "
+ "with the given name"), device);
+
+ verbose(_("%s: is whole-disk device"), device);
+ }
+
+ if (F_option == 0 && is_hotpluggable(device) == 0)
+ errx(EXIT_FAILURE, _("%s: is not hot-pluggable device"), device);
+
+ /* handle -n option */
+ if (n_option) {
+ info(_("device is `%s'"), device);
+ verbose(_("exiting due to -n/--noop option"));
+ return EXIT_SUCCESS;
+ }
+
+ /* handle -i option */
+ if (i_option) {
+ fd = open_device(device);
+ manual_eject(fd, i_arg);
+ return EXIT_SUCCESS;
+ }
+
+ /* handle -a option */
+ if (a_option) {
+ if (a_arg)
+ verbose(_("%s: enabling auto-eject mode"), device);
+ else
+ verbose(_("%s: disabling auto-eject mode"), device);
+ fd = open_device(device);
+ auto_eject(fd, a_arg);
+ return EXIT_SUCCESS;
+ }
+
+ /* handle -t option */
+ if (t_option) {
+ verbose(_("%s: closing tray"), device);
+ fd = open_device(device);
+ close_tray(fd);
+ set_device_speed(device);
+ return EXIT_SUCCESS;
+ }
+
+ /* handle -T option */
+ if (T_option) {
+ verbose(_("%s: toggling tray"), device);
+ fd = open_device(device);
+ toggle_tray(fd);
+ set_device_speed(device);
+ return EXIT_SUCCESS;
+ }
+
+ /* handle -X option */
+ if (X_option) {
+ verbose(_("%s: listing CD-ROM speed"), device);
+ fd = open_device(device);
+ list_speeds(device, fd);
+ return EXIT_SUCCESS;
+ }
+
+ /* handle -x option only */
+ if (!c_option)
+ set_device_speed(device);
+
+
+ /*
+ * Unmount all partitions if -m is not specified; or umount given
+ * mountpoint if -M is specified, otherwise print error of another
+ * partition is mounted.
+ */
+ if (!m_option) {
+ int ct = umount_partitions(device, M_option);
+
+ if (ct == 0 && mountpoint)
+ umount_one(mountpoint); /* probably whole-device */
+
+ if (M_option) {
+ if (ct == 1 && mountpoint)
+ umount_one(mountpoint);
+ else if (ct)
+ errx(EXIT_FAILURE, _("error: %s: device in use"), device);
+ }
+ }
+
+ /* handle -c option */
+ if (c_option) {
+ verbose(_("%s: selecting CD-ROM disc #%ld"), device, c_arg);
+ fd = open_device(device);
+ changer_select(fd, c_arg);
+ set_device_speed(device);
+ return EXIT_SUCCESS;
+ }
+
+ /* if user did not specify type of eject, try all four methods */
+ if (r_option + s_option + f_option + q_option == 0)
+ r_option = s_option = f_option = q_option = 1;
+
+ /* open device */
+ fd = open_device(device);
+
+ /* try various methods of ejecting until it works */
+ if (r_option) {
+ verbose(_("%s: trying to eject using CD-ROM eject command"), device);
+ worked = eject_cdrom(fd);
+ verbose(worked ? _("CD-ROM eject command succeeded") :
+ _("CD-ROM eject command failed"));
+ }
+
+ if (s_option && !worked) {
+ verbose(_("%s: trying to eject using SCSI commands"), device);
+ worked = eject_scsi(fd);
+ verbose(worked ? _("SCSI eject succeeded") :
+ _("SCSI eject failed"));
+ }
+
+ if (f_option && !worked) {
+ verbose(_("%s: trying to eject using floppy eject command"), device);
+ worked = eject_floppy(fd);
+ verbose(worked ? _("floppy eject command succeeded") :
+ _("floppy eject command failed"));
+ }
+
+ if (q_option && !worked) {
+ verbose(_("%s: trying to eject using tape offline command"), device);
+ worked = eject_tape(fd);
+ verbose(worked ? _("tape offline command succeeded") :
+ _("tape offline command failed"));
+ }
+
+ if (!worked)
+ errx(EXIT_FAILURE, _("unable to eject"));
+
+ /* cleanup */
+ close(fd);
+ free(device);
+ free(mountpoint);
+
+ mnt_free_table(mtab);
+ mnt_free_cache(cache);
+
+ return EXIT_SUCCESS;
+}
diff --git a/sys-utils/fallocate.1 b/sys-utils/fallocate.1
new file mode 100644
index 0000000..4952334
--- /dev/null
+++ b/sys-utils/fallocate.1
@@ -0,0 +1,62 @@
+.\" -*- nroff -*-
+.TH FALLOCATE 1 "September 2011" "util-linux" "User Commands"
+.SH NAME
+fallocate \- preallocate space to a file
+.SH SYNOPSIS
+.B fallocate
+.RB [ \-n ]
+.RB [ \-p ]
+.RB [ \-o
+.IR offset ]
+.B \-l
+.IR length
+.I filename
+.SH DESCRIPTION
+.B fallocate
+is used to preallocate blocks to a file. For filesystems which support the
+fallocate system call, this is done quickly by allocating blocks and marking
+them as uninitialized, requiring no IO to the data blocks. This is much faster
+than creating a file by filling it with zeros.
+.PP
+As of the Linux Kernel v2.6.31, the fallocate system call is supported by the
+btrfs, ext4, ocfs2, and xfs filesystems.
+.PP
+The exit code returned by
+.B fallocate
+is 0 on success and 1 on failure.
+.PP
+.SH OPTIONS
+The \fIlength\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.
+.IP "\fB\-n, \-\-keep-size\fP"
+Do not modify the apparent length of the file. This may effectively allocate
+blocks past EOF, which can be removed with a truncate.
+.IP "\fB\-p, \-\-punch-hole\fP"
+Punch holes in the file, the range should not exceed the length of the file.
+.IP "\fB\-o, \-\-offset\fP \fIoffset\fP
+Specifies the beginning offset of the allocation, in bytes.
+.IP "\fB\-l, \-\-length\fP \fIlength\fP
+Specifies the length of the allocation, in bytes.
+.IP "\fB\-h, \-\-help\fP"
+Print help and exit.
+.IP "\fB-V, \-\-version"
+Print version and exit.
+.SH AUTHORS
+.UR sandeen@redhat.com
+Eric Sandeen
+.UE
+.br
+.UR kzak@redhat.com
+Karel Zak
+.UE
+.SH SEE ALSO
+.BR fallocate (2),
+.BR posix_fallocate (3),
+.BR truncate (1)
+.SH AVAILABILITY
+The fallocate 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/sys-utils/fallocate.c b/sys-utils/fallocate.c
new file mode 100644
index 0000000..ff0f9e6
--- /dev/null
+++ b/sys-utils/fallocate.c
@@ -0,0 +1,173 @@
+/*
+ * fallocate - utility to use the fallocate system call
+ *
+ * Copyright (C) 2008-2009 Red Hat, Inc. All rights reserved.
+ * Written by Eric Sandeen <sandeen@redhat.com>
+ * Karel Zak <kzak@redhat.com>
+ *
+ * cvtnum routine taken from xfsprogs,
+ * Copyright (c) 2003-2005 Silicon Graphics, Inc.
+ *
+ * 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.
+ *
+ * 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 <limits.h>
+
+#ifndef HAVE_FALLOCATE
+# include <sys/syscall.h>
+#endif
+
+#ifdef HAVE_LINUX_FALLOC_H
+# include <linux/falloc.h> /* for FALLOC_FL_* flags */
+#endif
+
+#ifndef FALLOC_FL_KEEP_SIZE
+# define FALLOC_FL_KEEP_SIZE 1
+#endif
+
+#ifndef FALLOC_FL_PUNCH_HOLE
+# define FALLOC_FL_PUNCH_HOLE 2
+#endif
+
+#include "nls.h"
+#include "strutils.h"
+#include "c.h"
+#include "closestream.h"
+
+static void __attribute__((__noreturn__)) usage(FILE *out)
+{
+ fputs(USAGE_HEADER, out);
+ fprintf(out,
+ _(" %s [options] <filename>\n"), program_invocation_short_name);
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -n, --keep-size don't modify the length of the file\n"
+ " -p, --punch-hole punch holes in the file\n"
+ " -o, --offset <num> offset of the allocation, in bytes\n"
+ " -l, --length <num> length of the allocation, in bytes\n"), out);
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ fprintf(out, USAGE_MAN_TAIL("fallocate(1)"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static loff_t cvtnum(char *s)
+{
+ uintmax_t x;
+
+ if (strtosize(s, &x))
+ return -1LL;
+
+ return x;
+}
+
+int main(int argc, char **argv)
+{
+ char *fname;
+ int c;
+ int error;
+ int fd;
+ int mode = 0;
+ loff_t length = -2LL;
+ loff_t offset = 0;
+
+ static const struct option longopts[] = {
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'V' },
+ { "keep-size", 0, 0, 'n' },
+ { "punch-hole", 0, 0, 'p' },
+ { "offset", 1, 0, 'o' },
+ { "length", 1, 0, 'l' },
+ { NULL, 0, 0, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while ((c = getopt_long(argc, argv, "hVnpl:o:", longopts, NULL)) != -1) {
+ switch(c) {
+ case 'h':
+ usage(stdout);
+ break;
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'p':
+ mode |= FALLOC_FL_PUNCH_HOLE;
+ /* fall through */
+ case 'n':
+ mode |= FALLOC_FL_KEEP_SIZE;
+ break;
+ case 'l':
+ length = cvtnum(optarg);
+ break;
+ case 'o':
+ offset = cvtnum(optarg);
+ break;
+ default:
+ usage(stderr);
+ break;
+ }
+ }
+
+ if (length == -2LL)
+ errx(EXIT_FAILURE, _("no length argument specified"));
+ if (length <= 0)
+ errx(EXIT_FAILURE, _("invalid length value specified"));
+ if (offset < 0)
+ errx(EXIT_FAILURE, _("invalid offset value specified"));
+ if (optind == argc)
+ errx(EXIT_FAILURE, _("no filename specified."));
+
+ fname = argv[optind++];
+
+ if (optind != argc) {
+ warnx(_("unexpected number of arguments"));
+ usage(stderr);
+ }
+
+ fd = open(fname, O_WRONLY|O_CREAT, 0644);
+ if (fd < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), fname);
+
+#ifdef HAVE_FALLOCATE
+ error = fallocate(fd, mode, offset, length);
+#else
+ error = syscall(SYS_fallocate, fd, mode, offset, length);
+#endif
+ /*
+ * EOPNOTSUPP: The FALLOC_FL_KEEP_SIZE is unsupported
+ * ENOSYS: The filesystem does not support sys_fallocate
+ */
+ if (error < 0) {
+ if ((mode & FALLOC_FL_KEEP_SIZE) && errno == EOPNOTSUPP)
+ errx(EXIT_FAILURE,
+ _("keep size mode (-n option) unsupported"));
+ err(EXIT_FAILURE, _("%s: fallocate failed"), fname);
+ }
+
+ close(fd);
+ return EXIT_SUCCESS;
+}
diff --git a/sys-utils/flock.1 b/sys-utils/flock.1
new file mode 100644
index 0000000..b50c619
--- /dev/null
+++ b/sys-utils/flock.1
@@ -0,0 +1,166 @@
+.\" -----------------------------------------------------------------------
+.\"
+.\" Copyright 2003-2006 H. Peter Anvin - All Rights Reserved
+.\"
+.\" 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.
+.\"
+.\" -----------------------------------------------------------------------
+.TH FLOCK 1 "September 2011" "util-linux" "User Commands"
+.SH NAME
+flock \- manage locks from shell scripts
+.SH SYNOPSIS
+.B flock
+[options] <file> -c <command>
+.br
+.B flock
+[options] <directory> -c <command>
+.br
+.B flock
+[options] <file descriptor number>
+.SH DESCRIPTION
+.PP
+This utility manages
+.BR flock (2)
+locks from within shell scripts or the command line.
+.PP
+The first and second forms wrap the lock around the executing a command, in
+a manner similar to
+.BR su (1)
+or
+.BR newgrp (1).
+It locks a specified file or directory, which is created (assuming
+appropriate permissions), if it does not already exist. By default, if the
+lock cannot be immediately acquired,
+.B flock
+waits until the lock is available.
+.PP
+The third form uses open file by file descriptor number. See examples how
+that can be used.
+.SH OPTIONS
+.TP
+\fB\-s\fP, \fB\-\-shared\fP
+Obtain a shared lock, sometimes called a read lock.
+.TP
+\fB\-x\fP, \fB\-e\fP, \fB\-\-exclusive\fP
+Obtain an exclusive lock, sometimes called a write lock. This is the
+default.
+.TP
+\fB\-u\fP, \fB\-\-unlock\fP
+Drop a lock. This is usually not required, since a lock is automatically
+dropped when the file is closed. However, it may be required in special
+cases, for example if the enclosed command group may have forked a background
+process which should not be holding the lock.
+.TP
+\fB\-n\fP, \fB\-\-nb\fP, \fB\-\-nonblock\fP
+Fail rather than wait if the lock cannot be
+immediately acquired.
+See the
+.I \-E
+option for the exit code used.
+.TP
+\fB\-w\fP, \fB\-\-wait\fP, \fB\-\-timeout\fP \fIseconds\fP
+Fail if the lock cannot be acquired within
+.IR seconds .
+Decimal fractional values are allowed.
+See the
+.I \-E
+option for the exit code used.
+.TP
+\fB\-o\fP, \fB\-\-close\fP
+Close the file descriptor on which the lock is held before executing
+.BR command\ .
+This is useful if
+.B command
+spawns a child process which should not be holding the lock.
+.TP
+\fB\-E\fP, \fB\-\-conflict\-exit\-code\fP \fInumber\fP
+The exit code used when the \fB\-n\fP option is in use, and the
+conflicting lock exists, or the \fB\-w\fP option is in use,
+and the timeout is reached. The default value is 1.
+.TP
+\fB\-c\fP, \fB\-\-command\fP \fIcommand\fP
+Pass a single
+.IR command ,
+without arguments, to the shell with
+.BR -c .
+.TP
+\fB\-h\fP, \fB\-\-help\fP
+Print a help message.
+.IP "\fB\-V, \-\-version\fP"
+Show version number and exit.
+.SH EXAMPLES
+.TP
+shell1> flock /tmp -c cat
+.TQ
+shell2> flock -w .007 /tmp -c echo; /bin/echo $?
+Set exclusive lock to directory /tmp and the second command will fail.
+.TP
+shell1> flock -s /tmp -c cat
+.TQ
+shell2> flock -s -w .007 /tmp -c echo; /bin/echo $?
+Set shared lock to directory /tmp and the second command will not fail.
+Notice that attempting to get exclusive lock with second command would fail.
+.TP
+(
+.TQ
+ flock -n 9 || exit 1
+.TQ
+ # ... commands executed under lock ...
+.TQ
+) 9>/var/lock/mylockfile
+The form is convenient inside shell scripts. The mode used to open the file
+doesn't matter to
+.BR flock ;
+using
+.I >
+or
+.I >>
+allows the lockfile to be created if it does not already exist, however,
+write permission is required. Using
+.I <
+requires that the file already exists but only read permission is required.
+.SH "EXIT STATUS"
+The command uses
+.B sysexits.h
+return values for everything else but an options
+.I \-n
+or
+.I \-w
+failures which return either the value given by the
+.I \-E
+option, or 1 by default.
+.SH AUTHOR
+.UR hpa@zytor.com
+H. Peter Anvin
+.UE
+.SH COPYRIGHT
+Copyright \(co 2003\-2006 H. Peter Anvin.
+.br
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.BR flock (2)
+.SH AVAILABILITY
+The flock 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/sys-utils/flock.c b/sys-utils/flock.c
new file mode 100644
index 0000000..5a3422a
--- /dev/null
+++ b/sys-utils/flock.c
@@ -0,0 +1,343 @@
+/* Copyright 2003-2005 H. Peter Anvin - All Rights Reserved
+ *
+ * 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 <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "closestream.h"
+
+static void __attribute__((__noreturn__)) usage(int ex)
+{
+ fprintf(stderr, USAGE_HEADER);
+ fprintf(stderr,
+ _(" %1$s [options] <file descriptor number>\n"
+ " %1$s [options] <file> -c <command>\n"
+ " %1$s [options} <directory> -c <command>\n"),
+ program_invocation_short_name);
+ fputs(USAGE_OPTIONS, stderr);
+ fputs(_( " -s --shared get a shared lock\n"), stderr);
+ fputs(_( " -x --exclusive get an exclusive lock (default)\n"), stderr);
+ fputs(_( " -u --unlock remove a lock\n"), stderr);
+ fputs(_( " -n --nonblock fail rather than wait\n"), stderr);
+ fputs(_( " -w --timeout <secs> wait for a limited amount of time\n"), stderr);
+ fputs(_( " -E --conflict-exit-code <number> exit code after conflict or timeout\n"), stderr);
+ fputs(_( " -o --close close file descriptor before running command\n"), stderr);
+ fputs(_( " -c --command <command> run a single command string through the shell\n"), stderr);
+ fprintf(stderr, USAGE_SEPARATOR);
+ fprintf(stderr, USAGE_HELP);
+ fprintf(stderr, USAGE_VERSION);
+ fprintf(stderr, USAGE_MAN_TAIL("flock(1)"));
+ exit(ex);
+}
+
+static sig_atomic_t timeout_expired = 0;
+
+static void timeout_handler(int sig __attribute__((__unused__)))
+{
+ timeout_expired = 1;
+}
+
+static void strtotimeval(const char *str, struct timeval *tv)
+{
+ double user_input;
+
+ user_input = strtod_or_err(str, "bad number");
+ tv->tv_sec = (time_t) user_input;
+ tv->tv_usec = (long)((user_input - tv->tv_sec) * 1000000);
+ if ((tv->tv_sec + tv->tv_usec) == 0)
+ errx(EX_USAGE, _("timeout cannot be zero"));
+}
+
+static void setup_timer(struct itimerval *timer, struct itimerval *old_timer,
+ struct sigaction *sa, struct sigaction *old_sa)
+{
+ memset(sa, 0, sizeof *sa);
+ sa->sa_handler = timeout_handler;
+ sa->sa_flags = SA_RESETHAND;
+ sigaction(SIGALRM, sa, old_sa);
+ setitimer(ITIMER_REAL, timer, old_timer);
+}
+
+static void cancel_timer(struct itimerval *old_timer, struct sigaction *old_sa)
+{
+ setitimer(ITIMER_REAL, old_timer, NULL);
+ sigaction(SIGALRM, old_sa, NULL);
+}
+
+static int open_file(const char *filename, int *flags)
+{
+
+ int fd;
+ int fl = *flags == 0 ? O_RDONLY : *flags;
+
+ errno = 0;
+ fl |= O_NOCTTY | O_CREAT;
+ fd = open(filename, fl, 0666);
+
+ /* Linux doesn't like O_CREAT on a directory, even though it
+ * should be a no-op; POSIX doesn't allow O_RDWR or O_WRONLY
+ */
+ if (fd < 0 && errno == EISDIR) {
+ fl = O_RDONLY | O_NOCTTY;
+ fd = open(filename, fl);
+ }
+ if (fd < 0) {
+ warn(_("cannot open lock file %s"), filename);
+ if (errno == ENOMEM || errno == EMFILE || errno == ENFILE)
+ exit(EX_OSERR);
+ if (errno == EROFS || errno == ENOSPC)
+ exit(EX_CANTCREAT);
+ exit(EX_NOINPUT);
+ }
+ *flags = fl;
+ return fd;
+}
+
+int main(int argc, char *argv[])
+{
+ struct itimerval timeout, old_timer;
+ int have_timeout = 0;
+ int type = LOCK_EX;
+ int block = 0;
+ int open_flags = 0;
+ int fd = -1;
+ int opt, ix;
+ int do_close = 0;
+ int status;
+ /*
+ * The default exit code for lock conflict or timeout
+ * is specified in man flock.1
+ */
+ int conflict_exit_code = 1;
+ char **cmd_argv = NULL, *sh_c_argv[4];
+ const char *filename = NULL;
+ struct sigaction sa, old_sa;
+
+ static const struct option long_options[] = {
+ {"shared", no_argument, NULL, 's'},
+ {"exclusive", no_argument, NULL, 'x'},
+ {"unlock", no_argument, NULL, 'u'},
+ {"nonblocking", no_argument, NULL, 'n'},
+ {"nb", no_argument, NULL, 'n'},
+ {"timeout", required_argument, NULL, 'w'},
+ {"wait", required_argument, NULL, 'w'},
+ {"conflict-exit-code", required_argument, NULL, 'E'},
+ {"close", no_argument, NULL, 'o'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0}
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ if (argc < 2)
+ usage(EX_USAGE);
+
+ memset(&timeout, 0, sizeof timeout);
+
+ optopt = 0;
+ while ((opt =
+ getopt_long(argc, argv, "+sexnouw:E:hV?", long_options,
+ &ix)) != EOF) {
+ switch (opt) {
+ case 's':
+ type = LOCK_SH;
+ break;
+ case 'e':
+ case 'x':
+ type = LOCK_EX;
+ break;
+ case 'u':
+ type = LOCK_UN;
+ break;
+ case 'o':
+ do_close = 1;
+ break;
+ case 'n':
+ block = LOCK_NB;
+ break;
+ case 'w':
+ have_timeout = 1;
+ strtotimeval(optarg, &timeout.it_value);
+ break;
+ case 'E':
+ conflict_exit_code = strtos32_or_err(optarg,
+ _("invalid exit code"));
+ break;
+ case 'V':
+ printf("flock (%s)\n", PACKAGE_STRING);
+ exit(EX_OK);
+ default:
+ /* optopt will be set if this was an unrecognized
+ * option, i.e. *not* 'h' or '?
+ */
+ usage(optopt ? EX_USAGE : 0);
+ break;
+ }
+ }
+
+ if (argc > optind + 1) {
+ /* Run command */
+ if (!strcmp(argv[optind + 1], "-c") ||
+ !strcmp(argv[optind + 1], "--command")) {
+ if (argc != optind + 3)
+ errx(EX_USAGE,
+ _("%s requires exactly one command argument"),
+ argv[optind + 1]);
+ cmd_argv = sh_c_argv;
+ cmd_argv[0] = getenv("SHELL");
+ if (!cmd_argv[0] || !*cmd_argv[0])
+ cmd_argv[0] = _PATH_BSHELL;
+ cmd_argv[1] = "-c";
+ cmd_argv[2] = argv[optind + 2];
+ cmd_argv[3] = 0;
+ } else {
+ cmd_argv = &argv[optind + 1];
+ }
+
+ filename = argv[optind];
+ fd = open_file(filename, &open_flags);
+
+ } else if (optind < argc) {
+ /* Use provided file descriptor */
+ fd = (int)strtol_or_err(argv[optind], "bad number");
+ } else {
+ /* Bad options */
+ errx(EX_USAGE, _("requires file descriptor, file or directory"));
+ }
+
+ if (have_timeout) {
+ if (timeout.it_value.tv_sec == 0 &&
+ timeout.it_value.tv_usec == 0) {
+ /* -w 0 is equivalent to -n; this has to be
+ * special-cased because setting an itimer to zero
+ * means disabled!
+ */
+ have_timeout = 0;
+ block = LOCK_NB;
+ } else
+ setup_timer(&timeout, &old_timer, &sa, &old_sa);
+ }
+
+ while (flock(fd, type | block)) {
+ switch (errno) {
+ case EWOULDBLOCK:
+ /* -n option set and failed to lock. */
+ exit(conflict_exit_code);
+ case EINTR:
+ /* Signal received */
+ if (timeout_expired)
+ /* -w option set and failed to lock. */
+ exit(conflict_exit_code);
+ /* otherwise try again */
+ continue;
+ case EIO:
+ /* Probably NFSv4 where flock() is emulated by fcntl().
+ * Let's try to reopen in read-write mode.
+ */
+ if (!(open_flags & O_RDWR) &&
+ type != LOCK_SH &&
+ access(filename, R_OK | W_OK) == 0) {
+
+ close(fd);
+ open_flags = O_RDWR;
+ fd = open_file(filename, &open_flags);
+
+ if (open_flags & O_RDWR)
+ break;
+ }
+ /* go through */
+ default:
+ /* Other errors */
+ if (filename)
+ warn("%s", filename);
+ else
+ warn("%d", fd);
+ exit((errno == ENOLCK
+ || errno == ENOMEM) ? EX_OSERR : EX_DATAERR);
+ }
+ }
+
+ if (have_timeout)
+ cancel_timer(&old_timer, &old_sa);
+
+ status = EX_OK;
+
+ if (cmd_argv) {
+ pid_t w, f;
+ /* Clear any inherited settings */
+ signal(SIGCHLD, SIG_DFL);
+ f = fork();
+
+ if (f < 0) {
+ err(EX_OSERR, _("fork failed"));
+ } else if (f == 0) {
+ if (do_close)
+ close(fd);
+ execvp(cmd_argv[0], cmd_argv);
+ /* execvp() failed */
+ warn("%s", cmd_argv[0]);
+ _exit((errno == ENOMEM) ? EX_OSERR : EX_UNAVAILABLE);
+ } else {
+ do {
+ w = waitpid(f, &status, 0);
+ if (w == -1 && errno != EINTR)
+ break;
+ } while (w != f);
+
+ if (w == -1) {
+ status = EXIT_FAILURE;
+ warn(_("waitpid failed"));
+ } else if (WIFEXITED(status))
+ status = WEXITSTATUS(status);
+ else if (WIFSIGNALED(status))
+ status = WTERMSIG(status) + 128;
+ else
+ /* WTF? */
+ status = EX_OSERR;
+ }
+ }
+
+ return status;
+}
diff --git a/sys-utils/fsfreeze.8 b/sys-utils/fsfreeze.8
new file mode 100644
index 0000000..7693861
--- /dev/null
+++ b/sys-utils/fsfreeze.8
@@ -0,0 +1,73 @@
+.\" -*- nroff -*-
+.TH FSFREEZE 8 "May 2010" "util-linux" "System Administration"
+.SH NAME
+fsfreeze \- suspend access to a filesystem (Linux Ext3/4, ReiserFS, JFS, XFS)
+.SH SYNOPSIS
+.B fsfreeze \-f
+.I mountpoint
+
+.B fsfreeze \-u
+.I mountpoint
+
+.SH DESCRIPTION
+.B fsfreeze
+suspends and resumes access to an filesystem
+.PP
+.B fsfreeze
+halts new access to the filesystem and creates a stable image on disk.
+.B fsfreeze
+is intended to be used with hardware RAID devices that support the creation
+of snapshots.
+.PP
+.B fsfreeze
+is unnecessary for
+.B device-mapper
+devices. The device-mapper (and LVM)
+automatically freezes filesystem on the device when a snapshot creation is requested.
+For more details see the
+.BR dmsetup (8)
+man page.
+.PP
+The
+.I mount-point
+argument is the pathname of the directory where the filesystem
+is mounted.
+The filesystem must be mounted to be frozen (see
+.BR mount (8)).
+.SH OPTIONS
+.IP "\fB\-h, \-\-help\fP"
+Print help and exit.
+.IP "\fB\-f, \-\-freeze\fP"
+This option requests the specified a filesystem to be frozen from new
+modifications. When this is selected, all ongoing transactions in the
+filesystem are allowed to complete, new write system calls are halted, other
+calls which modify the filesystem are halted, and all dirty data, metadata, and
+log information are written to disk. Any process attempting to write to the
+frozen filesystem will block waiting for the filesystem to be unfrozen.
+
+Note that even after freezing, the on-disk filesystem can contain
+information on files that are still in the process of unlinking.
+These files will not be unlinked until the filesystem is unfrozen
+or a clean mount of the snapshot is complete.
+.IP "\fB\-u, \-\-unfreeze\fP
+This option is used to un-freeze the filesystem and allow operations to
+continue. Any filesystem modifications that were blocked by the freeze are
+unblocked and allowed to complete.
+.SH AUTHOR
+.PP
+Written by Hajime Taira.
+.SH NOTES
+.PP
+This man page based on xfs_freeze.
+One of
+.B \-f
+or
+.B \-u
+must be supplied to
+.BR fsfreeze .
+.SH SEE ALSO
+.BR mount (8)
+.SH AVAILABILITY
+The fsfreeze command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+
diff --git a/sys-utils/fsfreeze.c b/sys-utils/fsfreeze.c
new file mode 100644
index 0000000..7161442
--- /dev/null
+++ b/sys-utils/fsfreeze.c
@@ -0,0 +1,134 @@
+/*
+ * fsfreeze.c -- Filesystem freeze/unfreeze IO for Linux
+ *
+ * Copyright (C) 2010 Hajime Taira <htaira@redhat.com>
+ * Masatake Yamato <yamato@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 1 or
+ * (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <getopt.h>
+
+#include "blkdev.h"
+#include "nls.h"
+#include "closestream.h"
+#include "c.h"
+
+static int freeze_f(int fd)
+{
+ return ioctl(fd, FIFREEZE, 0);
+}
+
+static int unfreeze_f(int fd)
+{
+ return ioctl(fd, FITHAW, 0);
+}
+
+static void __attribute__((__noreturn__)) usage(FILE *out)
+{
+ fputs(_("\nUsage:\n"), out);
+ fprintf(out,
+ _(" %s [options] <mount point>\n"), program_invocation_short_name);
+
+ fputs(_("\nOptions:\n"), out);
+ fputs(_(" -h, --help this help\n"
+ " -f, --freeze freeze the filesystem\n"
+ " -u, --unfreeze unfreeze the filesystem\n"), out);
+
+ fputs(_("\nFor more information see fsfreeze(8).\n"), out);
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ int fd = -1, c;
+ int freeze = -1, rc = EXIT_FAILURE;
+ char *path;
+ struct stat sb;
+
+ static const struct option longopts[] = {
+ { "help", 0, 0, 'h' },
+ { "freeze", 0, 0, 'f' },
+ { "unfreeze", 0, 0, 'u' },
+ { NULL, 0, 0, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while ((c = getopt_long(argc, argv, "hfu", longopts, NULL)) != -1) {
+ switch(c) {
+ case 'h':
+ usage(stdout);
+ break;
+ case 'f':
+ freeze = TRUE;
+ break;
+ case 'u':
+ freeze = FALSE;
+ break;
+ default:
+ usage(stderr);
+ break;
+ }
+ }
+
+ if (freeze == -1)
+ errx(EXIT_FAILURE, _("no action specified"));
+ if (optind == argc)
+ errx(EXIT_FAILURE, _("no filename specified"));
+ path = argv[optind++];
+
+ if (optind != argc) {
+ warnx(_("unexpected number of arguments"));
+ usage(stderr);
+ }
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), path);
+
+ if (fstat(fd, &sb) == -1) {
+ warn(_("stat failed %s"), path);
+ goto done;
+ }
+
+ if (!S_ISDIR(sb.st_mode)) {
+ warnx(_("%s: is not a directory"), path);
+ goto done;
+ }
+
+ if (freeze) {
+ if (freeze_f(fd)) {
+ warn(_("%s: freeze failed"), path);
+ goto done;
+ }
+ } else {
+ if (unfreeze_f(fd)) {
+ warn(_("%s: unfreeze failed"), path);
+ goto done;
+ }
+ }
+
+ rc = EXIT_SUCCESS;
+done:
+ if (fd >= 0)
+ close(fd);
+ return rc;
+}
+
diff --git a/sys-utils/fstab.5 b/sys-utils/fstab.5
new file mode 100644
index 0000000..91c3157
--- /dev/null
+++ b/sys-utils/fstab.5
@@ -0,0 +1,271 @@
+.\" Copyright (c) 1980, 1989, 1991 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.
+.\"
+.\" @(#)fstab.5 6.5 (Berkeley) 5/10/91
+.\"
+.\" Modified Sat Mar 6 20:45:03 1993, faith@cs.unc.edu, for Linux
+.\" Sat Oct 9 10:07:10 1993: converted to man format by faith@cs.unc.edu
+.\" Sat Nov 20 20:47:38 1993: hpfs documentation added
+.\" Sat Nov 27 20:23:32 1993: Updated authorship information
+.\" Wed Jul 26 00:00:00 1995: Updated some nfs stuff, joey@infodrom.north.de
+.\" Tue Apr 2 00:38:28 1996: added info about "noauto", "user", etc.
+.\" Tue Jun 15 20:02:18 1999: added LABEL and UUID
+.\" Sat Jul 14 2001: Michael K. Johnson <johnsonm@redhat.com> added -O
+.\"
+.TH FSTAB 5 "August 2010" "util-linux" "File Formats"
+.SH NAME
+fstab \- static information about the filesystems
+.SH SYNOPSIS
+.B /etc/fstab
+.SH DESCRIPTION
+The file
+.B fstab
+contains descriptive information about the various file systems.
+.B fstab
+is only read by programs, and not written; it is the duty of the system
+administrator to properly create and maintain this file. Each filesystem
+is described on a separate line; fields on each line are separated by tabs or
+spaces. Lines starting with '#' are comments, blank lines are ignored. The
+order of records in
+.B fstab
+is important because
+.BR fsck (8),
+.BR mount (8),
+and
+.BR umount (8)
+sequentially iterate through
+.B fstab
+doing their thing.
+
+.B The first field
+.RI ( fs_spec ).
+.RS
+This field describes the block special device or
+remote filesystem to be mounted.
+.LP
+For ordinary mounts it will hold (a link to) a block special
+device node (as created by
+.BR mknod (8))
+for the device to be mounted, like `/dev/cdrom' or `/dev/sdb7'.
+For NFS mounts one will have <host>:<dir>, e.g., `knuth.aeb.nl:/'.
+For procfs, use `proc'.
+.LP
+Instead of giving the device explicitly, one may indicate
+the filesystem that is to be mounted by its UUID or
+LABEL (cf.
+.BR e2label (8)
+or
+.BR xfs_admin (8)),
+writing LABEL=<label> or UUID=<uuid>,
+e.g., `LABEL=Boot' or `UUID=3e6be9de\%-8139\%-11d1\%-9106\%-a43f08d823a6'.
+
+It's also possible to use PARTUUID= and PARTLABEL=. These partitions identifiers
+are supported for GUID Partition Table (GPT) and MAC partition table only.
+
+See
+.BR blkid (8)
+or
+.BR lsblk (8)
+for more details about devices identifiers.
+
+.LP
+Note that
+.BR mount (8)
+uses UUIDs as strings. The string representation of the UUID should be based on
+lower case characters.
+.RE
+
+.B The second field
+.RI ( fs_file ).
+.RS
+This field describes the mount point for the filesystem. For swap partitions, this
+field should be specified as `none'. If the name of the mount point
+contains spaces these can be escaped as `\\040'.
+.RE
+
+.B The third field
+.RI ( fs_vfstype ).
+.RS
+This field describes the type of the filesystem. Linux supports lots
+of filesystem types, such as
+.IR adfs ,
+.IR affs ,
+.IR autofs ,
+.IR coda ,
+.IR coherent ,
+.IR cramfs ,
+.IR devpts ,
+.IR efs ,
+.IR ext2 ,
+.IR ext3 ,
+.IR hfs ,
+.IR hpfs ,
+.IR iso9660 ,
+.IR jfs ,
+.IR minix ,
+.IR msdos ,
+.IR ncpfs ,
+.IR nfs ,
+.IR ntfs ,
+.IR proc ,
+.IR qnx4 ,
+.IR reiserfs ,
+.IR romfs ,
+.IR smbfs ,
+.IR sysv ,
+.IR tmpfs ,
+.IR udf ,
+.IR ufs ,
+.IR umsdos ,
+.IR vfat ,
+.IR xenix ,
+.IR xfs ,
+and possibly others. For more details, see
+.BR mount (8).
+
+For the filesystems currently supported by the running kernel, see
+.IR /proc/filesystems .
+
+An entry
+.I swap
+denotes a file or partition to be used
+for swapping, cf.\&
+.BR swapon (8).
+An entry
+.I none
+is useful for bind or move mounts.
+
+.BR mount (8)
+and
+.BR umount (8)
+support filesystem
+.IR subtypes .
+The subtype is defined by '.subtype' suffix. For
+example 'fuse.sshfs'. It's recommended to use subtype notation rather than add
+any prefix to the first fstab field (for example 'sshfs#example.com' is
+deprecated).
+.RE
+
+.B The fourth field
+.RI ( fs_mntops ).
+.RS
+This field describes the mount options associated with the filesystem.
+
+It is formatted as a comma separated list of options. It contains at least
+the type of mount plus any additional options appropriate to the filesystem
+type. For documentation on the available mount options, see
+.BR mount (8).
+For documentation on the available swap options, see
+.BR swapon (8).
+
+Basic file system independent options are:
+.TP
+.B defaults
+use default options: rw, suid, dev, exec, auto, nouser, and async.
+.TP
+.B noauto
+do not mount when "mount -a" is given (e.g., at boot time)
+.TP
+.B user
+allow a user to mount
+.TP
+.B owner
+allow device owner to mount
+.TP
+.B comment
+or
+.B x-<name>
+for use by fstab-maintaining programs
+.TP
+.B nofail
+do not report errors for this device if it does not exist.
+.RE
+
+.B The fifth field
+.RI ( fs_freq ).
+.RS
+This field is used for these filesystems by the
+.BR dump (8)
+command to determine which filesystems need to be dumped. If the fifth
+field is not present, a value of zero is returned and
+.B dump
+will assume that the filesystem does not need to be dumped.
+.RE
+
+.B The sixth field
+.RI ( fs_passno ).
+.RS
+This field is used by the
+.BR fsck (8)
+program to determine the order in which filesystem checks are done at
+reboot time. The root filesystem should be specified with a
+.I fs_passno
+of 1, and other filesystems should have a
+.I fs_passno
+of 2. Filesystems within a drive will be checked sequentially, but
+filesystems on different drives will be checked at the same time to utilize
+parallelism available in the hardware. If the sixth field is not present
+or zero, a value of zero is returned and
+.B fsck
+will assume that the filesystem does not need to be checked.
+.RE
+
+.SH NOTES
+The proper way to read records from
+.B fstab
+is to use the routines
+.BR getmntent (3)
+or
+.BR libmount .
+
+The keyword
+.B ignore
+as filesystem type (3rd field) is not more supported by the pure
+libmount based mount utility (since util-linux v2.22).
+
+.SH FILES
+.IR /etc/fstab ,
+.I <fstab.h>
+.SH "SEE ALSO"
+.BR findmnt (8),
+.BR mount (8),
+.BR swapon (8),
+.BR fs (5),
+.BR getmntent (3)
+.SH HISTORY
+The ancestor of this
+.B fstab
+file format appeared in 4.0BSD.
+.\" But without comment convention, and options and vfs_type.
+.\" Instead there was a type rw/ro/rq/sw/xx, where xx is the present 'ignore'.
+.SH AVAILABILITY
+This man page is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/sys-utils/fstrim.8 b/sys-utils/fstrim.8
new file mode 100644
index 0000000..a235703
--- /dev/null
+++ b/sys-utils/fstrim.8
@@ -0,0 +1,85 @@
+.\" -*- nroff -*-
+.TH FSTRIM 8 "November 2010" "util-linux" "System Administration"
+.SH NAME
+fstrim \- discard unused blocks on a mounted filesystem
+.SH SYNOPSIS
+.B fstrim
+.RB [ \-o
+.IR offset ]
+.RB [ \-l
+.IR length ]
+.RB [ \-m
+.IR minimum-free-extent ]
+.RB [ \-v ]
+.I mountpoint
+
+.SH DESCRIPTION
+.B fstrim
+is used on a mounted filesystem to discard (or "trim") blocks which are not in
+use by the filesystem. This is useful for solid-state drives (SSDs) and
+thinly-provisioned storage.
+.PP
+By default,
+.B fstrim
+will discard all unused blocks in the filesystem. Options may be used to
+modify this behavior based on range or size, as explained below.
+.PP
+The
+.I mountpoint
+argument is the pathname of the directory where the filesystem
+is mounted.
+
+.SH OPTIONS
+The \fIoffset\fR, \fIlength\fR, and \fIminimum-free-extent\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.
+.IP "\fB\-h, \-\-help\fP"
+Print help and exit.
+.IP "\fB\-o, \-\-offset\fP \fIoffset\fP"
+Byte offset in filesystem from which to begin searching for free blocks
+to discard. Default value is zero, starting at the beginning of the
+filesystem.
+.IP "\fB\-l, \-\-length\fP \fIlength\fP"
+Number of bytes after starting point to search for free blocks to discard.
+If the specified value extends past the end of the filesystem,
+.B fstrim
+will stop at the filesystem size boundary. Default value extends to the end
+of the filesystem.
+.IP "\fB\-m, \-\-minimum\fP \fIminimum-free-extent\fP"
+Minimum contiguous free range to discard, in bytes. (This value is internally
+rounded up to a multiple of the filesystem block size). Free ranges smaller
+than this will be ignored. By increasing this value, the fstrim operation
+will complete more quickly for filesystems with badly fragmented freespace,
+although not all blocks will be discarded. Default value is zero, discard
+every free block.
+.IP "\fB\-v, \-\-verbose\fP"
+Verbose execution. When specified
+.B fstrim
+will output the number of bytes passed from the filesystem
+down the block stack to the device for potential discard. This number is a
+maximum discard amount from the storage device's perspective, because
+.I FITRIM
+ioctl called repeated will keep sending the same sectors for discard repeatedly.
+
+.B fstrim
+will report the same potential discard bytes each time, but only sectors which
+had been written to between the discards would actually be discarded by the
+storage device. Further, the kernel block layer reserves the right to adjust
+the discard ranges to fit raid stripe geometry, non-trim capable devices in a
+LVM setup, etc. These reductions would not be reflected in fstrim_range.len
+(the
+.B --length
+option).
+
+.SH AUTHOR
+.nf
+Lukas Czerner <lczerner@redhat.com>
+Karel Zak <kzak@redhat.com>
+.fi
+.SH SEE ALSO
+.BR mount (8)
+.SH AVAILABILITY
+The fstrim command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/sys-utils/fstrim.c b/sys-utils/fstrim.c
new file mode 100644
index 0000000..332601d
--- /dev/null
+++ b/sys-utils/fstrim.c
@@ -0,0 +1,153 @@
+/*
+ * fstrim.c -- discard the part (or whole) of mounted filesystem.
+ *
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ * Written by Lukas Czerner <lczerner@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 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, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * This program uses FITRIM ioctl to discard parts or the whole filesystem
+ * online (mounted). You can specify range (start and length) to be
+ * discarded, or simply discard whole filesystem.
+ */
+
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <getopt.h>
+
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <linux/fs.h>
+
+#include "nls.h"
+#include "strutils.h"
+#include "c.h"
+#include "closestream.h"
+
+#ifndef FITRIM
+struct fstrim_range {
+ uint64_t start;
+ uint64_t len;
+ uint64_t minlen;
+};
+#define FITRIM _IOWR('X', 121, struct fstrim_range)
+#endif
+
+static void __attribute__((__noreturn__)) usage(FILE *out)
+{
+ fputs(USAGE_HEADER, out);
+ fprintf(out,
+ _(" %s [options] <mount point>\n"), program_invocation_short_name);
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -o, --offset <num> offset in bytes to discard from\n"
+ " -l, --length <num> length of bytes to discard from the offset\n"
+ " -m, --minimum <num> minimum extent length to discard\n"
+ " -v, --verbose print number of discarded bytes\n"), out);
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ fprintf(out, USAGE_MAN_TAIL("fstrim(8)"));
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ char *path;
+ int c, fd, verbose = 0;
+ struct fstrim_range range;
+ struct stat sb;
+
+ static const struct option longopts[] = {
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'V' },
+ { "offset", 1, 0, 'o' },
+ { "length", 1, 0, 'l' },
+ { "minimum", 1, 0, 'm' },
+ { "verbose", 0, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ memset(&range, 0, sizeof(range));
+ range.len = ULLONG_MAX;
+
+ while ((c = getopt_long(argc, argv, "hVo:l:m:v", longopts, NULL)) != -1) {
+ switch(c) {
+ case 'h':
+ usage(stdout);
+ break;
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'l':
+ range.len = strtosize_or_err(optarg,
+ _("failed to parse length"));
+ break;
+ case 'o':
+ range.start = strtosize_or_err(optarg,
+ _("failed to parse offset"));
+ break;
+ case 'm':
+ range.minlen = strtosize_or_err(optarg,
+ _("failed to parse minimum extent length"));
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage(stderr);
+ break;
+ }
+ }
+
+ if (optind == argc)
+ errx(EXIT_FAILURE, _("no mountpoint specified."));
+
+ path = argv[optind++];
+
+ if (optind != argc) {
+ warnx(_("unexpected number of arguments"));
+ usage(stderr);
+ }
+
+ if (stat(path, &sb) == -1)
+ err(EXIT_FAILURE, _("stat failed %s"), path);
+ if (!S_ISDIR(sb.st_mode))
+ errx(EXIT_FAILURE, _("%s: not a directory"), path);
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), path);
+
+ if (ioctl(fd, FITRIM, &range))
+ err(EXIT_FAILURE, _("%s: FITRIM ioctl failed"), path);
+
+ if (verbose)
+ /* TRANSLATORS: The standard value here is a very large number. */
+ printf(_("%s: %" PRIu64 " bytes were trimmed\n"),
+ path, (uint64_t) range.len);
+ close(fd);
+ return EXIT_SUCCESS;
+}
diff --git a/sys-utils/hwclock-cmos.c b/sys-utils/hwclock-cmos.c
new file mode 100644
index 0000000..61a15a3
--- /dev/null
+++ b/sys-utils/hwclock-cmos.c
@@ -0,0 +1,677 @@
+/*
+ * i386 CMOS starts out with 14 bytes clock data alpha has something
+ * similar, but with details depending on the machine type.
+ *
+ * byte 0: seconds 0-59
+ * byte 2: minutes 0-59
+ * byte 4: hours 0-23 in 24hr mode,
+ * 1-12 in 12hr mode, with high bit unset/set
+ * if am/pm.
+ * byte 6: weekday 1-7, Sunday=1
+ * byte 7: day of the month 1-31
+ * byte 8: month 1-12
+ * byte 9: year 0-99
+ *
+ * Numbers are stored in BCD/binary if bit 2 of byte 11 is unset/set The
+ * clock is in 12hr/24hr mode if bit 1 of byte 11 is unset/set The clock is
+ * undefined (being updated) if bit 7 of byte 10 is set. The clock is frozen
+ * (to be updated) by setting bit 7 of byte 11 Bit 7 of byte 14 indicates
+ * whether the CMOS clock is reliable: it is 1 if RTC power has been good
+ * since this bit was last read; it is 0 when the battery is dead and system
+ * power has been off.
+ *
+ * Avoid setting the RTC clock within 2 seconds of the day rollover that
+ * starts a new month or enters daylight saving time.
+ *
+ * The century situation is messy:
+ *
+ * Usually byte 50 (0x32) gives the century (in BCD, so 19 or 20 hex), but
+ * IBM PS/2 has (part of) a checksum there and uses byte 55 (0x37).
+ * Sometimes byte 127 (0x7f) or Bank 1, byte 0x48 gives the century. The
+ * original RTC will not access any century byte; some modern versions will.
+ * If a modern RTC or BIOS increments the century byte it may go from 0x19
+ * to 0x20, but in some buggy cases 0x1a is produced.
+ */
+/*
+ * A struct tm has int fields
+ * tm_sec 0-59, 60 or 61 only for leap seconds
+ * tm_min 0-59
+ * tm_hour 0-23
+ * tm_mday 1-31
+ * tm_mon 0-11
+ * tm_year number of years since 1900
+ * tm_wday 0-6, 0=Sunday
+ * tm_yday 0-365
+ * tm_isdst >0: yes, 0: no, <0: unknown
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "nls.h"
+
+#if defined(__i386__)
+# ifdef HAVE_SYS_IO_H
+# include <sys/io.h>
+# elif defined(HAVE_ASM_IO_H)
+# include <asm/io.h> /* for inb, outb */
+# else
+/*
+ * Disable cmos access; we can no longer use asm/io.h, since the kernel does
+ * not export that header.
+ */
+#undef __i386__
+void outb(int a __attribute__ ((__unused__)),
+ int b __attribute__ ((__unused__)))
+{
+}
+
+int inb(int c __attribute__ ((__unused__)))
+{
+ return 0;
+}
+#endif /* __i386__ */
+
+#elif defined(__alpha__)
+/* <asm/io.h> fails to compile, probably because of u8 etc */
+extern unsigned int inb(unsigned long port);
+extern void outb(unsigned char b, unsigned long port);
+#else
+static void outb(int a __attribute__ ((__unused__)),
+ int b __attribute__ ((__unused__)))
+{
+}
+
+static int inb(int c __attribute__ ((__unused__)))
+{
+ return 0;
+}
+#endif /* __alpha__ */
+
+#include "hwclock.h"
+
+#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
+#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10)
+
+/*
+ * The epoch.
+ *
+ * Unix uses 1900 as epoch for a struct tm, and 1970 for a time_t. But what
+ * was written to CMOS?
+ *
+ * Digital DECstations use 1928 - this is on a mips or alpha Digital Unix
+ * uses 1952, e.g. on AXPpxi33. Windows NT uses 1980. The ARC console
+ * expects to boot Windows NT and uses 1980. (But a Ruffian uses 1900, just
+ * like SRM.) It is reported that ALPHA_PRE_V1_2_SRM_CONSOLE uses 1958.
+ */
+#define TM_EPOCH 1900
+int cmos_epoch = 1900;
+
+/*
+ * Martin Ostermann writes:
+ *
+ * The problem with the Jensen is twofold: First, it has the clock at a
+ * different address. Secondly, it has a distinction between "local" and
+ * normal bus addresses. The local ones pertain to the hardware integrated
+ * into the chipset, like serial/parallel ports and of course, the RTC.
+ * Those need to be addressed differently. This is handled fine in the
+ * kernel, and it's not a problem, since this usually gets totally optimized
+ * by the compile. But the i/o routines of (g)libc lack this support so far.
+ * The result of this is, that the old clock program worked only on the
+ * Jensen when USE_DEV_PORT was defined, but not with the normal inb/outb
+ * functions.
+ */
+int use_dev_port = 0; /* 1 for Jensen */
+int dev_port_fd;
+unsigned short clock_ctl_addr = 0x70; /* 0x170 for Jensen */
+unsigned short clock_data_addr = 0x71; /* 0x171 for Jensen */
+
+int century_byte = 0; /* 0: don't access a century byte
+ * 50 (0x32): usual PC value
+ * 55 (0x37): PS/2
+ */
+
+#ifdef __alpha__
+int funkyTOY = 0; /* 1 for PC164/LX164/SX164 type alpha */
+#endif
+
+#ifdef __alpha
+
+static int is_in_cpuinfo(char *fmt, char *str)
+{
+ FILE *cpuinfo;
+ char field[256];
+ char format[256];
+ int found = 0;
+
+ sprintf(format, "%s : %s", fmt, "%255s");
+
+ if ((cpuinfo = fopen("/proc/cpuinfo", "r")) != NULL) {
+ while (!feof(cpuinfo)) {
+ if (fscanf(cpuinfo, format, field) == 1) {
+ if (strncmp(field, str, strlen(str)) == 0)
+ found = 1;
+ break;
+ }
+ fgets(field, 256, cpuinfo);
+ }
+ fclose(cpuinfo);
+ }
+ return found;
+}
+
+/*
+ * Set cmos_epoch, either from user options, or by asking the kernel, or by
+ * looking at /proc/cpu_info
+ */
+void set_cmos_epoch(int ARCconsole, int SRM)
+{
+ unsigned long epoch;
+
+ /* Believe the user */
+ if (epoch_option != -1) {
+ cmos_epoch = epoch_option;
+ return;
+ }
+
+ if (ARCconsole)
+ cmos_epoch = 1980;
+
+ if (ARCconsole || SRM)
+ return;
+
+#ifdef __linux__
+ /*
+ * If we can ask the kernel, we don't need guessing from
+ * /proc/cpuinfo
+ */
+ if (get_epoch_rtc(&epoch, 1) == 0) {
+ cmos_epoch = epoch;
+ return;
+ }
+#endif
+
+ /*
+ * The kernel source today says: read the year.
+ *
+ * If it is in 0-19 then the epoch is 2000.
+ * If it is in 20-47 then the epoch is 1980.
+ * If it is in 48-69 then the epoch is 1952.
+ * If it is in 70-99 then the epoch is 1928.
+ *
+ * Otherwise the epoch is 1900.
+ * TODO: Clearly, this must be changed before 2019.
+ */
+ /*
+ * See whether we are dealing with SRM or MILO, as they have
+ * different "epoch" ideas.
+ */
+ if (is_in_cpuinfo("system serial number", "MILO")) {
+ ARCconsole = 1;
+ if (debug)
+ printf(_("booted from MILO\n"));
+ }
+
+ /*
+ * See whether we are dealing with a RUFFIAN aka Alpha PC-164 UX (or
+ * BX), as they have REALLY different TOY (TimeOfYear) format: BCD,
+ * and not an ARC-style epoch. BCD is detected dynamically, but we
+ * must NOT adjust like ARC.
+ */
+ if (ARCconsole && is_in_cpuinfo("system type", "Ruffian")) {
+ ARCconsole = 0;
+ if (debug)
+ printf(_("Ruffian BCD clock\n"));
+ }
+
+ if (ARCconsole)
+ cmos_epoch = 1980;
+}
+
+void set_cmos_access(int Jensen, int funky_toy)
+{
+
+ /*
+ * See whether we're dealing with a Jensen---it has a weird I/O
+ * system. DEC was just learning how to build Alpha PCs.
+ */
+ if (Jensen || is_in_cpuinfo("system type", "Jensen")) {
+ use_dev_port = 1;
+ clock_ctl_addr = 0x170;
+ clock_data_addr = 0x171;
+ if (debug)
+ printf(_("clockport adjusted to 0x%x\n"),
+ clock_ctl_addr);
+ }
+
+ /*
+ * See whether we are dealing with PC164/LX164/SX164, as they have a
+ * TOY that must be accessed differently to work correctly.
+ */
+ /* Nautilus stuff reported by Neoklis Kyriazis */
+ if (funky_toy ||
+ is_in_cpuinfo("system variation", "PC164") ||
+ is_in_cpuinfo("system variation", "LX164") ||
+ is_in_cpuinfo("system variation", "SX164") ||
+ is_in_cpuinfo("system type", "Nautilus")) {
+ funkyTOY = 1;
+ if (debug)
+ printf(_("funky TOY!\n"));
+ }
+}
+#endif /* __alpha */
+
+#if __alpha__
+/*
+ * The Alpha doesn't allow user-level code to disable interrupts (for good
+ * reasons). Instead, we ensure atomic operation by performing the operation
+ * and checking whether the high 32 bits of the cycle counter changed. If
+ * they did, a context switch must have occurred and we redo the operation.
+ * As long as the operation is reasonably short, it will complete
+ * atomically, eventually.
+ */
+static unsigned long
+atomic(const char *name, unsigned long (*op) (unsigned long), unsigned long arg)
+{
+ unsigned long ts1, ts2, n, v;
+
+ for (n = 0; n < 1000; ++n) {
+ asm volatile ("rpcc %0":"r=" (ts1));
+ v = (*op) (arg);
+ asm volatile ("rpcc %0":"r=" (ts2));
+
+ if ((ts1 ^ ts2) >> 32 == 0) {
+ return v;
+ }
+ }
+ errx(EXIT_FAILURE, _("atomic %s failed for 1000 iterations!"),
+ name);
+}
+#else
+
+/*
+ * Hmmh, this isn't very atomic. Maybe we should force an error instead?
+ *
+ * TODO: optimize the access to CMOS by mlockall(MCL_CURRENT) and SCHED_FIFO
+ */
+static unsigned long
+atomic(const char *name __attribute__ ((__unused__)),
+ unsigned long (*op) (unsigned long),
+ unsigned long arg)
+{
+ return (*op) (arg);
+}
+
+#endif
+
+static inline unsigned long cmos_read(unsigned long reg)
+{
+ if (use_dev_port) {
+ unsigned char v = reg | 0x80;
+ lseek(dev_port_fd, clock_ctl_addr, 0);
+ if (write(dev_port_fd, &v, 1) == -1 && debug)
+ printf(_
+ ("cmos_read(): write to control address %X failed: %m\n"),
+ clock_ctl_addr);
+ lseek(dev_port_fd, clock_data_addr, 0);
+ if (read(dev_port_fd, &v, 1) == -1 && debug)
+ printf(_
+ ("cmos_read(): read data address %X failed: %m\n"),
+ clock_data_addr);
+ return v;
+ } else {
+ /*
+ * We only want to read CMOS data, but unfortunately writing
+ * to bit 7 disables (1) or enables (0) NMI; since this bit
+ * is read-only we have to guess the old status. Various
+ * docs suggest that one should disable NMI while
+ * reading/writing CMOS data, and enable it again
+ * afterwards. This would yield the sequence
+ *
+ * outb (reg | 0x80, 0x70);
+ * val = inb(0x71);
+ * outb (0x0d, 0x70); // 0x0d: random read-only location
+ *
+ * Other docs state that "any write to 0x70 should be
+ * followed by an action to 0x71 or the RTC wil be left in
+ * an unknown state". Most docs say that it doesn't matter at
+ * all what one does.
+ */
+ /*
+ * bit 0x80: disable NMI while reading - should we? Let us
+ * follow the kernel and not disable. Called only with 0 <=
+ * reg < 128
+ */
+ outb(reg, clock_ctl_addr);
+ return inb(clock_data_addr);
+ }
+}
+
+static inline unsigned long cmos_write(unsigned long reg, unsigned long val)
+{
+ if (use_dev_port) {
+ unsigned char v = reg | 0x80;
+ lseek(dev_port_fd, clock_ctl_addr, 0);
+ if (write(dev_port_fd, &v, 1) == -1 && debug)
+ printf(_
+ ("cmos_write(): write to control address %X failed: %m\n"),
+ clock_ctl_addr);
+ v = (val & 0xff);
+ lseek(dev_port_fd, clock_data_addr, 0);
+ if (write(dev_port_fd, &v, 1) == -1 && debug)
+ printf(_
+ ("cmos_write(): write to data address %X failed: %m\n"),
+ clock_data_addr);
+ } else {
+ outb(reg, clock_ctl_addr);
+ outb(val, clock_data_addr);
+ }
+ return 0;
+}
+
+static unsigned long cmos_set_time(unsigned long arg)
+{
+ unsigned char save_control, save_freq_select, pmbit = 0;
+ struct tm tm = *(struct tm *)arg;
+ unsigned int century;
+
+/*
+ * CMOS byte 10 (clock status register A) has 3 bitfields:
+ * bit 7: 1 if data invalid, update in progress (read-only bit)
+ * (this is raised 224 us before the actual update starts)
+ * 6-4 select base frequency
+ * 010: 32768 Hz time base (default)
+ * 111: reset
+ * all other combinations are manufacturer-dependent
+ * (e.g.: DS1287: 010 = start oscillator, anything else = stop)
+ * 3-0 rate selection bits for interrupt
+ * 0000 none (may stop RTC)
+ * 0001, 0010 give same frequency as 1000, 1001
+ * 0011 122 microseconds (minimum, 8192 Hz)
+ * .... each increase by 1 halves the frequency, doubles the period
+ * 1111 500 milliseconds (maximum, 2 Hz)
+ * 0110 976.562 microseconds (default 1024 Hz)
+ */
+ save_control = cmos_read(11); /* tell the clock it's being set */
+ cmos_write(11, (save_control | 0x80));
+ save_freq_select = cmos_read(10); /* stop and reset prescaler */
+ cmos_write(10, (save_freq_select | 0x70));
+
+ tm.tm_year += TM_EPOCH;
+ century = tm.tm_year / 100;
+ tm.tm_year -= cmos_epoch;
+ tm.tm_year %= 100;
+ tm.tm_mon += 1;
+ tm.tm_wday += 1;
+
+ if (!(save_control & 0x02)) { /* 12hr mode; the default is 24hr mode */
+ if (tm.tm_hour == 0)
+ tm.tm_hour = 24;
+ if (tm.tm_hour > 12) {
+ tm.tm_hour -= 12;
+ pmbit = 0x80;
+ }
+ }
+
+ if (!(save_control & 0x04)) { /* BCD mode - the default */
+ BIN_TO_BCD(tm.tm_sec);
+ BIN_TO_BCD(tm.tm_min);
+ BIN_TO_BCD(tm.tm_hour);
+ BIN_TO_BCD(tm.tm_wday);
+ BIN_TO_BCD(tm.tm_mday);
+ BIN_TO_BCD(tm.tm_mon);
+ BIN_TO_BCD(tm.tm_year);
+ BIN_TO_BCD(century);
+ }
+
+ cmos_write(0, tm.tm_sec);
+ cmos_write(2, tm.tm_min);
+ cmos_write(4, tm.tm_hour | pmbit);
+ cmos_write(6, tm.tm_wday);
+ cmos_write(7, tm.tm_mday);
+ cmos_write(8, tm.tm_mon);
+ cmos_write(9, tm.tm_year);
+ if (century_byte)
+ cmos_write(century_byte, century);
+
+ /*
+ * The kernel sources, linux/arch/i386/kernel/time.c, have the
+ * following comment:
+ *
+ * The following flags have to be released exactly in this order,
+ * otherwise the DS12887 (popular MC146818A clone with integrated
+ * battery and quartz) will not reset the oscillator and will not
+ * update precisely 500 ms later. You won't find this mentioned in
+ * the Dallas Semiconductor data sheets, but who believes data
+ * sheets anyway ... -- Markus Kuhn
+ */
+ cmos_write(11, save_control);
+ cmos_write(10, save_freq_select);
+ return 0;
+}
+
+static int hclock_read(unsigned long reg)
+{
+ return atomic("clock read", cmos_read, (reg));
+}
+
+static void hclock_set_time(const struct tm *tm)
+{
+ atomic("set time", cmos_set_time, (unsigned long)(tm));
+}
+
+static inline int cmos_clock_busy(void)
+{
+ return
+#ifdef __alpha__
+ /* poll bit 4 (UF) of Control Register C */
+ funkyTOY ? (hclock_read(12) & 0x10) :
+#endif
+ /* poll bit 7 (UIP) of Control Register A */
+ (hclock_read(10) & 0x80);
+}
+
+static int synchronize_to_clock_tick_cmos(void)
+{
+ int i;
+
+ /*
+ * Wait for rise. Should be within a second, but in case something
+ * weird happens, we have a limit on this loop to reduce the impact
+ * of this failure.
+ */
+ for (i = 0; !cmos_clock_busy(); i++)
+ if (i >= 10000000)
+ return 1;
+
+ /* Wait for fall. Should be within 2.228 ms. */
+ for (i = 0; cmos_clock_busy(); i++)
+ if (i >= 1000000)
+ return 1;
+ return 0;
+}
+
+/*
+ * Read the hardware clock and return the current time via <tm> argument.
+ * Assume we have an ISA machine and read the clock directly with CPU I/O
+ * instructions.
+ *
+ * This function is not totally reliable. It takes a finite and
+ * unpredictable amount of time to execute the code below. During that time,
+ * the clock may change and we may even read an invalid value in the middle
+ * of an update. We do a few checks to minimize this possibility, but only
+ * the kernel can actually read the clock properly, since it can execute
+ * code in a short and predictable amount of time (by turning of
+ * interrupts).
+ *
+ * In practice, the chance of this function returning the wrong time is
+ * extremely remote.
+ */
+static int read_hardware_clock_cmos(struct tm *tm)
+{
+ bool got_time = FALSE;
+ unsigned char status, pmbit;
+
+ status = pmbit = 0; /* just for gcc */
+
+ while (!got_time) {
+ /*
+ * Bit 7 of Byte 10 of the Hardware Clock value is the
+ * Update In Progress (UIP) bit, which is on while and 244
+ * uS before the Hardware Clock updates itself. It updates
+ * the counters individually, so reading them during an
+ * update would produce garbage. The update takes 2mS, so we
+ * could be spinning here that long waiting for this bit to
+ * turn off.
+ *
+ * Furthermore, it is pathologically possible for us to be
+ * in this code so long that even if the UIP bit is not on
+ * at first, the clock has changed while we were running. We
+ * check for that too, and if it happens, we start over.
+ */
+ if (!cmos_clock_busy()) {
+ /* No clock update in progress, go ahead and read */
+ tm->tm_sec = hclock_read(0);
+ tm->tm_min = hclock_read(2);
+ tm->tm_hour = hclock_read(4);
+ tm->tm_wday = hclock_read(6);
+ tm->tm_mday = hclock_read(7);
+ tm->tm_mon = hclock_read(8);
+ tm->tm_year = hclock_read(9);
+ status = hclock_read(11);
+#if 0
+ if (century_byte)
+ century = hclock_read(century_byte);
+#endif
+ /*
+ * Unless the clock changed while we were reading,
+ * consider this a good clock read .
+ */
+ if (tm->tm_sec == hclock_read(0))
+ got_time = TRUE;
+ }
+ /*
+ * Yes, in theory we could have been running for 60 seconds
+ * and the above test wouldn't work!
+ */
+ }
+
+ if (!(status & 0x04)) { /* BCD mode - the default */
+ BCD_TO_BIN(tm->tm_sec);
+ BCD_TO_BIN(tm->tm_min);
+ pmbit = (tm->tm_hour & 0x80);
+ tm->tm_hour &= 0x7f;
+ BCD_TO_BIN(tm->tm_hour);
+ BCD_TO_BIN(tm->tm_wday);
+ BCD_TO_BIN(tm->tm_mday);
+ BCD_TO_BIN(tm->tm_mon);
+ BCD_TO_BIN(tm->tm_year);
+#if 0
+ BCD_TO_BIN(century);
+#endif
+ }
+
+ /*
+ * We don't use the century byte of the Hardware Clock since we
+ * don't know its address (usually 50 or 55). Here, we follow the
+ * advice of the X/Open Base Working Group: "if century is not
+ * specified, then values in the range [69-99] refer to years in the
+ * twentieth century (1969 to 1999 inclusive), and values in the
+ * range [00-68] refer to years in the twenty-first century (2000 to
+ * 2068 inclusive)."
+ */
+ tm->tm_wday -= 1;
+ tm->tm_mon -= 1;
+ tm->tm_year += (cmos_epoch - TM_EPOCH);
+ if (tm->tm_year < 69)
+ tm->tm_year += 100;
+ if (pmbit) {
+ tm->tm_hour += 12;
+ if (tm->tm_hour == 24)
+ tm->tm_hour = 0;
+ }
+
+ tm->tm_isdst = -1; /* don't know whether it's daylight */
+ return 0;
+}
+
+static int set_hardware_clock_cmos(const struct tm *new_broken_time)
+{
+
+ hclock_set_time(new_broken_time);
+ return 0;
+}
+
+#if defined(__i386__) || defined(__alpha__)
+# if defined(HAVE_IOPL)
+static int i386_iopl(const int level)
+{
+ extern int iopl(const int lvl);
+ return iopl(level);
+}
+# else
+static int i386_iopl(const int level __attribute__ ((__unused__)))
+{
+ extern int ioperm(unsigned long from, unsigned long num, int turn_on);
+ return ioperm(clock_ctl_addr, 2, 1);
+}
+# endif
+#else
+static int i386_iopl(const int level __attribute__ ((__unused__)))
+{
+ return -2;
+}
+#endif
+
+static int get_permissions_cmos(void)
+{
+ int rc;
+
+ if (use_dev_port) {
+ if ((dev_port_fd = open("/dev/port", O_RDWR)) < 0) {
+ warn(_("cannot open %s"), "/dev/port");
+ rc = 1;
+ } else
+ rc = 0;
+ } else {
+ rc = i386_iopl(3);
+ if (rc == -2) {
+ warnx(_("I failed to get permission because I didn't try."));
+ } else if (rc != 0) {
+ rc = errno;
+ warn(_("unable to get I/O port access: "
+ "the iopl(3) call failed."));
+ if (rc == EPERM && geteuid())
+ warnx(_("Probably you need root privileges.\n"));
+ }
+ }
+ return rc ? 1 : 0;
+}
+
+static struct clock_ops cmos = {
+ "direct I/O instructions to ISA clock",
+ get_permissions_cmos,
+ read_hardware_clock_cmos,
+ set_hardware_clock_cmos,
+ synchronize_to_clock_tick_cmos,
+};
+
+/*
+ * return &cmos if cmos clock present, NULL otherwise choose this
+ * construction to avoid gcc messages about unused variables
+ */
+struct clock_ops *probe_for_cmos_clock(void)
+{
+ int have_cmos =
+#if defined(__i386__) || defined(__alpha__)
+ TRUE;
+#else
+ FALSE;
+#endif
+ return have_cmos ? &cmos : NULL;
+}
diff --git a/sys-utils/hwclock-kd.c b/sys-utils/hwclock-kd.c
new file mode 100644
index 0000000..bfe50dc
--- /dev/null
+++ b/sys-utils/hwclock-kd.c
@@ -0,0 +1,182 @@
+/*
+ * kd.c - KDGHWCLK stuff, possibly m68k only, likely to be deprecated
+ */
+
+# include "hwclock.h"
+
+#ifdef __m68k__
+
+# include <fcntl.h>
+# include <sysexits.h>
+# include <sys/ioctl.h>
+# include <unistd.h>
+
+# include "nls.h"
+# include "usleep.h"
+
+/* Get defines for KDGHWCLK and KDSHWCLK (m68k) */
+# include <linux/kd.h>
+
+/* Even on m68k, if KDGHWCLK (antique) is not defined, don't build this */
+
+#endif
+
+#if !defined(__m68k__) || !defined(KDGHWCLK)
+
+#include <stddef.h>
+struct clock_ops *probe_for_kd_clock(void)
+{
+ return NULL;
+}
+
+#else /* __m68k__ && KDGHWCLK */
+
+/* Opened by probe_for_kd_clock(), and never closed. */
+static int con_fd = -1;
+static char *con_fd_filename; /* usually "/dev/tty1" */
+
+/*
+ * Wait for the top of a clock tick by calling KDGHWCLK in a busy loop until
+ * we see it.
+ */
+static int synchronize_to_clock_tick_kd(void)
+{
+ /* The time when we were called (and started waiting) */
+ struct hwclk_time start_time, nowtime;
+ struct timeval begin, now;
+
+ if (debug)
+ printf(_("Waiting in loop for time from KDGHWCLK to change\n"));
+
+ if (ioctl(con_fd, KDGHWCLK, &start_time) == -1) {
+ warn(_("KDGHWCLK ioctl to read time failed"));
+ return 3;
+ }
+
+ /*
+ * Wait for change. Should be within a second, but in case something
+ * weird happens, we have a time limit (1.5s) on this loop to reduce
+ * the impact of this failure.
+ */
+ gettimeofday(&begin, NULL);
+ do {
+ /*
+ * Added by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ *
+ * "The culprit is the fast loop with KDGHWCLK ioctls. It
+ * seems the kernel gets confused by those on Amigas with
+ * A2000 RTCs and simply hangs after some time. Inserting a
+ * sleep helps."
+ */
+ usleep(1);
+
+ if (ioctl(con_fd, KDGHWCLK, &nowtime) == -1) {
+ warn(_("KDGHWCLK ioctl to read time failed in loop"));
+ return 3;
+ }
+ if (start_time.tm_sec != nowtime.tm_sec)
+ break;
+ gettimeofday(&now, NULL);
+ if (time_diff(now, begin) > 1.5) {
+ warnx(_("Timed out waiting for time change."));
+ return 2;
+ }
+ } while (1);
+
+ return 0;
+}
+
+/*
+ * Read the hardware clock and return the current time via <tm> argument.
+ * Use ioctls to /dev/tty1 on what we assume is an m68k machine.
+ *
+ * Note that we don't use /dev/console here. That might be a serial console.
+ */
+static int read_hardware_clock_kd(struct tm *tm)
+{
+ struct hwclk_time t;
+
+ if (ioctl(con_fd, KDGHWCLK, &t) == -1) {
+ warn(_("ioctl() failed to read time from %s"),
+ con_fd_filename);
+ hwclock_exit(EX_IOERR);
+ }
+
+ tm->tm_sec = t.sec;
+ tm->tm_min = t.min;
+ tm->tm_hour = t.hour;
+ tm->tm_mday = t.day;
+ tm->tm_mon = t.mon;
+ tm->tm_year = t.year;
+ tm->tm_wday = t.wday;
+ tm->tm_isdst = -1; /* Don't know if it's Daylight Savings Time */
+
+ return 0;
+}
+
+/*
+ * Set the Hardware Clock to the time <new_broken_time>. Use ioctls to
+ * /dev/tty1 on what we assume is an m68k machine.
+ *
+ * Note that we don't use /dev/console here. That might be a serial console.
+ */
+static int set_hardware_clock_kd(const struct tm *new_broken_time)
+{
+ struct hwclk_time t;
+
+ t.sec = new_broken_time->tm_sec;
+ t.min = new_broken_time->tm_min;
+ t.hour = new_broken_time->tm_hour;
+ t.day = new_broken_time->tm_mday;
+ t.mon = new_broken_time->tm_mon;
+ t.year = new_broken_time->tm_year;
+ t.wday = new_broken_time->tm_wday;
+
+ if (ioctl(con_fd, KDSHWCLK, &t) == -1) {
+ warn(_("ioctl KDSHWCLK failed"));
+ hwclock_exit(EX_IOERR);
+ }
+ return 0;
+}
+
+static int get_permissions_kd(void)
+{
+ return 0;
+}
+
+static struct clock_ops kd = {
+ "KDGHWCLK interface to m68k clock",
+ get_permissions_kd,
+ read_hardware_clock_kd,
+ set_hardware_clock_kd,
+ synchronize_to_clock_tick_kd,
+};
+
+/* return &kd if KDGHWCLK works, NULL otherwise */
+struct clock_ops *probe_for_kd_clock()
+{
+ struct clock_ops *ret = NULL;
+ struct hwclk_time t;
+
+ if (con_fd < 0) { /* first time here */
+ con_fd_filename = "/dev/tty1";
+ con_fd = open(con_fd_filename, O_RDONLY);
+ }
+ if (con_fd < 0) {
+ /* perhaps they are using devfs? */
+ con_fd_filename = "/dev/vc/1";
+ con_fd = open(con_fd_filename, O_RDONLY);
+ }
+ if (con_fd < 0) {
+ /* probably KDGHWCLK exists on m68k only */
+ warn(_("Can't open /dev/tty1 or /dev/vc/1"));
+ } else {
+ if (ioctl(con_fd, KDGHWCLK, &t) == -1) {
+ if (errno != EINVAL)
+ warn(_("KDGHWCLK ioctl failed"));
+ } else
+ ret = &kd;
+ }
+ return ret;
+}
+#endif /* __m68k__ && KDGHWCLK */
diff --git a/sys-utils/hwclock-rtc.c b/sys-utils/hwclock-rtc.c
new file mode 100644
index 0000000..3380941
--- /dev/null
+++ b/sys-utils/hwclock-rtc.c
@@ -0,0 +1,509 @@
+/*
+ * rtc.c - Use /dev/rtc for clock access
+ */
+#include <asm/ioctl.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "nls.h"
+
+#include "hwclock.h"
+
+/*
+ * Get defines for rtc stuff.
+ *
+ * Getting the rtc defines is nontrivial. The obvious way is by including
+ * <linux/mc146818rtc.h> but that again includes <asm/io.h> which again
+ * includes ... and on sparc and alpha this gives compilation errors for
+ * many kernel versions. So, we give the defines ourselves here. Moreover,
+ * some Sparc person decided to be incompatible, and used a struct rtc_time
+ * different from that used in mc146818rtc.h.
+ */
+
+/*
+ * On Sparcs, there is a <asm/rtc.h> that defines different ioctls (that are
+ * required on my machine). However, this include file does not exist on
+ * other architectures.
+ */
+/* One might do:
+#ifdef __sparc__
+# include <asm/rtc.h>
+#endif
+ */
+/* The following is roughly equivalent */
+struct sparc_rtc_time
+{
+ int sec; /* Seconds 0-59 */
+ int min; /* Minutes 0-59 */
+ int hour; /* Hour 0-23 */
+ int dow; /* Day of the week 1-7 */
+ int dom; /* Day of the month 1-31 */
+ int month; /* Month of year 1-12 */
+ int year; /* Year 0-99 */
+};
+
+#define RTCGET _IOR('p', 20, struct sparc_rtc_time)
+#define RTCSET _IOW('p', 21, struct sparc_rtc_time)
+
+/* non-sparc stuff */
+#if 0
+# include <linux/version.h>
+/*
+ * Check if the /dev/rtc interface is available in this version of the
+ * system headers. 131072 is linux 2.0.0.
+ */
+# if LINUX_VERSION_CODE >= 131072
+# include <linux/mc146818rtc.h>
+# endif
+#endif
+
+/*
+ * struct rtc_time is present since 1.3.99.
+ * Earlier (since 1.3.89), a struct tm was used.
+ */
+struct linux_rtc_time {
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_mday;
+ int tm_mon;
+ int tm_year;
+ int tm_wday;
+ int tm_yday;
+ int tm_isdst;
+};
+
+/* RTC_RD_TIME etc have this definition since 1.99.9 (pre2.0-9) */
+#ifndef RTC_RD_TIME
+# define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time)
+# define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time)
+# define RTC_UIE_ON _IO('p', 0x03) /* Update int. enable on */
+# define RTC_UIE_OFF _IO('p', 0x04) /* Update int. enable off */
+#endif
+
+/* RTC_EPOCH_READ and RTC_EPOCH_SET are present since 2.0.34 and 2.1.89 */
+#ifndef RTC_EPOCH_READ
+# define RTC_EPOCH_READ _IOR('p', 0x0d, unsigned long) /* Read epoch */
+# define RTC_EPOCH_SET _IOW('p', 0x0e, unsigned long) /* Set epoch */
+#endif
+
+/*
+ * /dev/rtc is conventionally chardev 10/135
+ * ia64 uses /dev/efirtc, chardev 10/136
+ * devfs (obsolete) used /dev/misc/... for miscdev
+ * new RTC framework + udev uses dynamic major and /dev/rtc0.../dev/rtcN
+ * ... so we need an overridable default
+ */
+
+/* default or user defined dev (by hwclock --rtc=<path>) */
+char *rtc_dev_name;
+
+static int rtc_dev_fd = -1;
+
+static void close_rtc(void)
+{
+ if (rtc_dev_fd != -1)
+ close(rtc_dev_fd);
+ rtc_dev_fd = -1;
+}
+
+static int open_rtc(void)
+{
+ char *fls[] = {
+#ifdef __ia64__
+ "/dev/efirtc",
+ "/dev/misc/efirtc",
+#endif
+ "/dev/rtc",
+ "/dev/rtc0",
+ "/dev/misc/rtc",
+ NULL
+ };
+ char **p;
+
+ if (rtc_dev_fd != -1)
+ return rtc_dev_fd;
+
+ /* --rtc option has been given */
+ if (rtc_dev_name)
+ rtc_dev_fd = open(rtc_dev_name, O_RDONLY);
+ else {
+ for (p = fls; *p; ++p) {
+ rtc_dev_fd = open(*p, O_RDONLY);
+
+ if (rtc_dev_fd < 0
+ && (errno == ENOENT || errno == ENODEV))
+ continue;
+ rtc_dev_name = *p;
+ break;
+ }
+ if (rtc_dev_fd < 0)
+ rtc_dev_name = *fls; /* default for error messages */
+ }
+
+ if (rtc_dev_fd != 1)
+ atexit(close_rtc);
+ return rtc_dev_fd;
+}
+
+static int open_rtc_or_exit(void)
+{
+ int rtc_fd = open_rtc();
+
+ if (rtc_fd < 0) {
+ warn(_("cannot open %s"), rtc_dev_name);
+ hwclock_exit(EX_OSFILE);
+ }
+ return rtc_fd;
+}
+
+static int do_rtc_read_ioctl(int rtc_fd, struct tm *tm)
+{
+ int rc = -1;
+ char *ioctlname;
+
+#ifdef __sparc__
+ /* some but not all sparcs use a different ioctl and struct */
+ struct sparc_rtc_time stm;
+
+ ioctlname = "RTCGET";
+ rc = ioctl(rtc_fd, RTCGET, &stm);
+ if (rc == 0) {
+ tm->tm_sec = stm.sec;
+ tm->tm_min = stm.min;
+ tm->tm_hour = stm.hour;
+ tm->tm_mday = stm.dom;
+ tm->tm_mon = stm.month - 1;
+ tm->tm_year = stm.year - 1900;
+ tm->tm_wday = stm.dow - 1;
+ tm->tm_yday = -1; /* day in the year */
+ }
+#endif
+ if (rc == -1) { /* no sparc, or RTCGET failed */
+ ioctlname = "RTC_RD_TIME";
+ rc = ioctl(rtc_fd, RTC_RD_TIME, tm);
+ }
+ if (rc == -1) {
+ warn(_("ioctl(%s) to %s to read the time failed"),
+ ioctlname, rtc_dev_name);
+ return -1;
+ }
+
+ tm->tm_isdst = -1; /* don't know whether it's dst */
+ return 0;
+}
+
+/*
+ * Wait for the top of a clock tick by reading /dev/rtc in a busy loop until
+ * we see it.
+ */
+static int busywait_for_rtc_clock_tick(const int rtc_fd)
+{
+ struct tm start_time;
+ /* The time when we were called (and started waiting) */
+ struct tm nowtime;
+ int rc;
+ struct timeval begin, now;
+
+ if (debug)
+ printf(_("Waiting in loop for time from %s to change\n"),
+ rtc_dev_name);
+
+ rc = do_rtc_read_ioctl(rtc_fd, &start_time);
+ if (rc)
+ return 1;
+
+ /*
+ * Wait for change. Should be within a second, but in case
+ * something weird happens, we have a time limit (1.5s) on this loop
+ * to reduce the impact of this failure.
+ */
+ gettimeofday(&begin, NULL);
+ do {
+ rc = do_rtc_read_ioctl(rtc_fd, &nowtime);
+ if (rc || start_time.tm_sec != nowtime.tm_sec)
+ break;
+ gettimeofday(&now, NULL);
+ if (time_diff(now, begin) > 1.5) {
+ warnx(_("Timed out waiting for time change."));
+ return 2;
+ }
+ } while (1);
+
+ if (rc)
+ return 3;
+ return 0;
+}
+
+/*
+ * Same as synchronize_to_clock_tick(), but just for /dev/rtc.
+ */
+static int synchronize_to_clock_tick_rtc(void)
+{
+ int rtc_fd; /* File descriptor of /dev/rtc */
+ int ret;
+
+ rtc_fd = open_rtc();
+ if (rtc_fd == -1) {
+ warn(_("cannot open %s"), rtc_dev_name);
+ ret = 1;
+ } else {
+ int rc; /* Return code from ioctl */
+ /* Turn on update interrupts (one per second) */
+#if defined(__alpha__) || defined(__sparc__)
+ /*
+ * Not all alpha kernels reject RTC_UIE_ON, but probably
+ * they should.
+ */
+ rc = -1;
+ errno = EINVAL;
+#else
+ rc = ioctl(rtc_fd, RTC_UIE_ON, 0);
+#endif
+ if (rc == -1 && (errno == ENOTTY || errno == EINVAL)) {
+ /*
+ * This rtc device doesn't have interrupt functions.
+ * This is typical on an Alpha, where the Hardware
+ * Clock interrupts are used by the kernel for the
+ * system clock, so aren't at the user's disposal.
+ */
+ if (debug)
+ printf(_
+ ("%s does not have interrupt functions. "),
+ rtc_dev_name);
+ ret = busywait_for_rtc_clock_tick(rtc_fd);
+ } else if (rc == 0) {
+#ifdef Wait_until_update_interrupt
+ unsigned long dummy;
+
+ /* this blocks until the next update interrupt */
+ rc = read(rtc_fd, &dummy, sizeof(dummy));
+ ret = 1;
+ if (rc == -1)
+ warn(_
+ ("read() to %s to wait for clock tick failed"),
+ rtc_dev_name);
+ else
+ ret = 0;
+#else
+ /*
+ * Just reading rtc_fd fails on broken hardware: no
+ * update interrupt comes and a bootscript with a
+ * hwclock call hangs
+ */
+ fd_set rfds;
+ struct timeval tv;
+
+ /*
+ * Wait up to five seconds for the next update
+ * interrupt
+ */
+ FD_ZERO(&rfds);
+ FD_SET(rtc_fd, &rfds);
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ rc = select(rtc_fd + 1, &rfds, NULL, NULL, &tv);
+ ret = 1;
+ if (rc == -1)
+ warn(_
+ ("select() to %s to wait for clock tick failed"),
+ rtc_dev_name);
+ else if (rc == 0)
+ warn(_
+ ("select() to %s to wait for clock tick timed out"),
+ rtc_dev_name);
+ else
+ ret = 0;
+#endif
+
+ /* Turn off update interrupts */
+ rc = ioctl(rtc_fd, RTC_UIE_OFF, 0);
+ if (rc == -1)
+ warn(_
+ ("ioctl() to %s to turn off update interrupts failed"),
+ rtc_dev_name);
+ } else {
+ warn(_
+ ("ioctl() to %s to turn on update interrupts "
+ "failed unexpectedly"), rtc_dev_name);
+ ret = 1;
+ }
+ }
+ return ret;
+}
+
+static int read_hardware_clock_rtc(struct tm *tm)
+{
+ int rtc_fd, rc;
+
+ rtc_fd = open_rtc_or_exit();
+
+ /* Read the RTC time/date, return answer via tm */
+ rc = do_rtc_read_ioctl(rtc_fd, tm);
+
+ return rc;
+}
+
+/*
+ * Set the Hardware Clock to the broken down time <new_broken_time>. Use
+ * ioctls to "rtc" device /dev/rtc.
+ */
+static int set_hardware_clock_rtc(const struct tm *new_broken_time)
+{
+ int rc = -1;
+ int rtc_fd;
+ char *ioctlname;
+
+ rtc_fd = open_rtc_or_exit();
+
+#ifdef __sparc__
+ {
+ struct sparc_rtc_time stm;
+
+ stm.sec = new_broken_time->tm_sec;
+ stm.min = new_broken_time->tm_min;
+ stm.hour = new_broken_time->tm_hour;
+ stm.dom = new_broken_time->tm_mday;
+ stm.month = new_broken_time->tm_mon + 1;
+ stm.year = new_broken_time->tm_year + 1900;
+ stm.dow = new_broken_time->tm_wday + 1;
+
+ ioctlname = "RTCSET";
+ rc = ioctl(rtc_fd, RTCSET, &stm);
+ }
+#endif
+ if (rc == -1) { /* no sparc, or RTCSET failed */
+ ioctlname = "RTC_SET_TIME";
+ rc = ioctl(rtc_fd, RTC_SET_TIME, new_broken_time);
+ }
+
+ if (rc == -1) {
+ warn(_("ioctl(%s) to %s to set the time failed."),
+ ioctlname, rtc_dev_name);
+ hwclock_exit(EX_IOERR);
+ }
+
+ if (debug)
+ printf(_("ioctl(%s) was successful.\n"), ioctlname);
+
+ return 0;
+}
+
+static int get_permissions_rtc(void)
+{
+ return 0;
+}
+
+static struct clock_ops rtc = {
+ "/dev interface to clock",
+ get_permissions_rtc,
+ read_hardware_clock_rtc,
+ set_hardware_clock_rtc,
+ synchronize_to_clock_tick_rtc,
+};
+
+/* return &rtc if /dev/rtc can be opened, NULL otherwise */
+struct clock_ops *probe_for_rtc_clock(void)
+{
+ int rtc_fd = open_rtc();
+ if (rtc_fd >= 0)
+ return &rtc;
+ if (debug)
+ warn(_("cannot open %s"), rtc_dev_name);
+ return NULL;
+}
+
+/*
+ * Get the Hardware Clock epoch setting from the kernel.
+ */
+int get_epoch_rtc(unsigned long *epoch_p, int silent)
+{
+ int rtc_fd;
+
+ rtc_fd = open_rtc();
+ if (rtc_fd < 0) {
+ if (!silent) {
+ if (errno == ENOENT)
+ warnx(_
+ ("To manipulate the epoch value in the kernel, we must "
+ "access the Linux 'rtc' device driver via the device special "
+ "file %s. This file does not exist on this system."),
+ rtc_dev_name);
+ else
+ warn(_("cannot open %s"), rtc_dev_name);
+ }
+ return 1;
+ }
+
+ if (ioctl(rtc_fd, RTC_EPOCH_READ, epoch_p) == -1) {
+ if (!silent)
+ warn(_("ioctl(RTC_EPOCH_READ) to %s failed"),
+ rtc_dev_name);
+ return 1;
+ }
+
+ if (debug)
+ printf(_("we have read epoch %ld from %s "
+ "with RTC_EPOCH_READ ioctl.\n"), *epoch_p,
+ rtc_dev_name);
+
+ return 0;
+}
+
+/*
+ * Set the Hardware Clock epoch in the kernel.
+ */
+int set_epoch_rtc(unsigned long epoch)
+{
+ int rtc_fd;
+
+ if (epoch < 1900) {
+ /* kernel would not accept this epoch value
+ *
+ * Bad habit, deciding not to do what the user asks just
+ * because one believes that the kernel might not like it.
+ */
+ warnx(_("The epoch value may not be less than 1900. "
+ "You requested %ld"), epoch);
+ return 1;
+ }
+
+ rtc_fd = open_rtc();
+ if (rtc_fd < 0) {
+ if (errno == ENOENT)
+ warnx(_
+ ("To manipulate the epoch value in the kernel, we must "
+ "access the Linux 'rtc' device driver via the device special "
+ "file %s. This file does not exist on this system."),
+ rtc_dev_name);
+ else
+ warn(_("cannot open %s"), rtc_dev_name);
+ return 1;
+ }
+
+ if (debug)
+ printf(_("setting epoch to %ld "
+ "with RTC_EPOCH_SET ioctl to %s.\n"), epoch,
+ rtc_dev_name);
+
+ if (ioctl(rtc_fd, RTC_EPOCH_SET, epoch) == -1) {
+ if (errno == EINVAL)
+ warnx(_("The kernel device driver for %s "
+ "does not have the RTC_EPOCH_SET ioctl."),
+ rtc_dev_name);
+ else
+ warn(_("ioctl(RTC_EPOCH_SET) to %s failed"),
+ rtc_dev_name);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/sys-utils/hwclock.8 b/sys-utils/hwclock.8
new file mode 100644
index 0000000..07d9fc0
--- /dev/null
+++ b/sys-utils/hwclock.8
@@ -0,0 +1,661 @@
+.TH HWCLOCK 8 "August 2011" "util-linux" "System Administration"
+.SH NAME
+hwclock \- query or set the hardware clock (RTC)
+.SH SYNOPSIS
+.B hwclock
+.RI [ function ]
+.RI [ option ...]
+
+.SH DESCRIPTION
+.B hwclock
+is a tool for accessing the Hardware Clock. You can display the
+current time, set the Hardware Clock to a specified time, set the
+Hardware Clock from the System Time, or set the System Time from the
+Hardware Clock.
+.PP
+You can also run
+.B hwclock
+periodically to add or subtract time from the Hardware Clock to
+compensate for systematic drift (where the clock consistently loses or
+gains time at a certain rate when left to run).
+
+.SH FUNCTIONS
+You need exactly one of the following options to tell
+.B hwclock
+what function to perform:
+.PP
+.TP
+.BR \-r , \ \-\-show
+Read the Hardware Clock and print the time on standard output.
+The time shown is always in local time, even if you keep your Hardware Clock
+in Coordinated Universal Time. See the
+.B \-\-utc
+option.
+Showing the Hardware Clock time is the default when no function is specified.
+
+.TP
+.B \-\-set
+Set the Hardware Clock to the time given by the
+.B \-\-date
+option.
+.TP
+.BR \-s , \ \-\-hctosys
+Set the System Time from the Hardware Clock.
+
+Also set the kernel's timezone value to the local timezone
+as indicated by the TZ environment variable and/or
+.IR /usr/share/zoneinfo ,
+as
+.BR tzset (3)
+would interpret them.
+The obsolete tz_dsttime field of the kernel's timezone value is set
+to DST_NONE. (For details on what this field used to mean, see
+.BR settimeofday (2).)
+
+This is a good option to use in one of the system startup scripts.
+.TP
+.BR \-w , \ \-\-systohc
+Set the Hardware Clock to the current System Time.
+.TP
+.B \-\-systz
+Reset the System Time based on the current timezone.
+
+Also set the kernel's timezone value to the local timezone
+as indicated by the TZ environment variable and/or
+.IR /usr/share/zoneinfo ,
+as
+.BR tzset (3)
+would interpret them.
+The obsolete tz_dsttime field of the kernel's timezone value is set
+to DST_NONE. (For details on what this field used to mean, see
+.BR settimeofday (2).)
+
+This is an alternate option to
+.B \-\-hctosys
+that does not read the hardware clock, and may be used in system startup
+scripts for recent 2.6 kernels where you know the System Time contains
+the Hardware Clock time.
+.TP
+.B \-\-adjust
+Add or subtract time from the Hardware Clock to account for systematic
+drift since the last time the clock was set or adjusted. See discussion
+below.
+.TP
+.B \-\-getepoch
+Print the kernel's Hardware Clock epoch value to standard output.
+This is the number of years into AD to which a zero year value in the
+Hardware Clock refers. For example, if you are using the convention
+that the year counter in your Hardware Clock contains the number of
+full years since 1952, then the kernel's Hardware Clock epoch value
+must be 1952.
+
+This epoch value is used whenever
+.B hwclock
+reads or sets the Hardware Clock.
+.TP
+.B \-\-setepoch
+Set the kernel's Hardware Clock epoch value to the value specified by the
+.B \-\-epoch
+option. See the
+.B \-\-getepoch
+option for details.
+
+.TP
+.BI \-\-predict
+Predict what the RTC will read at time given by the
+.B \-\-date
+option based on the adjtime file. This is useful for example if you
+need to set an RTC wakeup time to distant future and want to account
+for the RTC drift.
+.TP
+.BR \-h , \ \-\-help
+Display a help text and exit.
+.TP
+.BR \-V , \ \-\-version
+Display the version of
+.B hwclock
+and exit.
+
+.SH OPTIONS
+.PP
+The first two options apply to just a few specific functions,
+the others apply to most functions.
+.TP
+.BI \-\-date= date_string
+You need this option if you specify the
+.B \-\-set
+or
+.B \-\-predict
+functions, otherwise it is ignored.
+It specifies the time to which to set the Hardware Clock, or the
+time for which to predict the Hardware Clock reading.
+The value of this option is an argument to the
+.BR date (1)
+program.
+For example:
+.sp
+.B " hwclock" --set --date="2011-08-14 16:45:05"
+.sp
+The argument must be in local time, even if you keep your Hardware Clock in
+Coordinated Universal time. See the
+.B \-\-utc
+option.
+
+.TP
+.BI \-\-epoch= year
+Specifies the year which is the beginning of the Hardware Clock's
+epoch, that is the number of years into AD to which a zero value in the
+Hardware Clock's year counter refers. It is used together with
+the \fB\-\-setepoch\fR option to set the kernel's idea of the epoch of the
+Hardware Clock, or otherwise to specify the epoch for use with
+direct ISA access.
+
+For example, on a Digital Unix machine:
+.sp
+.B " hwclock" --setepoch --epoch=1952
+
+.TP
+.BR \-u , \ \-\-utc
+.TP
+.B \-\-localtime
+Indicates that the Hardware Clock is kept in Coordinated Universal
+Time or local time, respectively. It is your choice whether to keep
+your clock in UTC or local time, but nothing in the clock tells which
+you've chosen. So this option is how you give that information to
+.BR hwclock .
+
+If you specify the wrong one of these options (or specify neither and
+take a wrong default), both setting and querying of the Hardware Clock
+will be messed up.
+
+If you specify neither
+.B \-\-utc
+nor
+.BR \-\-localtime ,
+the default is whichever was specified the last time
+.B hwclock
+was used to set the clock (i.e.
+.B hwclock
+was successfully run with the
+.BR \-\-set ,
+.BR \-\-systohc ,
+or
+.B \-\-adjust
+options), as recorded in the adjtime file. If the adjtime file doesn't
+exist, the default is UTC time.
+
+.TP
+.B \-\-noadjfile
+Disables the facilities provided by
+.IR /etc/adjtime .
+.B hwclock
+will not read nor write to that file with this option. Either
+.B \-\-utc
+or
+.B \-\-localtime
+must be specified when using this option.
+
+.TP
+.BI \-\-adjfile= filename
+Overrides the default /etc/adjtime.
+
+.TP
+.BR \-f , \ \-\-rtc=\fIfilename\fB
+Overrides the default /dev file name, which is
+.IR /dev/rtc
+on many platforms but may be
+.IR /dev/rtc0 ,
+.IR /dev/rtc1 ,
+and so on.
+
+.TP
+.B \-\-directisa
+This option is meaningful only on an ISA machine or an Alpha (which implements
+enough of ISA to be, roughly speaking, an ISA machine for
+.BR hwclock 's
+purposes). For other machines, it has no effect. This option tells
+.B hwclock
+to use explicit I/O instructions to access the Hardware Clock.
+Without this option,
+.B hwclock
+will try to use the /dev/rtc device (which it assumes to be driven by the
+RTC device driver). If it is unable to open the device (for reading), it will
+use the explicit I/O instructions anyway.
+
+.TP
+.B \-\-badyear
+Indicates that the Hardware Clock is incapable of storing years outside
+the range 1994-1999. There is a problem in some BIOSes (almost all
+Award BIOSes made between 4/26/94 and 5/31/95) wherein they are unable
+to deal with years after 1999. If one attempts to set the year-of-century
+value to something less than 94 (or 95 in some cases), the value that
+actually gets set is 94 (or 95). Thus, if you have one of these machines,
+.B hwclock
+cannot set the year after 1999 and cannot use the value of the clock as
+the true time in the normal way.
+
+To compensate for this (without your getting a BIOS update, which would
+definitely be preferable), always use
+.B \-\-badyear
+if you have one of these machines. When
+.B hwclock
+knows it's working with a brain-damaged clock, it ignores the year part of
+the Hardware Clock value and instead tries to guess the year based on the
+last calibrated date in the adjtime file, by assuming that that date is
+within the past year. For this to work, you had better do a
+.B hwclock \-\-set
+or
+.B hwclock \-\-systohc
+at least once a year!
+
+Though
+.B hwclock
+ignores the year value when it reads the Hardware Clock, it sets the
+year value when it sets the clock. It sets it to 1995, 1996, 1997, or
+1998, whichever one has the same position in the leap year cycle as
+the true year. That way, the Hardware Clock inserts leap days where
+they belong. Again, if you let the Hardware Clock run for more than a
+year without setting it, this scheme could be defeated and you could
+end up losing a day.
+
+.B hwclock
+warns you that you probably need
+.B \-\-badyear
+whenever it finds your Hardware Clock set to 1994 or 1995.
+
+.TP
+.B \-\-srm
+This option is equivalent to
+.B \-\-epoch=1900
+and is used to specify the most common epoch on Alphas
+with SRM console.
+.TP
+.B \-\-arc
+This option is equivalent to
+.B \-\-epoch=1980
+and is used to specify the most common epoch on Alphas
+with ARC console (but Ruffians have epoch 1900).
+.TP
+.B \-\-jensen
+.TP
+.B \-\-funky\-toy
+These two options specify what kind of Alpha machine you have. They
+are invalid if you don't have an Alpha and are usually unnecessary
+if you do, because
+.B hwclock
+should be able to determine by itself what it's
+running on, at least when
+.I /proc
+is mounted.
+(If you find you need one of these options to make
+.B hwclock
+work, contact the maintainer to see if the program can be improved
+to detect your system automatically. Output of `hwclock --debug'
+and `cat /proc/cpuinfo' may be of interest.)
+
+Option
+.B \-\-jensen
+means you are running on a Jensen model. And
+.B \-\-funky\-toy
+means that on your machine one has to use the UF bit instead
+of the UIP bit in the Hardware Clock to detect a time transition. "Toy"
+in the option name refers to the Time Of Year facility of the machine.
+
+
+.TP
+.B \-\-test
+Do everything except actually updating the Hardware Clock or anything
+else. This is useful, especially in conjunction with
+.BR \-\-debug ,
+in learning about
+.BR hwclock .
+.TP
+.B \-\-debug
+Display a lot of information about what
+.B hwclock
+is doing internally. Some of its function is complex and this output
+can help you understand how the program works.
+
+
+.SH NOTES
+
+
+.SH Clocks in a Linux System
+.PP
+There are two main clocks in a Linux system:
+.PP
+.B The Hardware Clock:
+This is a clock that runs independently of any control program running
+in the CPU and even when the machine is powered off.
+
+On an ISA system, this clock is specified as part of the ISA standard.
+The control program can read or set this clock to a whole second, but
+the control program can also detect the edges of the 1 second clock
+ticks, so the clock actually has virtually infinite precision.
+.PP
+This clock is commonly called the hardware clock, the real time clock,
+the RTC, the BIOS clock, and the CMOS clock. Hardware Clock, in its
+capitalized form, was coined for use by
+.B hwclock
+because all of the other names are inappropriate to the point of being
+misleading.
+.PP
+So for example, some non-ISA systems have a few real time clocks with
+only one of them having its own power domain.
+A very low power external I2C or SPI clock chip might be used with a
+backup battery as the hardware clock to initialize a more functional
+integrated real-time clock which is used for most other purposes.
+.PP
+.B The System Time:
+This is the time kept by a clock inside the Linux kernel and driven by
+a timer interrupt. (On an ISA machine, the timer interrupt is part of
+the ISA standard). It has meaning only while Linux is running on the
+machine. The System Time is the number of seconds since 00:00:00
+January 1, 1970 UTC (or more succinctly, the number of seconds since
+1969). The System Time is not an integer, though. It has virtually
+infinite precision.
+.PP
+The System Time is the time that matters. The Hardware Clock's basic
+purpose in a Linux system is to keep time when Linux is not running. You
+initialize the System Time to the time from the Hardware Clock when Linux
+starts up, and then never use the Hardware Clock again. Note that in DOS,
+for which ISA was designed, the Hardware Clock is the only real time clock.
+.PP
+It is important that the System Time not have any discontinuities such as
+would happen if you used the
+.BR date (1L)
+program to set it while the system is running. You can, however, do whatever
+you want to the Hardware Clock while the system is running, and the next
+time Linux starts up, it will do so with the adjusted time from the Hardware
+Clock. You can also use the program
+.BR adjtimex (8)
+to smoothly adjust the System Time while the system runs.
+.PP
+A Linux kernel maintains a concept of a local timezone for the system.
+But don't be misled -- almost nobody cares what timezone the kernel
+thinks it is in. Instead, programs that care about the timezone
+(perhaps because they want to display a local time for you) almost
+always use a more traditional method of determining the timezone: They
+use the TZ environment variable and/or the
+.I /usr/share/zoneinfo
+directory, as explained in the man page for
+.BR tzset (3).
+However, some
+programs and fringe parts of the Linux kernel such as filesystems use
+the kernel timezone value. An example is the vfat filesystem. If the
+kernel timezone value is wrong, the vfat filesystem will report and
+set the wrong timestamps on files.
+.PP
+.B hwclock
+sets the kernel timezone to the value indicated by TZ and/or
+.I /usr/share/zoneinfo
+when you set the System Time using the
+.B \-\-hctosys
+option.
+.PP
+The timezone value actually consists of two parts: 1) a field
+tz_minuteswest indicating how many minutes local time (not adjusted
+for DST) lags behind UTC, and 2) a field tz_dsttime indicating
+the type of Daylight Savings Time (DST) convention that is in effect
+in the locality at the present time.
+This second field is not used under Linux and is always zero.
+(See also
+.BR settimeofday (2).)
+
+.SH Users access and setuid
+.PP
+Sometimes, you need to install
+.B hwclock
+setuid root. If you want users other than the superuser to be able to
+display the clock value using the direct ISA I/O method, install it setuid
+root. If you have the /dev/rtc interface on your system or are on a non-ISA
+system, there's probably no need for users to use the direct ISA I/O method,
+so don't bother.
+
+In any case, hwclock will not allow you to set anything unless you have the
+superuser real uid. (This is restriction is not necessary if you haven't
+installed setuid root, but it's there for now).
+
+.SH How hwclock Accesses the Hardware Clock
+.PP
+.B hwclock
+uses many different ways to get and set Hardware Clock values.
+The most normal way is to do I/O to the device special file /dev/rtc,
+which is presumed to be driven by the rtc device driver. However,
+this method is not always available. For one thing, the rtc driver is
+a relatively recent addition to Linux. Older systems don't have it.
+Also, though there are versions of the rtc driver that work on DEC
+Alphas, there appear to be plenty of Alphas on which the rtc driver
+does not work (a common symptom is hwclock hanging).
+Moreover, recent Linux systems have more generic support for RTCs,
+even systems that have more than one, so you might need to override
+the default by specifying
+.I /dev/rtc0
+or
+.I /dev/rtc1
+instead.
+.PP
+On older systems, the method of accessing the Hardware Clock depends on
+the system hardware.
+.PP
+On an ISA system,
+.B hwclock
+can directly access the "CMOS memory" registers that
+constitute the clock, by doing I/O to Ports 0x70 and 0x71. It does
+this with actual I/O instructions and consequently can only do it if
+running with superuser effective userid. (In the case of a Jensen
+Alpha, there is no way for
+.B hwclock
+to execute those I/O instructions, and so it uses instead the
+/dev/port device special file, which provides almost as low-level an
+interface to the I/O subsystem).
+
+This is a really poor method of accessing the clock, for all the
+reasons that user space programs are generally not supposed to do
+direct I/O and disable interrupts. Hwclock provides it because it is
+the only method available on ISA and Alpha systems which don't have
+working rtc device drivers available.
+
+.PP
+On an m68k system,
+.B hwclock
+can access the clock via the console driver, via the device special
+file /dev/tty1.
+.PP
+.B hwclock
+tries to use /dev/rtc. If it is compiled for a kernel that doesn't have
+that function or it is unable to open /dev/rtc
+(or the alternative special file you've defined on the command line)
+.B hwclock
+will fall back to another method, if available. On an ISA or Alpha
+machine, you can force
+.B hwclock
+to use the direct manipulation of the CMOS registers without even trying
+.I /dev/rtc
+by specifying the
+.B \-\-directisa
+option.
+
+
+.SH The Adjust Function
+.PP
+The Hardware Clock is usually not very accurate. However, much of its
+inaccuracy is completely predictable - it gains or loses the same amount
+of time every day. This is called systematic drift.
+.BR hwclock 's
+"adjust" function lets you make systematic corrections to correct the
+systematic drift.
+.PP
+It works like this:
+.B hwclock
+keeps a file,
+.IR /etc/adjtime ,
+that keeps some historical information. This is called the adjtime file.
+.PP
+Suppose you start with no adjtime file. You issue a
+.I hwclock \-\-set
+command to set the Hardware Clock to the true current time.
+.B Hwclock
+creates the adjtime file and records in it the current time as the
+last time the clock was calibrated.
+5 days later, the clock has gained 10 seconds, so you issue another
+.I hwclock \-\-set
+command to set it back 10 seconds.
+.B Hwclock
+updates the adjtime file to show the current time as the last time the
+clock was calibrated, and records 2 seconds per day as the systematic
+drift rate. 24 hours go by, and then you issue a
+.I hwclock \-\-adjust
+command.
+.B Hwclock
+consults the adjtime file and sees that the clock gains 2 seconds per
+day when left alone and that it has been left alone for exactly one
+day. So it subtracts 2 seconds from the Hardware Clock. It then
+records the current time as the last time the clock was adjusted.
+Another 24 hours goes by and you issue another
+.IR "hwclock \-\-adjust" .
+.B Hwclock
+does the same thing: subtracts 2 seconds and updates the adjtime file
+with the current time as the last time the clock was adjusted.
+.PP
+Every time you calibrate (set) the clock (using
+.I \-\-set
+or
+.IR \-\-systohc ),
+.B hwclock
+recalculates the systematic drift rate based on how long it has been
+since the last calibration, how long it has been since the last
+adjustment, what drift rate was assumed in any intervening
+adjustments, and the amount by which the clock is presently off.
+.PP
+A small amount of error creeps in any time
+.B hwclock
+sets the clock, so it refrains from making an adjustment that would be
+less than 1 second. Later on, when you request an adjustment again,
+the accumulated drift will be more than a second and
+.B hwclock
+will do the adjustment then.
+.PP
+It is good to do a
+.I hwclock \-\-adjust
+just before the
+.I hwclock \-\-hctosys
+at system startup time, and maybe periodically while the system is
+running via cron.
+.PP
+The adjtime file, while named for its historical purpose of controlling
+adjustments only, actually contains other information for use by hwclock
+in remembering information from one invocation to the next.
+.PP
+The format of the adjtime file is, in ASCII:
+.PP
+Line 1: 3 numbers, separated by blanks: 1) systematic drift rate in
+seconds per day, floating point decimal; 2) Resulting number of
+seconds since 1969 UTC of most recent adjustment or calibration,
+decimal integer; 3) zero (for compatibility with
+.BR clock (8))
+as a decimal integer.
+.PP
+Line 2: 1 number: Resulting number of seconds since 1969 UTC of most
+recent calibration. Zero if there has been no calibration yet or it
+is known that any previous calibration is moot (for example, because
+the Hardware Clock has been found, since that calibration, not to
+contain a valid time). This is a decimal integer.
+.PP
+Line 3: "UTC" or "LOCAL". Tells whether the Hardware Clock is set to
+Coordinated Universal Time or local time. You can always override this
+value with options on the
+.B hwclock
+command line.
+.PP
+You can use an adjtime file that was previously used with the
+.BR clock (8)
+program with
+.BR hwclock .
+
+
+.SH "Automatic Hardware Clock Synchronization By the Kernel"
+
+You should be aware of another way that the Hardware Clock is kept
+synchronized in some systems. The Linux kernel has a mode wherein it
+copies the System Time to the Hardware Clock every 11 minutes.
+This is a good mode to use when you are using something sophisticated
+like ntp to keep your System Time synchronized. (ntp is a way to keep
+your System Time synchronized either to a time server somewhere on the
+network or to a radio clock hooked up to your system. See RFC 1305).
+
+This mode (we'll call it "11 minute mode") is off until something
+turns it on. The ntp daemon xntpd is one thing that turns it on. You
+can turn it off by running anything, including
+.IR "hwclock \-\-hctosys" ,
+that sets the System Time the old fashioned way.
+
+To see if it is on or
+off, use the command
+.I adjtimex \-\-print
+and look at the value of "status". If the "64" bit of this number
+(expressed in binary) equal to 0, 11 minute mode is on. Otherwise, it
+is off.
+
+If your system runs with 11 minute mode on, don't use
+.I hwclock \-\-adjust
+or
+.IR "hwclock \-\-hctosys" .
+You'll just make a mess. It is acceptable to use a
+.I hwclock \-\-hctosys
+at startup time to get a reasonable System Time until your system is
+able to set the System Time from the external source and start 11
+minute mode.
+
+
+.SH ISA Hardware Clock Century value
+
+There is some sort of standard that defines CMOS memory Byte 50 on an ISA
+machine as an indicator of what century it is.
+.B hwclock
+does not use or set that byte because there are some machines that
+don't define the byte that way, and it really isn't necessary anyway,
+since the year-of-century does a good job of implying which century it
+is.
+
+If you have a bona fide use for a CMOS century byte, contact the
+.B hwclock
+maintainer; an option may be appropriate.
+
+Note that this section is only relevant when you are using the "direct
+ISA" method of accessing the Hardware Clock.
+ACPI provides a standard way to access century values, when they
+are supported by the hardware.
+
+.SH "ENVIRONMENT VARIABLES"
+.I TZ
+
+.SH FILES
+.I /etc/adjtime
+.I /usr/share/zoneinfo/
+.RI ( /usr/lib/zoneinfo
+on old systems)
+.I /dev/rtc
+.I /dev/rtc0
+.I /dev/port
+.I /dev/tty1
+.I /proc/cpuinfo
+
+.SH "SEE ALSO"
+.BR adjtimex (8),
+.BR date (1),
+.BR gettimeofday (2),
+.BR settimeofday (2),
+.BR crontab (1),
+.BR tzset (3)
+
+.SH AUTHORS
+Written by Bryan Henderson, September 1996 (bryanh@giraffe-data.com),
+based on work done on the
+.I clock
+program by Charles Hedrick, Rob Hooft, and Harald Koenig.
+See the source code for complete history and credits.
+
+.SH AVAILABILITY
+The hwclock command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/sys-utils/hwclock.c b/sys-utils/hwclock.c
new file mode 100644
index 0000000..e25546d
--- /dev/null
+++ b/sys-utils/hwclock.c
@@ -0,0 +1,1870 @@
+/*
+ * hwclock.c
+ *
+ * clock.c was written by Charles Hedrick, hedrick@cs.rutgers.edu, Apr 1992
+ * Modified for clock adjustments - Rob Hooft <hooft@chem.ruu.nl>, Nov 1992
+ * Improvements by Harald Koenig <koenig@nova.tat.physik.uni-tuebingen.de>
+ * and Alan Modra <alan@spri.levels.unisa.edu.au>.
+ *
+ * Major rewrite by Bryan Henderson <bryanh@giraffe-data.com>, 96.09.19.
+ * The new program is called hwclock. New features:
+ *
+ * - You can set the hardware clock without also modifying the system
+ * clock.
+ * - You can read and set the clock with finer than 1 second precision.
+ * - When you set the clock, hwclock automatically refigures the drift
+ * rate, based on how far off the clock was before you set it.
+ *
+ * Reshuffled things, added sparc code, and re-added alpha stuff
+ * by David Mosberger <davidm@azstarnet.com>
+ * and Jay Estabrook <jestabro@amt.tay1.dec.com>
+ * and Martin Ostermann <ost@coments.rwth-aachen.de>, aeb@cwi.nl, 990212.
+ *
+ * Fix for Award 2094 bug, Dave Coffin (dcoffin@shore.net) 11/12/98
+ * Change of local time handling, Stefan Ring <e9725446@stud3.tuwien.ac.at>
+ * Change of adjtime handling, James P. Rutledge <ao112@rgfn.epcc.edu>.
+ *
+ * Distributed under GPL
+ */
+/*
+ * Explanation of `adjusting' (Rob Hooft):
+ *
+ * The problem with my machine is that its CMOS clock is 10 seconds
+ * per day slow. With this version of clock.c, and my '/etc/rc.local'
+ * reading '/etc/clock -au' instead of '/etc/clock -u -s', this error
+ * is automatically corrected at every boot.
+ *
+ * To do this job, the program reads and writes the file '/etc/adjtime'
+ * to determine the correction, and to save its data. In this file are
+ * three numbers:
+ *
+ * 1) the correction in seconds per day. (So if your clock runs 5
+ * seconds per day fast, the first number should read -5.0)
+ * 2) the number of seconds since 1/1/1970 the last time the program
+ * was used
+ * 3) the remaining part of a second which was leftover after the last
+ * adjustment
+ *
+ * Installation and use of this program:
+ *
+ * a) create a file '/etc/adjtime' containing as the first and only
+ * line: '0.0 0 0.0'
+ * b) run 'clock -au' or 'clock -a', depending on whether your cmos is
+ * in universal or local time. This updates the second number.
+ * c) set your system time using the 'date' command.
+ * d) update your cmos time using 'clock -wu' or 'clock -w'
+ * e) replace the first number in /etc/adjtime by your correction.
+ * f) put the command 'clock -au' or 'clock -a' in your '/etc/rc.local'
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+#define OPTUTILS_EXIT_CODE EX_USAGE
+
+#include "c.h"
+#include "closestream.h"
+#include "nls.h"
+#include "optutils.h"
+#include "pathnames.h"
+#include "strutils.h"
+#include "hwclock.h"
+
+#ifdef HAVE_LIBAUDIT
+#include <libaudit.h>
+static int hwaudit_fd = -1;
+static int hwaudit_on;
+#endif
+
+/* The struct that holds our hardware access routines */
+struct clock_ops *ur;
+
+#define FLOOR(arg) ((arg >= 0 ? (int) arg : ((int) arg) - 1));
+
+const char *adj_file_name = NULL;
+
+struct adjtime {
+ /*
+ * This is information we keep in the adjtime file that tells us how
+ * to do drift corrections. Elements are all straight from the
+ * adjtime file, so see documentation of that file for details.
+ * Exception is <dirty>, which is an indication that what's in this
+ * structure is not what's in the disk file (because it has been
+ * updated since read from the disk file).
+ */
+ bool dirty;
+ /* line 1 */
+ double drift_factor;
+ time_t last_adj_time;
+ double not_adjusted;
+ /* line 2 */
+ time_t last_calib_time;
+ /*
+ * The most recent time that we set the clock from an external
+ * authority (as opposed to just doing a drift adjustment)
+ */
+ /* line 3 */
+ enum a_local_utc { LOCAL, UTC, UNKNOWN } local_utc;
+ /*
+ * To which time zone, local or UTC, we most recently set the
+ * hardware clock.
+ */
+};
+
+/*
+ * We are running in debug mode, wherein we put a lot of information about
+ * what we're doing to standard output.
+ */
+bool debug;
+
+/* Workaround for Award 4.50g BIOS bug: keep the year in a file. */
+bool badyear;
+
+/* User-specified epoch, used when rtc fails to return epoch. */
+unsigned long epoch_option = -1;
+
+/*
+ * Almost all Award BIOS's made between 04/26/94 and 05/31/95 have a nasty
+ * bug limiting the RTC year byte to the range 94-99. Any year between 2000
+ * and 2093 gets changed to 2094, every time you start the system.
+ *
+ * With the --badyear option, we write the date to file and hope that the
+ * file is updated at least once a year. I recommend putting this command
+ * "hwclock --badyear" in the monthly crontab, just to be safe.
+ *
+ * -- Dave Coffin 11/12/98
+ */
+static void write_date_to_file(struct tm *tm)
+{
+ FILE *fp;
+
+ if ((fp = fopen(_PATH_LASTDATE, "w"))) {
+ fprintf(fp, "%02d.%02d.%04d\n", tm->tm_mday, tm->tm_mon + 1,
+ tm->tm_year + 1900);
+ if (close_stream(fp) != 0)
+ warn(_("cannot write %s"), _PATH_LASTDATE);
+ } else
+ warn(_("cannot write %s"), _PATH_LASTDATE);
+}
+
+static void read_date_from_file(struct tm *tm)
+{
+ int last_mday, last_mon, last_year;
+ FILE *fp;
+
+ if ((fp = fopen(_PATH_LASTDATE, "r"))) {
+ if (fscanf(fp, "%d.%d.%d\n", &last_mday, &last_mon, &last_year)
+ == 3) {
+ tm->tm_year = last_year - 1900;
+ if ((tm->tm_mon << 5) + tm->tm_mday <
+ ((last_mon - 1) << 5) + last_mday)
+ tm->tm_year++;
+ }
+ fclose(fp);
+ }
+ write_date_to_file(tm);
+}
+
+/*
+ * The difference in seconds between two times in "timeval" format.
+ */
+double time_diff(struct timeval subtrahend, struct timeval subtractor)
+{
+ return (subtrahend.tv_sec - subtractor.tv_sec)
+ + (subtrahend.tv_usec - subtractor.tv_usec) / 1E6;
+}
+
+/*
+ * The time, in "timeval" format, which is <increment> seconds after the
+ * time <addend>. Of course, <increment> may be negative.
+ */
+static struct timeval time_inc(struct timeval addend, double increment)
+{
+ struct timeval newtime;
+
+ newtime.tv_sec = addend.tv_sec + (int)increment;
+ newtime.tv_usec = addend.tv_usec + (increment - (int)increment) * 1E6;
+
+ /*
+ * Now adjust it so that the microsecond value is between 0 and 1
+ * million.
+ */
+ if (newtime.tv_usec < 0) {
+ newtime.tv_usec += 1E6;
+ newtime.tv_sec -= 1;
+ } else if (newtime.tv_usec >= 1E6) {
+ newtime.tv_usec -= 1E6;
+ newtime.tv_sec += 1;
+ }
+ return newtime;
+}
+
+static bool
+hw_clock_is_utc(const bool utc, const bool local_opt,
+ const struct adjtime adjtime)
+{
+ bool ret;
+
+ if (utc)
+ ret = TRUE; /* --utc explicitly given on command line */
+ else if (local_opt)
+ ret = FALSE; /* --localtime explicitly given */
+ else
+ /* get info from adjtime file - default is UTC */
+ ret = (adjtime.local_utc != LOCAL);
+ if (debug)
+ printf(_("Assuming hardware clock is kept in %s time.\n"),
+ ret ? _("UTC") : _("local"));
+ return ret;
+}
+
+/*
+ * Read the adjustment parameters out of the /etc/adjtime file.
+ *
+ * Return them as the adjtime structure <*adjtime_p>. If there is no
+ * /etc/adjtime file, return defaults. If values are missing from the file,
+ * return defaults for them.
+ *
+ * return value 0 if all OK, !=0 otherwise.
+ */
+static int read_adjtime(struct adjtime *adjtime_p)
+{
+ FILE *adjfile;
+ int rc; /* local return code */
+ struct stat statbuf; /* We don't even use the contents of this. */
+ char line1[81]; /* String: first line of adjtime file */
+ char line2[81]; /* String: second line of adjtime file */
+ char line3[81]; /* String: third line of adjtime file */
+ long timeval;
+
+ rc = stat(adj_file_name, &statbuf);
+ if (rc < 0 && errno == ENOENT) {
+ /* He doesn't have a adjtime file, so we'll use defaults. */
+ adjtime_p->drift_factor = 0;
+ adjtime_p->last_adj_time = 0;
+ adjtime_p->not_adjusted = 0;
+ adjtime_p->last_calib_time = 0;
+ adjtime_p->local_utc = UNKNOWN;
+ adjtime_p->dirty = FALSE; /* don't create a zero adjfile */
+
+ return 0;
+ }
+
+ adjfile = fopen(adj_file_name, "r"); /* open file for reading */
+ if (adjfile == NULL) {
+ warn("cannot open %s", adj_file_name);
+ return EX_OSFILE;
+ }
+
+
+ if (!fgets(line1, sizeof(line1), adjfile))
+ line1[0] = '\0'; /* In case fgets fails */
+ if (!fgets(line2, sizeof(line2), adjfile))
+ line2[0] = '\0'; /* In case fgets fails */
+ if (!fgets(line3, sizeof(line3), adjfile))
+ line3[0] = '\0'; /* In case fgets fails */
+
+ fclose(adjfile);
+
+ /* Set defaults in case values are missing from file */
+ adjtime_p->drift_factor = 0;
+ adjtime_p->last_adj_time = 0;
+ adjtime_p->not_adjusted = 0;
+ adjtime_p->last_calib_time = 0;
+ timeval = 0;
+
+ sscanf(line1, "%lf %ld %lf",
+ &adjtime_p->drift_factor,
+ &timeval, &adjtime_p->not_adjusted);
+ adjtime_p->last_adj_time = timeval;
+
+ sscanf(line2, "%ld", &timeval);
+ adjtime_p->last_calib_time = timeval;
+
+ if (!strcmp(line3, "UTC\n")) {
+ adjtime_p->local_utc = UTC;
+ } else if (!strcmp(line3, "LOCAL\n")) {
+ adjtime_p->local_utc = LOCAL;
+ } else {
+ adjtime_p->local_utc = UNKNOWN;
+ if (line3[0]) {
+ warnx(_("Warning: unrecognized third line in adjtime file\n"
+ "(Expected: `UTC' or `LOCAL' or nothing.)"));
+ }
+ }
+
+ adjtime_p->dirty = FALSE;
+
+ if (debug) {
+ printf(_
+ ("Last drift adjustment done at %ld seconds after 1969\n"),
+ (long)adjtime_p->last_adj_time);
+ printf(_("Last calibration done at %ld seconds after 1969\n"),
+ (long)adjtime_p->last_calib_time);
+ printf(_("Hardware clock is on %s time\n"),
+ (adjtime_p->local_utc ==
+ LOCAL) ? _("local") : (adjtime_p->local_utc ==
+ UTC) ? _("UTC") : _("unknown"));
+ }
+
+ return 0;
+}
+
+/*
+ * Wait until the falling edge of the Hardware Clock's update flag so that
+ * any time that is read from the clock immediately after we return will be
+ * exact.
+ *
+ * The clock only has 1 second precision, so it gives the exact time only
+ * once per second, right on the falling edge of the update flag.
+ *
+ * We wait (up to one second) either blocked waiting for an rtc device or in
+ * a CPU spin loop. The former is probably not very accurate.
+ *
+ * Return 0 if it worked, nonzero if it didn't.
+ */
+static int synchronize_to_clock_tick(void)
+{
+ int rc;
+
+ if (debug)
+ printf(_("Waiting for clock tick...\n"));
+
+ rc = ur->synchronize_to_clock_tick();
+
+ if (debug) {
+ if (rc)
+ printf(_("...synchronization failed\n"));
+ else
+ printf(_("...got clock tick\n"));
+ }
+
+ return rc;
+}
+
+/*
+ * Convert a time in broken down format (hours, minutes, etc.) into standard
+ * unix time (seconds into epoch). Return it as *systime_p.
+ *
+ * The broken down time is argument <tm>. This broken down time is either
+ * in local time zone or UTC, depending on value of logical argument
+ * "universal". True means it is in UTC.
+ *
+ * If the argument contains values that do not constitute a valid time, and
+ * mktime() recognizes this, return *valid_p == false and *systime_p
+ * undefined. However, mktime() sometimes goes ahead and computes a
+ * fictional time "as if" the input values were valid, e.g. if they indicate
+ * the 31st day of April, mktime() may compute the time of May 1. In such a
+ * case, we return the same fictional value mktime() does as *systime_p and
+ * return *valid_p == true.
+ */
+static void
+mktime_tz(struct tm tm, const bool universal,
+ bool * valid_p, time_t * systime_p)
+{
+ time_t mktime_result; /* The value returned by our mktime() call */
+ char *zone; /* Local time zone name */
+
+ /*
+ * We use the C library function mktime(), but since it only works
+ * on local time zone input, we may have to fake it out by
+ * temporarily changing the local time zone to UTC.
+ */
+ zone = getenv("TZ"); /* remember original time zone */
+ if (universal) {
+ /* Set timezone to UTC */
+ setenv("TZ", "", TRUE);
+ /*
+ * Note: tzset() gets called implicitly by the time code,
+ * but only the first time. When changing the environment
+ * variable, better call tzset() explicitly.
+ */
+ tzset();
+ }
+ mktime_result = mktime(&tm);
+ if (mktime_result == -1) {
+ /*
+ * This apparently (not specified in mktime() documentation)
+ * means the 'tm' structure does not contain valid values
+ * (however, not containing valid values does _not_ imply
+ * mktime() returns -1).
+ */
+ *valid_p = FALSE;
+ *systime_p = 0;
+ if (debug)
+ printf(_("Invalid values in hardware clock: "
+ "%4d/%.2d/%.2d %.2d:%.2d:%.2d\n"),
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ } else {
+ *valid_p = TRUE;
+ *systime_p = mktime_result;
+ if (debug)
+ printf(_
+ ("Hw clock time : %4d/%.2d/%.2d %.2d:%.2d:%.2d = "
+ "%ld seconds since 1969\n"), tm.tm_year + 1900,
+ tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min,
+ tm.tm_sec, (long)*systime_p);
+ }
+ /* now put back the original zone. */
+ if (zone)
+ setenv("TZ", zone, TRUE);
+ else
+ unsetenv("TZ");
+ tzset();
+}
+
+/*
+ * Read the hardware clock and return the current time via <tm> argument.
+ *
+ * Use the method indicated by <method> argument to access the hardware
+ * clock.
+ */
+static int
+read_hardware_clock(const bool universal, bool * valid_p, time_t * systime_p)
+{
+ struct tm tm;
+ int err;
+
+ err = ur->read_hardware_clock(&tm);
+ if (err)
+ return err;
+
+ if (badyear)
+ read_date_from_file(&tm);
+
+ if (debug)
+ printf(_
+ ("Time read from Hardware Clock: %4d/%.2d/%.2d %02d:%02d:%02d\n"),
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
+ tm.tm_min, tm.tm_sec);
+ mktime_tz(tm, universal, valid_p, systime_p);
+
+ return 0;
+}
+
+/*
+ * Set the Hardware Clock to the time <newtime>, in local time zone or UTC,
+ * according to <universal>.
+ */
+static void
+set_hardware_clock(const time_t newtime,
+ const bool universal, const bool testing)
+{
+ struct tm new_broken_time;
+ /*
+ * Time to which we will set Hardware Clock, in broken down format,
+ * in the time zone of caller's choice
+ */
+
+ if (universal)
+ new_broken_time = *gmtime(&newtime);
+ else
+ new_broken_time = *localtime(&newtime);
+
+ if (debug)
+ printf(_("Setting Hardware Clock to %.2d:%.2d:%.2d "
+ "= %ld seconds since 1969\n"),
+ new_broken_time.tm_hour, new_broken_time.tm_min,
+ new_broken_time.tm_sec, (long)newtime);
+
+ if (testing)
+ printf(_("Clock not changed - testing only.\n"));
+ else {
+ if (badyear) {
+ /*
+ * Write the real year to a file, then write a fake
+ * year between 1995 and 1998 to the RTC. This way,
+ * Award BIOS boots on 29 Feb 2000 thinking that
+ * it's 29 Feb 1996.
+ */
+ write_date_to_file(&new_broken_time);
+ new_broken_time.tm_year =
+ 95 + ((new_broken_time.tm_year + 1) & 3);
+ }
+ ur->set_hardware_clock(&new_broken_time);
+ }
+}
+
+/*
+ * Set the Hardware Clock to the time "sethwtime", in local time zone or
+ * UTC, according to "universal".
+ *
+ * Wait for a fraction of a second so that "sethwtime" is the value of the
+ * Hardware Clock as of system time "refsystime", which is in the past. For
+ * example, if "sethwtime" is 14:03:05 and "refsystime" is 12:10:04.5 and
+ * the current system time is 12:10:06.0: Wait .5 seconds (to make exactly 2
+ * seconds since "refsystime") and then set the Hardware Clock to 14:03:07,
+ * thus getting a precise and retroactive setting of the clock.
+ *
+ * (Don't be confused by the fact that the system clock and the Hardware
+ * Clock differ by two hours in the above example. That's just to remind you
+ * that there are two independent time scales here).
+ *
+ * This function ought to be able to accept set times as fractional times.
+ * Idea for future enhancement.
+ */
+static void
+set_hardware_clock_exact(const time_t sethwtime,
+ const struct timeval refsystime,
+ const bool universal, const bool testing)
+{
+ time_t newhwtime = sethwtime;
+ struct timeval beginsystime, nowsystime;
+ double tdiff;
+ int time_resync = 1;
+
+ /*
+ * Now delay some more until Hardware Clock time newhwtime arrives.
+ * The 0.5 s is because the Hardware Clock always sets to your set
+ * time plus 500 ms (because it is designed to update to the next
+ * second precisely 500 ms after you finish the setting).
+ */
+ do {
+ if (time_resync) {
+ gettimeofday(&beginsystime, NULL);
+ tdiff = time_diff(beginsystime, refsystime);
+ newhwtime = sethwtime + (int)(tdiff + 0.5);
+ if (debug)
+ printf(_
+ ("Time elapsed since reference time has been %.6f seconds.\n"
+ "Delaying further to reach the new time.\n"),
+ tdiff);
+ time_resync = 0;
+ }
+
+ gettimeofday(&nowsystime, NULL);
+ tdiff = time_diff(nowsystime, beginsystime);
+ if (tdiff < 0) {
+ time_resync = 1; /* probably backward time reset */
+ continue;
+ }
+ if (tdiff > 0.1) {
+ time_resync = 1; /* probably forward time reset */
+ continue;
+ }
+ beginsystime = nowsystime;
+ tdiff = time_diff(nowsystime, refsystime);
+ } while (newhwtime == sethwtime + (int)(tdiff + 0.5));
+
+ set_hardware_clock(newhwtime, universal, testing);
+}
+
+/*
+ * Put the time "systime" on standard output in display format. Except if
+ * hclock_valid == false, just tell standard output that we don't know what
+ * time it is.
+ *
+ * Include in the output the adjustment "sync_duration".
+ */
+static void
+display_time(const bool hclock_valid, const time_t systime,
+ const double sync_duration)
+{
+ if (!hclock_valid)
+ warnx(_
+ ("The Hardware Clock registers contain values that are "
+ "either invalid (e.g. 50th day of month) or beyond the range "
+ "we can handle (e.g. Year 2095)."));
+ else {
+ struct tm *lt;
+ char *format = "%c";
+ char ctime_now[200];
+
+ lt = localtime(&systime);
+ strftime(ctime_now, sizeof(ctime_now), format, lt);
+ printf(_("%s %.6f seconds\n"), ctime_now, -(sync_duration));
+ }
+}
+
+/*
+ * Interpret the value of the --date option, which is something like
+ * "13:05:01". In fact, it can be any of the myriad ASCII strings that
+ * specify a time which the "date" program can understand. The date option
+ * value in question is our "dateopt" argument.
+ *
+ * The specified time is in the local time zone.
+ *
+ * Our output, "*time_p", is a seconds-into-epoch time.
+ *
+ * We use the "date" program to interpret the date string. "date" must be
+ * runnable by issuing the command "date" to the /bin/sh shell. That means
+ * in must be in the current PATH.
+ *
+ * If anything goes wrong (and many things can), we return return code 10
+ * and arbitrary *time_p. Otherwise, return code is 0 and *time_p is valid.
+ */
+static int interpret_date_string(const char *date_opt, time_t * const time_p)
+{
+ FILE *date_child_fp;
+ char date_resp[100];
+ const char magic[] = "seconds-into-epoch=";
+ char date_command[100];
+ int retcode; /* our eventual return code */
+ int rc; /* local return code */
+
+ if (date_opt == NULL) {
+ warnx(_("No --date option specified."));
+ return 14;
+ }
+
+ /* prevent overflow - a security risk */
+ if (strlen(date_opt) > sizeof(date_command) - 50) {
+ warnx(_("--date argument too long"));
+ return 13;
+ }
+
+ /* Quotes in date_opt would ruin the date command we construct. */
+ if (strchr(date_opt, '"') != NULL) {
+ warnx(_
+ ("The value of the --date option is not a valid date.\n"
+ "In particular, it contains quotation marks."));
+ return 12;
+ }
+
+ sprintf(date_command, "date --date=\"%s\" +seconds-into-epoch=%%s",
+ date_opt);
+ if (debug)
+ printf(_("Issuing date command: %s\n"), date_command);
+
+ date_child_fp = popen(date_command, "r");
+ if (date_child_fp == NULL) {
+ warn(_("Unable to run 'date' program in /bin/sh shell. "
+ "popen() failed"));
+ return 10;
+ }
+
+ if (!fgets(date_resp, sizeof(date_resp), date_child_fp))
+ date_resp[0] = '\0'; /* in case fgets fails */
+ if (debug)
+ printf(_("response from date command = %s\n"), date_resp);
+ if (strncmp(date_resp, magic, sizeof(magic) - 1) != 0) {
+ warnx(_("The date command issued by %s returned "
+ "unexpected results.\n"
+ "The command was:\n %s\n"
+ "The response was:\n %s"),
+ program_invocation_short_name, date_command, date_resp);
+ retcode = 8;
+ } else {
+ long seconds_since_epoch;
+ rc = sscanf(date_resp + sizeof(magic) - 1, "%ld",
+ &seconds_since_epoch);
+ if (rc < 1) {
+ warnx(_("The date command issued by %s returned "
+ "something other than an integer where the "
+ "converted time value was expected.\n"
+ "The command was:\n %s\n"
+ "The response was:\n %s\n"),
+ program_invocation_short_name, date_command,
+ date_resp);
+ retcode = 6;
+ } else {
+ retcode = 0;
+ *time_p = seconds_since_epoch;
+ if (debug)
+ printf(_("date string %s equates to "
+ "%ld seconds since 1969.\n"),
+ date_opt, (long)*time_p);
+ }
+ }
+ pclose(date_child_fp);
+
+ return retcode;
+}
+
+/*
+ * Set the System Clock to time 'newtime'.
+ *
+ * Also set the kernel time zone value to the value indicated by the TZ
+ * environment variable and/or /usr/lib/zoneinfo/, interpreted as tzset()
+ * would interpret them.
+ *
+ * EXCEPT: if hclock_valid is false, just issue an error message saying
+ * there is no valid time in the Hardware Clock to which to set the system
+ * time.
+ *
+ * If 'testing' is true, don't actually update anything -- just say we would
+ * have.
+ */
+static int
+set_system_clock(const bool hclock_valid, const time_t newtime,
+ const bool testing)
+{
+ int retcode;
+
+ if (!hclock_valid) {
+ warnx(_
+ ("The Hardware Clock does not contain a valid time, so "
+ "we cannot set the System Time from it."));
+ retcode = 1;
+ } else {
+ struct timeval tv;
+ struct tm *broken;
+ int minuteswest;
+ int rc;
+
+ tv.tv_sec = newtime;
+ tv.tv_usec = 0;
+
+ broken = localtime(&newtime);
+#ifdef HAVE_TM_GMTOFF
+ minuteswest = -broken->tm_gmtoff / 60; /* GNU extension */
+#else
+ minuteswest = timezone / 60;
+ if (broken->tm_isdst)
+ minuteswest -= 60;
+#endif
+
+ if (debug) {
+ printf(_("Calling settimeofday:\n"));
+ printf(_("\ttv.tv_sec = %ld, tv.tv_usec = %ld\n"),
+ (long)tv.tv_sec, (long)tv.tv_usec);
+ printf(_("\ttz.tz_minuteswest = %d\n"), minuteswest);
+ }
+ if (testing) {
+ printf(_
+ ("Not setting system clock because running in test mode.\n"));
+ retcode = 0;
+ } else {
+ const struct timezone tz = { minuteswest, 0 };
+
+ rc = settimeofday(&tv, &tz);
+ if (rc) {
+ if (errno == EPERM) {
+ warnx(_
+ ("Must be superuser to set system clock."));
+ retcode = EX_NOPERM;
+ } else {
+ warn(_("settimeofday() failed"));
+ retcode = 1;
+ }
+ } else
+ retcode = 0;
+ }
+ }
+ return retcode;
+}
+
+/*
+ * Reset the System Clock from local time to UTC, based on its current value
+ * and the timezone unless universal is TRUE.
+ *
+ * Also set the kernel time zone value to the value indicated by the TZ
+ * environment variable and/or /usr/lib/zoneinfo/, interpreted as tzset()
+ * would interpret them.
+ *
+ * If 'testing' is true, don't actually update anything -- just say we would
+ * have.
+ */
+static int set_system_clock_timezone(const bool universal, const bool testing)
+{
+ int retcode;
+ struct timeval tv;
+ struct tm *broken;
+ int minuteswest;
+ int rc;
+
+ gettimeofday(&tv, NULL);
+ if (debug) {
+ struct tm broken_time;
+ char ctime_now[200];
+
+ broken_time = *gmtime(&tv.tv_sec);
+ strftime(ctime_now, sizeof(ctime_now), "%Y/%m/%d %H:%M:%S",
+ &broken_time);
+ printf(_("Current system time: %ld = %s\n"), (long)tv.tv_sec,
+ ctime_now);
+ }
+
+ broken = localtime(&tv.tv_sec);
+#ifdef HAVE_TM_GMTOFF
+ minuteswest = -broken->tm_gmtoff / 60; /* GNU extension */
+#else
+ minuteswest = timezone / 60;
+ if (broken->tm_isdst)
+ minuteswest -= 60;
+#endif
+
+ if (debug) {
+ struct tm broken_time;
+ char ctime_now[200];
+
+ gettimeofday(&tv, NULL);
+ if (!universal)
+ tv.tv_sec += minuteswest * 60;
+
+ broken_time = *gmtime(&tv.tv_sec);
+ strftime(ctime_now, sizeof(ctime_now), "%Y/%m/%d %H:%M:%S",
+ &broken_time);
+
+ printf(_("Calling settimeofday:\n"));
+ printf(_("\tUTC: %s\n"), ctime_now);
+ printf(_("\ttv.tv_sec = %ld, tv.tv_usec = %ld\n"),
+ (long)tv.tv_sec, (long)tv.tv_usec);
+ printf(_("\ttz.tz_minuteswest = %d\n"), minuteswest);
+ }
+ if (testing) {
+ printf(_
+ ("Not setting system clock because running in test mode.\n"));
+ retcode = 0;
+ } else {
+ const struct timezone tz = { minuteswest, 0 };
+ const struct timeval *tv_null = NULL;
+
+ rc = settimeofday(tv_null, &tz);
+ if (rc) {
+ if (errno == EPERM) {
+ warnx(_
+ ("Must be superuser to set system clock."));
+ retcode = EX_NOPERM;
+ } else {
+ warn(_("settimeofday() failed"));
+ retcode = 1;
+ }
+ } else
+ retcode = 0;
+ }
+ return retcode;
+}
+
+/*
+ * Update the drift factor in <*adjtime_p> to reflect the fact that the
+ * Hardware Clock was calibrated to <nowtime> and before that was set to
+ * <hclocktime>.
+ *
+ * We record in the adjtime file the time at which we last calibrated the
+ * clock so we can compute the drift rate each time we calibrate.
+ *
+ * EXCEPT: if <hclock_valid> is false, assume Hardware Clock was not set
+ * before to anything meaningful and regular adjustments have not been done,
+ * so don't adjust the drift factor.
+ */
+static void
+adjust_drift_factor(struct adjtime *adjtime_p,
+ const time_t nowtime,
+ const bool hclock_valid,
+ const time_t hclocktime, const double sync_delay)
+{
+ if (!hclock_valid) {
+ if (debug)
+ printf(_("Not adjusting drift factor because the "
+ "Hardware Clock previously contained "
+ "garbage.\n"));
+ } else if (adjtime_p->last_calib_time == 0) {
+ if (debug)
+ printf(_("Not adjusting drift factor because last "
+ "calibration time is zero,\n"
+ "so history is bad and calibration startover "
+ "is necessary.\n"));
+ } else if ((hclocktime - adjtime_p->last_calib_time) < 23 * 60 * 60) {
+ if (debug)
+ printf(_("Not adjusting drift factor because it has "
+ "been less than a day since the last "
+ "calibration.\n"));
+ } else if (adjtime_p->last_calib_time != 0) {
+ /*
+ * At adjustment time we adjust the hardware clock according
+ * to the contents of /etc/adjtime.
+ *
+ * At calibration time we set the hardware clock and update
+ * /etc/adjtime, that is, for each calibration (except the
+ * first) we also do an adjustment.
+ *
+ * We are now at calibration time.
+ *
+ * Let us do computation in doubles. (Floats almost suffice,
+ * but 195 days + 1 second equals 195 days in floats.)
+ */
+ const double sec_per_day = 24.0 * 60.0 * 60.0;
+ double atime_per_htime;
+ double adj_days, cal_days;
+ double exp_drift, unc_drift;
+ double factor_adjust;
+
+ /* Adjusted time units per hardware time unit */
+ atime_per_htime = 1.0 + adjtime_p->drift_factor / sec_per_day;
+
+ /* Days since last adjustment (in hardware clock time) */
+ adj_days = (double)(hclocktime - adjtime_p->last_adj_time)
+ / sec_per_day;
+
+ /* Expected drift (sec) since last adjustment */
+ exp_drift = adj_days * adjtime_p->drift_factor
+ + adjtime_p->not_adjusted;
+
+ /* Uncorrected drift (sec) since last calibration */
+ unc_drift = (double)(nowtime - hclocktime)
+ + sync_delay - exp_drift;
+
+ /* Days since last calibration (in hardware clock time) */
+ cal_days = ((double)(adjtime_p->last_adj_time
+ - adjtime_p->last_calib_time)
+ + adjtime_p->not_adjusted)
+ / (sec_per_day * atime_per_htime) + adj_days;
+
+ /* Amount to add to previous drift factor */
+ factor_adjust = unc_drift / cal_days;
+
+ if (debug)
+ printf(_("Clock drifted %.1f seconds in the past "
+ "%d seconds in spite of a drift factor of "
+ "%f seconds/day.\n"
+ "Adjusting drift factor by %f seconds/day\n"),
+ unc_drift,
+ (int)(nowtime - adjtime_p->last_calib_time),
+ adjtime_p->drift_factor, factor_adjust);
+
+ adjtime_p->drift_factor += factor_adjust;
+ }
+ adjtime_p->last_calib_time = nowtime;
+
+ adjtime_p->last_adj_time = nowtime;
+
+ adjtime_p->not_adjusted = 0;
+
+ adjtime_p->dirty = TRUE;
+}
+
+/*
+ * Do the drift adjustment calculation.
+ *
+ * The way we have to set the clock, we need the adjustment in two parts:
+ *
+ * 1) an integer number of seconds (return as *adjustment_p)
+ * 2) a positive fraction of a second (less than 1) (return as *retro_p)
+ *
+ * The sum of these two values is the adjustment needed. Positive means to
+ * advance the clock or insert seconds. Negative means to retard the clock
+ * or remove seconds.
+ */
+static void
+calculate_adjustment(const double factor,
+ const time_t last_time,
+ const double not_adjusted,
+ const time_t systime, int *adjustment_p, double *retro_p)
+{
+ double exact_adjustment;
+
+ exact_adjustment =
+ ((double)(systime - last_time)) * factor / (24 * 60 * 60)
+ + not_adjusted;
+ *adjustment_p = FLOOR(exact_adjustment);
+
+ *retro_p = exact_adjustment - (double)*adjustment_p;
+ if (debug) {
+ printf(_("Time since last adjustment is %d seconds\n"),
+ (int)(systime - last_time));
+ printf(_("Need to insert %d seconds and refer time back "
+ "%.6f seconds ago\n"), *adjustment_p, *retro_p);
+ }
+}
+
+/*
+ * Write the contents of the <adjtime> structure to its disk file.
+ *
+ * But if the contents are clean (unchanged since read from disk), don't
+ * bother.
+ */
+static void save_adjtime(const struct adjtime adjtime, const bool testing)
+{
+ char newfile[412]; /* Stuff to write to disk file */
+
+ if (adjtime.dirty) {
+ /*
+ * snprintf is not always available, but this is safe as
+ * long as libc does not use more than 100 positions for %ld
+ * or %f
+ */
+ sprintf(newfile, "%f %ld %f\n%ld\n%s\n",
+ adjtime.drift_factor,
+ (long)adjtime.last_adj_time,
+ adjtime.not_adjusted,
+ (long)adjtime.last_calib_time,
+ (adjtime.local_utc == UTC) ? "UTC" : "LOCAL");
+
+ if (testing) {
+ printf(_
+ ("Not updating adjtime file because of testing mode.\n"));
+ printf(_("Would have written the following to %s:\n%s"),
+ adj_file_name, newfile);
+ } else {
+ FILE *adjfile;
+ int err = 0;
+
+ adjfile = fopen(adj_file_name, "w");
+ if (adjfile == NULL) {
+ warn(_
+ ("Could not open file with the clock adjustment parameters "
+ "in it (%s) for writing"), adj_file_name);
+ err = 1;
+ } else {
+ if (fputs(newfile, adjfile) < 0) {
+ warn(_
+ ("Could not update file with the clock adjustment "
+ "parameters (%s) in it"),
+ adj_file_name);
+ err = 1;
+ }
+ if (close_stream(adjfile) != 0) {
+ warn(_
+ ("Could not update file with the clock adjustment "
+ "parameters (%s) in it"),
+ adj_file_name);
+ err = 1;
+ }
+ }
+ if (err)
+ warnx(_
+ ("Drift adjustment parameters not updated."));
+ }
+ }
+}
+
+/*
+ * Do the adjustment requested, by 1) setting the Hardware Clock (if
+ * necessary), and 2) updating the last-adjusted time in the adjtime
+ * structure.
+ *
+ * Do not update anything if the Hardware Clock does not currently present a
+ * valid time.
+ *
+ * Arguments <factor> and <last_time> are current values from the adjtime
+ * file.
+ *
+ * <hclock_valid> means the Hardware Clock contains a valid time, and that
+ * time is <hclocktime>.
+ *
+ * <read_time> is the current system time (to be precise, it is the system
+ * time at the time <hclocktime> was read, which due to computational delay
+ * could be a short time ago).
+ *
+ * <universal>: the Hardware Clock is kept in UTC.
+ *
+ * <testing>: We are running in test mode (no updating of clock).
+ *
+ * We do not bother to update the clock if the adjustment would be less than
+ * one second. This is to avoid cumulative error and needless CPU hogging
+ * (remember we use an infinite loop for some timing) if the user runs us
+ * frequently.
+ */
+static void
+do_adjustment(struct adjtime *adjtime_p,
+ const bool hclock_valid, const time_t hclocktime,
+ const struct timeval read_time,
+ const bool universal, const bool testing)
+{
+ if (!hclock_valid) {
+ warnx(_("The Hardware Clock does not contain a valid time, "
+ "so we cannot adjust it."));
+ adjtime_p->last_calib_time = 0; /* calibration startover is required */
+ adjtime_p->last_adj_time = 0;
+ adjtime_p->not_adjusted = 0;
+ adjtime_p->dirty = TRUE;
+ } else if (adjtime_p->last_adj_time == 0) {
+ if (debug)
+ printf(_
+ ("Not setting clock because last adjustment time is zero, "
+ "so history is bad."));
+ } else {
+ int adjustment;
+ /* Number of seconds we must insert in the Hardware Clock */
+ double retro;
+ /*
+ * Fraction of second we have to remove from clock after
+ * inserting <adjustment> whole seconds.
+ */
+ calculate_adjustment(adjtime_p->drift_factor,
+ adjtime_p->last_adj_time,
+ adjtime_p->not_adjusted,
+ hclocktime, &adjustment, &retro);
+ if (adjustment > 0 || adjustment < -1) {
+ set_hardware_clock_exact(hclocktime + adjustment,
+ time_inc(read_time, -retro),
+ universal, testing);
+ adjtime_p->last_adj_time = hclocktime + adjustment;
+ adjtime_p->not_adjusted = 0;
+ adjtime_p->dirty = TRUE;
+ } else if (debug)
+ printf(_("Needed adjustment is less than one second, "
+ "so not setting clock.\n"));
+ }
+}
+
+static void determine_clock_access_method(const bool user_requests_ISA)
+{
+ ur = NULL;
+
+ if (user_requests_ISA)
+ ur = probe_for_cmos_clock();
+
+#ifdef __linux__
+ if (!ur)
+ ur = probe_for_rtc_clock();
+#endif
+
+ if (!ur)
+ ur = probe_for_kd_clock();
+
+ if (!ur && !user_requests_ISA)
+ ur = probe_for_cmos_clock();
+
+ if (debug) {
+ if (ur)
+ printf(_("Using %s.\n"), ur->interface_name);
+ else
+ printf(_("No usable clock interface found.\n"));
+ }
+}
+
+/*
+ * Do all the normal work of hwclock - read, set clock, etc.
+ *
+ * Issue output to stdout and error message to stderr where appropriate.
+ *
+ * Return rc == 0 if everything went OK, rc != 0 if not.
+ */
+static int
+manipulate_clock(const bool show, const bool adjust, const bool noadjfile,
+ const bool set, const time_t set_time,
+ const bool hctosys, const bool systohc, const bool systz,
+ const struct timeval startup_time,
+ const bool utc, const bool local_opt,
+ const bool testing, const bool predict)
+{
+ /* Contents of the adjtime file, or what they should be. */
+ struct adjtime adjtime;
+ bool universal;
+ /* Set if user lacks necessary authorization to access the clock */
+ bool no_auth;
+ /* The time at which we read the Hardware Clock */
+ struct timeval read_time;
+ /*
+ * The Hardware Clock gives us a valid time, or at
+ * least something close enough to fool mktime().
+ */
+ bool hclock_valid = FALSE;
+ /*
+ * The time the hardware clock had just after we
+ * synchronized to its next clock tick when we
+ * started up. Defined only if hclock_valid is true.
+ */
+ time_t hclocktime = 0;
+ /* local return code */
+ int rc = 0;
+
+ if (!systz && !predict) {
+ no_auth = ur->get_permissions();
+ if (no_auth)
+ return EX_NOPERM;
+ }
+
+ if (!noadjfile
+ && (adjust || set || systohc || (!utc && !local_opt) || predict)) {
+ rc = read_adjtime(&adjtime);
+ if (rc)
+ return rc;
+ } else {
+ /* A little trick to avoid reading the file if we don't have to */
+ adjtime.dirty = FALSE;
+ }
+
+ universal = hw_clock_is_utc(utc, local_opt, adjtime);
+
+ if ((set || systohc || adjust) &&
+ (adjtime.local_utc == UTC) != universal) {
+ adjtime.local_utc = universal ? UTC : LOCAL;
+ adjtime.dirty = TRUE;
+ }
+
+ if (show || adjust || hctosys || (!noadjfile && !systz && !predict)) {
+ /* data from HW-clock are required */
+ rc = synchronize_to_clock_tick();
+
+ /*
+ * 2 = synchronization timeout. We don't
+ * error out if the user is attempting to
+ * set the RTC - the RTC could be
+ * functioning but contain invalid time data
+ * so we still want to allow a user to set
+ * the RTC time.
+ */
+ if (rc && rc != 2 && !set && !systohc)
+ return EX_IOERR;
+ gettimeofday(&read_time, NULL);
+
+ /*
+ * If we can't synchronize to a clock tick,
+ * we likely can't read from the RTC so
+ * don't bother reading it again.
+ */
+ if (!rc) {
+ rc = read_hardware_clock(universal,
+ &hclock_valid, &hclocktime);
+ if (rc && !set && !systohc)
+ return EX_IOERR;
+ }
+ }
+
+ if (show) {
+ display_time(hclock_valid, hclocktime,
+ time_diff(read_time, startup_time));
+ } else if (set) {
+ set_hardware_clock_exact(set_time, startup_time,
+ universal, testing);
+ if (!noadjfile)
+ adjust_drift_factor(&adjtime, set_time,
+ hclock_valid,
+ hclocktime,
+ time_diff(read_time, startup_time));
+ } else if (adjust) {
+ do_adjustment(&adjtime, hclock_valid,
+ hclocktime, read_time, universal, testing);
+ } else if (systohc) {
+ struct timeval nowtime, reftime;
+ /*
+ * We can only set_hardware_clock_exact to a
+ * whole seconds time, so we set it with
+ * reference to the most recent whole
+ * seconds time.
+ */
+ gettimeofday(&nowtime, NULL);
+ reftime.tv_sec = nowtime.tv_sec;
+ reftime.tv_usec = 0;
+ set_hardware_clock_exact((time_t)
+ reftime.tv_sec,
+ reftime, universal, testing);
+ if (!noadjfile)
+ adjust_drift_factor(&adjtime, (time_t)
+ reftime.tv_sec,
+ hclock_valid, hclocktime, (double)
+ read_time.tv_usec / 1E6);
+ } else if (hctosys) {
+ rc = set_system_clock(hclock_valid, hclocktime, testing);
+ if (rc) {
+ printf(_("Unable to set system clock.\n"));
+ return rc;
+ }
+ } else if (systz) {
+ rc = set_system_clock_timezone(universal, testing);
+ if (rc) {
+ printf(_("Unable to set system clock.\n"));
+ return rc;
+ }
+ } else if (predict) {
+ int adjustment;
+ double retro;
+
+ calculate_adjustment(adjtime.drift_factor,
+ adjtime.last_adj_time,
+ adjtime.not_adjusted,
+ set_time, &adjustment, &retro);
+ if (debug) {
+ printf(_
+ ("At %ld seconds after 1969, RTC is predicted to read %ld seconds after 1969.\n"),
+ set_time, set_time + adjustment);
+ }
+ display_time(TRUE, set_time + adjustment, -retro);
+ }
+ if (!noadjfile)
+ save_adjtime(adjtime, testing);
+ return 0;
+}
+
+/*
+ * Get or set the Hardware Clock epoch value in the kernel, as appropriate.
+ * <getepoch>, <setepoch>, and <epoch> are hwclock invocation options.
+ *
+ * <epoch> == -1 if the user did not specify an "epoch" option.
+ */
+#ifdef __linux__
+/*
+ * Maintenance note: This should work on non-Alpha machines, but the
+ * evidence today (98.03.04) indicates that the kernel only keeps the epoch
+ * value on Alphas. If that is ever fixed, this function should be changed.
+ */
+# ifndef __alpha__
+static void
+manipulate_epoch(const bool getepoch __attribute__ ((__unused__)),
+ const bool setepoch __attribute__ ((__unused__)),
+ const unsigned long epoch_opt __attribute__ ((__unused__)),
+ const bool testing __attribute__ ((__unused__)))
+{
+ warnx(_("The kernel keeps an epoch value for the Hardware Clock "
+ "only on an Alpha machine.\nThis copy of hwclock was built for "
+ "a machine other than Alpha\n(and thus is presumably not running "
+ "on an Alpha now). No action taken."));
+}
+# else
+static void
+manipulate_epoch(const bool getepoch,
+ const bool setepoch,
+ const unsigned long epoch_opt,
+ const bool testing)
+{
+ if (getepoch) {
+ unsigned long epoch;
+
+ if (get_epoch_rtc(&epoch, 0))
+ warnx(_
+ ("Unable to get the epoch value from the kernel."));
+ else
+ printf(_("Kernel is assuming an epoch value of %lu\n"),
+ epoch);
+ } else if (setepoch) {
+ if (epoch_opt == -1)
+ warnx(_
+ ("To set the epoch value, you must use the 'epoch' "
+ "option to tell to what value to set it."));
+ else if (testing)
+ printf(_
+ ("Not setting the epoch to %d - testing only.\n"),
+ epoch_opt);
+ else if (set_epoch_rtc(epoch_opt))
+ printf(_
+ ("Unable to set the epoch value in the kernel.\n"));
+ }
+}
+# endif /* __alpha__ */
+#endif /* __linux__ */
+
+static void out_version(void)
+{
+ printf(_("%s from %s\n"), program_invocation_short_name, PACKAGE_STRING);
+}
+
+/*
+ * usage - Output (error and) usage information
+ *
+ * This function is called both directly from main to show usage information
+ * and as fatal function from shhopt if some argument is not understood. In
+ * case of normal usage info FMT should be NULL. In that case the info is
+ * printed to stdout. If FMT is given usage will act like fprintf( stderr,
+ * fmt, ... ), show a usage information and terminate the program
+ * afterwards.
+ */
+static void usage(const char *fmt, ...)
+{
+ FILE *usageto;
+ va_list ap;
+
+ usageto = fmt ? stderr : stdout;
+
+ fputs(_("\nUsage:\n"), usageto);
+ fputs(_(" hwclock [function] [option...]\n"), usageto);
+
+ fputs(_("\nFunctions:\n"), usageto);
+ fputs(_(" -h, --help show this help text and exit\n"
+ " -r, --show read hardware clock and print result\n"
+ " --set set the RTC to the time given with --date\n"), usageto);
+ fputs(_(" -s, --hctosys set the system time from the hardware clock\n"
+ " -w, --systohc set the hardware clock from the current system time\n"
+ " --systz set the system time based on the current timezone\n"
+ " --adjust adjust the RTC to account for systematic drift since\n"
+ " the clock was last set or adjusted\n"), usageto);
+#ifdef __linux__
+ fputs(_(" --getepoch print out the kernel's hardware clock epoch value\n"
+ " --setepoch set the kernel's hardware clock epoch value to the \n"
+ " value given with --epoch\n"), usageto);
+#endif
+ fputs(_(" --predict predict RTC reading at time given with --date\n"
+ " -V, --version display version information and exit\n"), usageto);
+
+ fputs(_("\nOptions:\n"), usageto);
+ fputs(_(" -u, --utc the hardware clock is kept in UTC\n"
+ " --localtime the hardware clock is kept in local time\n"), usageto);
+#ifdef __linux__
+ fputs(_(" -f, --rtc <file> special /dev/... file to use instead of default\n"), usageto);
+#endif
+ fprintf(usageto, _(
+ " --directisa access the ISA bus directly instead of %s\n"
+ " --badyear ignore RTC's year because the BIOS is broken\n"
+ " --date <time> specifies the time to which to set the hardware clock\n"
+ " --epoch <year> specifies the year which is the beginning of the\n"
+ " hardware clock's epoch value\n"), _PATH_RTC_DEV);
+ fprintf(usageto, _(
+ " --noadjfile do not access %s; this requires the use of\n"
+ " either --utc or --localtime\n"
+ " --adjfile <file> specifies the path to the adjust file;\n"
+ " the default is %s\n"), _PATH_ADJPATH, _PATH_ADJPATH);
+ fputs(_(" --test do not update anything, just show what would happen\n"
+ " -D, --debug debugging mode\n" "\n"), usageto);
+#ifdef __alpha__
+ fputs(_(" -J|--jensen, -A|--arc, -S|--srm, -F|--funky-toy\n"
+ " tell hwclock the type of Alpha you have (see hwclock(8))\n"
+ "\n"), usageto);
+#endif
+
+ if (fmt) {
+ va_start(ap, fmt);
+ vfprintf(usageto, fmt, ap);
+ va_end(ap);
+ }
+
+ fflush(usageto);
+ hwclock_exit(fmt ? EX_USAGE : EX_OK);
+}
+
+/*
+ * Returns:
+ * EX_USAGE: bad invocation
+ * EX_NOPERM: no permission
+ * EX_OSFILE: cannot open /dev/rtc or /etc/adjtime
+ * EX_IOERR: ioctl error getting or setting the time
+ * 0: OK (or not)
+ * 1: failure
+ */
+int main(int argc, char **argv)
+{
+ struct timeval startup_time;
+ /*
+ * The time we started up, in seconds into the epoch, including
+ * fractions.
+ */
+ time_t set_time = 0; /* Time to which user said to set Hardware Clock */
+
+ bool permitted; /* User is permitted to do the function */
+ int rc, c;
+
+ /* Variables set by various options; show may also be set later */
+ /* The options debug, badyear and epoch_option are global */
+ bool show, set, systohc, hctosys, systz, adjust, getepoch, setepoch,
+ predict;
+ bool utc, testing, local_opt, noadjfile, directisa;
+ char *date_opt;
+#ifdef __alpha__
+ bool ARCconsole, Jensen, SRM, funky_toy;
+#endif
+ /* Long only options. */
+ enum {
+ OPT_ADJFILE = CHAR_MAX + 1,
+ OPT_BADYEAR,
+ OPT_DATE,
+ OPT_DIRECTISA,
+ OPT_EPOCH,
+ OPT_GETEPOCH,
+ OPT_LOCALTIME,
+ OPT_NOADJFILE,
+ OPT_PREDICT_HC,
+ OPT_SET,
+ OPT_SETEPOCH,
+ OPT_SYSTZ,
+ OPT_TEST
+ };
+
+ static const struct option longopts[] = {
+ {"adjust", 0, 0, 'a'},
+ {"help", 0, 0, 'h'},
+ {"show", 0, 0, 'r'},
+ {"hctosys", 0, 0, 's'},
+ {"utc", 0, 0, 'u'},
+ {"version", 0, 0, 'v'},
+ {"systohc", 0, 0, 'w'},
+ {"debug", 0, 0, 'D'},
+#ifdef __alpha__
+ {"ARC", 0, 0, 'A'},
+ {"arc", 0, 0, 'A'},
+ {"Jensen", 0, 0, 'J'},
+ {"jensen", 0, 0, 'J'},
+ {"SRM", 0, 0, 'S'},
+ {"srm", 0, 0, 'S'},
+ {"funky-toy", 0, 0, 'F'},
+#endif
+ {"set", 0, 0, OPT_SET},
+#ifdef __linux__
+ {"getepoch", 0, 0, OPT_GETEPOCH},
+ {"setepoch", 0, 0, OPT_SETEPOCH},
+#endif
+ {"noadjfile", 0, 0, OPT_NOADJFILE},
+ {"localtime", 0, 0, OPT_LOCALTIME},
+ {"badyear", 0, 0, OPT_BADYEAR},
+ {"directisa", 0, 0, OPT_DIRECTISA},
+ {"test", 0, 0, OPT_TEST},
+ {"date", 1, 0, OPT_DATE},
+ {"epoch", 1, 0, OPT_EPOCH},
+#ifdef __linux__
+ {"rtc", 1, 0, 'f'},
+#endif
+ {"adjfile", 1, 0, OPT_ADJFILE},
+ {"systz", 0, 0, OPT_SYSTZ},
+ {"predict-hc", 0, 0, OPT_PREDICT_HC},
+ {NULL, 0, NULL, 0}
+ };
+
+ static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */
+ { 'a','r','s','w',
+ OPT_GETEPOCH, OPT_PREDICT_HC, OPT_SET,
+ OPT_SETEPOCH, OPT_SYSTZ },
+ { 'u', OPT_LOCALTIME},
+ { OPT_ADJFILE, OPT_NOADJFILE },
+ { 0 }
+ };
+ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+
+ /* Remember what time we were invoked */
+ gettimeofday(&startup_time, NULL);
+
+#ifdef HAVE_LIBAUDIT
+ hwaudit_fd = audit_open();
+ if (hwaudit_fd < 0 && !(errno == EINVAL || errno == EPROTONOSUPPORT ||
+ errno == EAFNOSUPPORT)) {
+ /*
+ * You get these error codes only when the kernel doesn't
+ * have audit compiled in.
+ */
+ warnx(_("Unable to connect to audit system"));
+ return EX_NOPERM;
+ }
+#endif
+ setlocale(LC_ALL, "");
+#ifdef LC_NUMERIC
+ /*
+ * We need LC_CTYPE and LC_TIME and LC_MESSAGES, but must avoid
+ * LC_NUMERIC since it gives problems when we write to /etc/adjtime.
+ * - gqueri@mail.dotcom.fr
+ */
+ setlocale(LC_NUMERIC, "C");
+#endif
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ /* Set option defaults */
+ show = set = systohc = hctosys = systz = adjust = noadjfile = predict =
+ FALSE;
+ getepoch = setepoch = utc = local_opt = directisa = testing = debug = FALSE;
+#ifdef __alpha__
+ ARCconsole = Jensen = SRM = funky_toy = badyear = FALSE;
+#endif
+ date_opt = NULL;
+
+ while ((c = getopt_long(argc, argv,
+ "?hvVDarsuwAJSFf:", longopts, NULL)) != -1) {
+
+ err_exclusive_options(c, longopts, excl, excl_st);
+
+ switch (c) {
+ case 'D':
+ debug = TRUE;
+ break;
+ case 'a':
+ adjust = TRUE;
+ break;
+ case 'r':
+ show = TRUE;
+ break;
+ case 's':
+ hctosys = TRUE;
+ break;
+ case 'u':
+ utc = TRUE;
+ break;
+ case 'w':
+ systohc = TRUE;
+ break;
+#ifdef __alpha__
+ case 'A':
+ ARCconsole = TRUE;
+ break;
+ case 'J':
+ Jensen = TRUE;
+ break;
+ case 'S':
+ SRM = TRUE;
+ break;
+ case 'F':
+ funky_toy = TRUE;
+ break;
+#endif
+ case OPT_SET:
+ set = TRUE;
+ break;
+#ifdef __linux__
+ case OPT_GETEPOCH:
+ getepoch = TRUE;
+ break;
+ case OPT_SETEPOCH:
+ setepoch = TRUE;
+ break;
+#endif
+ case OPT_NOADJFILE:
+ noadjfile = TRUE;
+ break;
+ case OPT_LOCALTIME:
+ local_opt = TRUE; /* --localtime */
+ break;
+ case OPT_BADYEAR:
+ badyear = TRUE;
+ break;
+ case OPT_DIRECTISA:
+ directisa = TRUE;
+ break;
+ case OPT_TEST:
+ testing = TRUE; /* --test */
+ break;
+ case OPT_DATE:
+ date_opt = optarg; /* --date */
+ break;
+ case OPT_EPOCH:
+ epoch_option = /* --epoch */
+ strtoul_or_err(optarg, _("invalid epoch argument"));
+ break;
+ case OPT_ADJFILE:
+ adj_file_name = optarg; /* --adjfile */
+ break;
+ case OPT_SYSTZ:
+ systz = TRUE; /* --systz */
+ break;
+ case OPT_PREDICT_HC:
+ predict = TRUE; /* --predict-hc */
+ break;
+#ifdef __linux__
+ case 'f':
+ rtc_dev_name = optarg; /* --rtc */
+ break;
+#endif
+ case 'v': /* --version */
+ case 'V':
+ out_version();
+ return 0;
+ case 'h': /* --help */
+ case '?':
+ default:
+ usage(NULL);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+#ifdef HAVE_LIBAUDIT
+ if (testing != TRUE) {
+ if (adjust == TRUE || hctosys == TRUE || systohc == TRUE ||
+ set == TRUE || setepoch == TRUE) {
+ hwaudit_on = TRUE;
+ }
+ }
+#endif
+ if (argc > 0) {
+ usage(_("%s takes no non-option arguments. "
+ "You supplied %d.\n"), program_invocation_short_name,
+ argc);
+ }
+
+ if (!adj_file_name)
+ adj_file_name = _PATH_ADJPATH;
+
+ if (noadjfile && !utc && !local_opt) {
+ warnx(_("With --noadjfile, you must specify "
+ "either --utc or --localtime"));
+ hwclock_exit(EX_USAGE);
+ }
+#ifdef __alpha__
+ set_cmos_epoch(ARCconsole, SRM);
+ set_cmos_access(Jensen, funky_toy);
+#endif
+
+ if (set || predict) {
+ rc = interpret_date_string(date_opt, &set_time);
+ /* (time-consuming) */
+ if (rc != 0) {
+ warnx(_("No usable set-to time. "
+ "Cannot set clock."));
+ hwclock_exit(EX_USAGE);
+ }
+ }
+
+ if (!(show | set | systohc | hctosys | systz | adjust | getepoch
+ | setepoch | predict))
+ show = 1; /* default to show */
+
+ if (getuid() == 0)
+ permitted = TRUE;
+ else {
+ /* program is designed to run setuid (in some situations) */
+ if (set || systohc || adjust) {
+ warnx(_("Sorry, only the superuser can change "
+ "the Hardware Clock."));
+ permitted = FALSE;
+ } else if (systz || hctosys) {
+ warnx(_("Sorry, only the superuser can change "
+ "the System Clock."));
+ permitted = FALSE;
+ } else if (setepoch) {
+ warnx(_("Sorry, only the superuser can change the "
+ "Hardware Clock epoch in the kernel."));
+ permitted = FALSE;
+ } else
+ permitted = TRUE;
+ }
+
+ if (!permitted)
+ hwclock_exit(EX_NOPERM);
+
+#ifdef __linux__
+ if (getepoch || setepoch) {
+ manipulate_epoch(getepoch, setepoch, epoch_option, testing);
+ hwclock_exit(EX_OK);
+ }
+#endif
+
+ if (debug)
+ out_version();
+
+ if (!systz && !predict) {
+ determine_clock_access_method(directisa);
+ if (!ur) {
+ warnx(_("Cannot access the Hardware Clock via "
+ "any known method."));
+ if (!debug)
+ warnx(_("Use the --debug option to see the "
+ "details of our search for an access "
+ "method."));
+ hwclock_exit(EX_SOFTWARE);
+ }
+ }
+
+ rc = manipulate_clock(show, adjust, noadjfile, set, set_time,
+ hctosys, systohc, systz, startup_time, utc,
+ local_opt, testing, predict);
+ hwclock_exit(rc);
+ return rc; /* Not reached */
+}
+
+#ifdef HAVE_LIBAUDIT
+/*
+ * hwclock_exit calls either this function or plain exit depending
+ * HAVE_LIBAUDIT see also clock.h
+ */
+void __attribute__((__noreturn__)) hwaudit_exit(int status)
+{
+ if (hwaudit_on) {
+ audit_log_user_message(hwaudit_fd, AUDIT_USYS_CONFIG,
+ "changing system time", NULL, NULL, NULL,
+ status ? 0 : 1);
+ close(hwaudit_fd);
+ }
+ exit(status);
+}
+#endif
+
+/*
+ * History of this program:
+ *
+ * 98.08.12 BJH Version 2.4
+ *
+ * Don't use century byte from Hardware Clock. Add comments telling why.
+ *
+ * 98.06.20 BJH Version 2.3.
+ *
+ * Make --hctosys set the kernel timezone from TZ environment variable
+ * and/or /usr/lib/zoneinfo. From Klaus Ripke (klaus@ripke.com).
+ *
+ * 98.03.05 BJH. Version 2.2.
+ *
+ * Add --getepoch and --setepoch.
+ *
+ * Fix some word length things so it works on Alpha.
+ *
+ * Make it work when /dev/rtc doesn't have the interrupt functions. In this
+ * case, busywait for the top of a second instead of blocking and waiting
+ * for the update complete interrupt.
+ *
+ * Fix a bunch of bugs too numerous to mention.
+ *
+ * 97.06.01: BJH. Version 2.1. Read and write the century byte (Byte 50) of
+ * the ISA Hardware Clock when using direct ISA I/O. Problem discovered by
+ * job (jei@iclnl.icl.nl).
+ *
+ * Use the rtc clock access method in preference to the KDGHWCLK method.
+ * Problem discovered by Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>.
+ *
+ * November 1996: Version 2.0.1. Modifications by Nicolai Langfeldt
+ * (janl@math.uio.no) to make it compile on linux 1.2 machines as well as
+ * more recent versions of the kernel. Introduced the NO_CLOCK access method
+ * and wrote feature test code to detect absence of rtc headers.
+ *
+ ***************************************************************************
+ * Maintenance notes
+ *
+ * To compile this, you must use GNU compiler optimization (-O option) in
+ * order to make the "extern inline" functions from asm/io.h (inb(), etc.)
+ * compile. If you don't optimize, which means the compiler will generate no
+ * inline functions, the references to these functions in this program will
+ * be compiled as external references. Since you probably won't be linking
+ * with any functions by these names, you will have unresolved external
+ * references when you link.
+ *
+ * The program is designed to run setuid superuser, since we need to be able
+ * to do direct I/O. (More to the point: we need permission to execute the
+ * iopl() system call). (However, if you use one of the methods other than
+ * direct ISA I/O to access the clock, no setuid is required).
+ *
+ * Here's some info on how we must deal with the time that elapses while
+ * this program runs: There are two major delays as we run:
+ *
+ * 1) Waiting up to 1 second for a transition of the Hardware Clock so
+ * we are synchronized to the Hardware Clock.
+ * 2) Running the "date" program to interpret the value of our --date
+ * option.
+ *
+ * Reading the /etc/adjtime file is the next biggest source of delay and
+ * uncertainty.
+ *
+ * The user wants to know what time it was at the moment he invoked us, not
+ * some arbitrary time later. And in setting the clock, he is giving us the
+ * time at the moment we are invoked, so if we set the clock some time
+ * later, we have to add some time to that.
+ *
+ * So we check the system time as soon as we start up, then run "date" and
+ * do file I/O if necessary, then wait to synchronize with a Hardware Clock
+ * edge, then check the system time again to see how much time we spent. We
+ * immediately read the clock then and (if appropriate) report that time,
+ * and additionally, the delay we measured.
+ *
+ * If we're setting the clock to a time given by the user, we wait some more
+ * so that the total delay is an integral number of seconds, then set the
+ * Hardware Clock to the time the user requested plus that integral number
+ * of seconds. N.B. The Hardware Clock can only be set in integral seconds.
+ *
+ * If we're setting the clock to the system clock value, we wait for the
+ * system clock to reach the top of a second, and then set the Hardware
+ * Clock to the system clock's value.
+ *
+ * Here's an interesting point about setting the Hardware Clock: On my
+ * machine, when you set it, it sets to that precise time. But one can
+ * imagine another clock whose update oscillator marches on a steady one
+ * second period, so updating the clock between any two oscillator ticks is
+ * the same as updating it right at the earlier tick. To avoid any
+ * complications that might cause, we set the clock as soon as possible
+ * after an oscillator tick.
+ *
+ * About synchronizing to the Hardware Clock when reading the time: The
+ * precision of the Hardware Clock counters themselves is one second. You
+ * can't read the counters and find out that is 12:01:02.5. But if you
+ * consider the location in time of the counter's ticks as part of its
+ * value, then its precision is as infinite as time is continuous! What I'm
+ * saying is this: To find out the _exact_ time in the hardware clock, we
+ * wait until the next clock tick (the next time the second counter changes)
+ * and measure how long we had to wait. We then read the value of the clock
+ * counters and subtract the wait time and we know precisely what time it
+ * was when we set out to query the time.
+ *
+ * hwclock uses this method, and considers the Hardware Clock to have
+ * infinite precision.
+ *
+ * TODO: Enhancements needed:
+ *
+ * - When waiting for whole second boundary in set_hardware_clock_exact,
+ * fail if we miss the goal by more than .1 second, as could happen if we
+ * get pre-empted (by the kernel dispatcher).
+ */
diff --git a/sys-utils/hwclock.h b/sys-utils/hwclock.h
new file mode 100644
index 0000000..175a6d1
--- /dev/null
+++ b/sys-utils/hwclock.h
@@ -0,0 +1,47 @@
+#ifndef HWCLOCK_CLOCK_H
+#define HWCLOCK_CLOCK_H
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "c.h"
+
+struct clock_ops {
+ char *interface_name;
+ int (*get_permissions) (void);
+ int (*read_hardware_clock) (struct tm * tm);
+ int (*set_hardware_clock) (const struct tm * tm);
+ int (*synchronize_to_clock_tick) (void);
+};
+
+extern struct clock_ops *probe_for_cmos_clock(void);
+extern struct clock_ops *probe_for_rtc_clock(void);
+extern struct clock_ops *probe_for_kd_clock(void);
+
+typedef int bool;
+
+/* hwclock.c */
+extern char *progname;
+extern int debug;
+extern unsigned long epoch_option;
+extern double time_diff(struct timeval subtrahend, struct timeval subtractor);
+/* cmos.c */
+extern void set_cmos_epoch(int ARCconsole, int SRM);
+extern void set_cmos_access(int Jensen, int funky_toy);
+
+/* rtc.c */
+extern int get_epoch_rtc(unsigned long *epoch, int silent);
+extern int set_epoch_rtc(unsigned long epoch);
+extern char *rtc_dev_name;
+
+#ifdef HAVE_LIBAUDIT
+extern void hwaudit_exit(int status);
+# define hwclock_exit(_status) hwaudit_exit(_status)
+#else
+# define hwclock_exit(_status) exit(_status)
+#endif
+
+#endif /* HWCLOCK_CLOCK_H */
diff --git a/sys-utils/ipcmk.1 b/sys-utils/ipcmk.1
new file mode 100644
index 0000000..0900a19
--- /dev/null
+++ b/sys-utils/ipcmk.1
@@ -0,0 +1,51 @@
+.\" Copyright 2008 Hayden A. James (hayden.james@gmail.com)
+.\" May be distributed under the GNU General Public License
+.TH IPCMK "1" "September 2011" "util-linux" "User Commands"
+.SH "NAME"
+ipcmk \- create various ipc resources
+.SH "SYNOPSIS"
+.B ipcmk
+<resource options>
+[additional options]
+.SH "DESCRIPTION"
+.B ipcmk
+allows you to create shared memory segments, message queues or semaphore arrays.
+.SH "RESOURCE OPTIONS"
+.TP
+Resources may be specified as follows:
+.TP
+\fB\-M\fR, \fB\-\-shmem\fR [\fIsize\fR]
+Shared memory segment of
+.I size
+bytes.
+.TP
+\fB\-S\fR, \fB\-\-semaphore\fR [\fInumber\fR]
+Semaphore array with
+.I number
+of elements.
+.TP
+\fB\-Q\fR, \fB\-\-queue\fR
+Message queue.
+.SH "ADDITIONAL OPTIONS"
+.TP
+\fB\-p\fR, \fB\-\-mode\fR [\fImode\fR]
+Permission for the resource. Default is 0644.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display a short help message and exit.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Output version information and exit.
+.PP
+.SH "SEE ALSO"
+.BR ipcrm (1),
+.BR ipcs (1)
+.SH "AUTHOR"
+.MT hayden.james@gmail.com
+Hayden A. James
+.ME
+.SH "AVAILABILITY"
+The ipcmk 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/sys-utils/ipcmk.c b/sys-utils/ipcmk.c
new file mode 100644
index 0000000..a862ba1
--- /dev/null
+++ b/sys-utils/ipcmk.c
@@ -0,0 +1,160 @@
+/*
+ * ipcmk.c - used to create ad-hoc IPC segments
+ *
+ * Copyright (C) 2008 Hayden A. James (hayden.james@gmail.com)
+ * Copyright (C) 2008 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 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.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+#include <sys/time.h>
+
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "closestream.h"
+
+static key_t create_key(void)
+{
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ srandom(now.tv_usec);
+ return random();
+}
+
+static int create_shm(size_t size, int permission)
+{
+ key_t key = create_key();
+ return shmget(key, size, permission | IPC_CREAT);
+}
+
+static int create_msg(int permission)
+{
+ key_t key = create_key();
+ return msgget(key, permission | IPC_CREAT);
+}
+
+static int create_sem(int nsems, int permission)
+{
+ key_t key = create_key();
+ return semget(key, nsems, permission | IPC_CREAT);
+}
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fprintf(out, USAGE_HEADER);
+ fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
+ fprintf(out, USAGE_OPTIONS);
+
+ fputs(_(" -M, --shmem <size> create shared memory segment of size <size>\n"), out);
+ fputs(_(" -S, --semaphore <nsems> create semaphore array with <nsems> elements\n"), out);
+ fputs(_(" -Q, --queue create message queue\n"), out);
+ fputs(_(" -p, --mode <mode> permission for the resource (default is 0644)\n"), out);
+
+ fprintf(out, USAGE_SEPARATOR);
+ fprintf(out, USAGE_HELP);
+ fprintf(out, USAGE_VERSION);
+ fprintf(out, USAGE_MAN_TAIL("ipcmk(1)"));
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ int permission = 0644;
+ int opt;
+ size_t size = 0;
+ int nsems = 0;
+ int ask_shm = 0, ask_msg = 0, ask_sem = 0;
+ static const struct option longopts[] = {
+ {"shmem", required_argument, NULL, 'M'},
+ {"semaphore", required_argument, NULL, 'S'},
+ {"queue", no_argument, NULL, 'Q'},
+ {"mode", required_argument, NULL, 'p'},
+ {"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((opt = getopt_long(argc, argv, "hM:QS:p:Vh", longopts, NULL)) != -1) {
+ switch(opt) {
+ case 'M':
+ size = strtou64_or_err(optarg, _("failed to parse size"));
+ ask_shm = 1;
+ break;
+ case 'Q':
+ ask_msg = 1;
+ break;
+ case 'S':
+ nsems = strtos32_or_err(optarg, _("failed to parse elements"));
+ ask_sem = 1;
+ break;
+ case 'p':
+ permission = strtoul(optarg, NULL, 8);
+ break;
+ case 'h':
+ usage(stdout);
+ break;
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ default:
+ ask_shm = ask_msg = ask_sem = 0;
+ break;
+ }
+ }
+
+ if(!ask_shm && !ask_msg && !ask_sem)
+ usage(stderr);
+
+ if (ask_shm) {
+ int shmid;
+ if (-1 == (shmid = create_shm(size, permission)))
+ err(EXIT_FAILURE, _("create share memory failed"));
+ else
+ printf(_("Shared memory id: %d\n"), shmid);
+ }
+
+ if (ask_msg) {
+ int msgid;
+ if (-1 == (msgid = create_msg(permission)))
+ err(EXIT_FAILURE, _("create message queue failed"));
+ else
+ printf(_("Message queue id: %d\n"), msgid);
+ }
+
+ if (ask_sem) {
+ int semid;
+ if (-1 == (semid = create_sem(nsems, permission)))
+ err(EXIT_FAILURE, _("create semaphore failed"));
+ else
+ printf(_("Semaphore id: %d\n"), semid);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/sys-utils/ipcrm.1 b/sys-utils/ipcrm.1
new file mode 100644
index 0000000..6cf74a5
--- /dev/null
+++ b/sys-utils/ipcrm.1
@@ -0,0 +1,111 @@
+.\" Copyright 2002 Andre C. Mazzone (linuxdev@karagee.com)
+.\" May be distributed under the GNU General Public License
+.TH IPCRM "1" "September 2011" "util-linux" "User Commands"
+.SH NAME
+ipcrm \- remove a message queue, semaphore set or shared memory id
+.SH SYNOPSIS
+.B ipcrm
+[options]
+.br
+.B ipcrm
+.I <shm|msg|sem> <id>
+[...]
+.SH DESCRIPTION
+.I ipcrm
+removes System V interprocess communication (IPC) objects
+and associated data structures from the system.
+In order to delete such objects, you must be superuser, or
+the creator or owner of the object.
+.PP
+System V IPC objects are of three types: shared memory,
+message queues, and semaphores.
+Deletion of a message queue or semaphore object is immediate
+(regardless of whether any process still holds an IPC
+identifier for the object).
+A shared memory object is only removed
+after all currently attached processes have detached
+.RB ( shmdt (2))
+the object from their virtual address space.
+.PP
+Two syntax styles are supported. The old Linux historical syntax specifies
+a three letter keyword indicating which class of object is to be deleted,
+followed by one or more IPC identifiers for objects of this type.
+.PP
+The SUS-compliant syntax allows the specification of
+zero or more objects of all three types in a single command line,
+with objects specified either by key or by identifier. (See below.)
+Both keys and identifiers may be specified in decimal, hexadecimal
+(specified with an initial '0x' or '0X'), or octal (specified with
+an initial '0').
+.SH OPTIONS
+.TP
+\fB\-M\fR, \fB\-\-shmem\-key\fR \fIshmkey\fR
+removes the shared memorysegment created with
+.I shmkey
+after the last detach is performed.
+.TP
+\fB\-m\fR, \fB\-\-shmem\-id\fR \fIshmid\fR
+removes the shared memory segment identified by
+.I shmid
+after the last detach is performed.
+.TP
+\fB\-Q\fR, \fB\-\-queue\-key\fR \fImsgkey\fR
+removes the message queue created with
+.IR msgkey .
+.TP
+\fB\-q\fR, \fB\-\-queue\-id\fR \fImsgid\fR
+removes the message queue identified by
+.IR msgid .
+.TP
+\fB\-S\fR, \fB\-\-semaphore\-key\fR \fIsemkey\fR
+removes the semaphore created with
+.IR semkey .
+.TP
+\fB\-s\fR, \fB\-\-semaphore\-id\fR \fIsemid\fR
+removes the semaphore identified by
+.IR semid .
+.TP
+\fB-a\fR, \fB\-\-all\fR [\fIshm msg sem\fR]
+Remove all resources. When option argument is provided the removal is
+performed only to for the specified resource types. Warning! Do not use
+.B \-a
+if you are unsure how the software using resources might react on missing
+objects. Some programs create these resources at start up and may not have
+any code to deal unexpected disappearance.
+.LP
+The details of the removes are described in
+.IR msgctl (2),
+.IR shmctl (2),
+and
+.IR semctl (2).
+The identifiers and keys may be found by using
+.IR ipcs (1).
+.SH NOTES
+In its first Linux implementation, ipcrm used the deprecated syntax
+shown in the
+.BR SYNOPSIS .
+Functionality present in other *nix implementations of ipcrm has since
+been added, namely the ability to delete resources by key (not just
+identifier), and to respect the same command-line syntax. For backward
+compatibility the previous syntax is still supported.
+.\" .SH AUTHORS
+.\" Andre C. Mazzone (linuxdev@karagee.com)
+.\" .br
+.\" Krishna Balasubramanian (balasub@cis.ohio-state.edu)
+.SH SEE ALSO
+.nh
+.BR ipcs (1),
+.BR ipcmk (1),
+.BR msgctl (2),
+.BR msgget (2),
+.BR semctl (2),
+.BR semget (2),
+.BR shmctl (2),
+.BR shmdt (2),
+.BR shmget (2),
+.BR ftok (3)
+.SH AVAILABILITY
+The ipcrm 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/sys-utils/ipcrm.c b/sys-utils/ipcrm.c
new file mode 100644
index 0000000..7807bd4
--- /dev/null
+++ b/sys-utils/ipcrm.c
@@ -0,0 +1,410 @@
+/*
+ * krishna balasubramanian 1993
+ *
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ *
+ * 1999-04-02 frank zago
+ * - can now remove several id's in the same call
+ *
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+#include <sys/types.h>
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "closestream.h"
+
+#ifndef HAVE_UNION_SEMUN
+/* according to X/OPEN we have to define it ourselves */
+union semun {
+ int val;
+ struct semid_ds *buf;
+ unsigned short int *array;
+ struct seminfo *__buf;
+};
+#endif
+
+typedef enum type_id {
+ SHM,
+ SEM,
+ MSG,
+ ALL
+} type_id;
+
+static int verbose = 0;
+
+/* print the new usage */
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fprintf(out, USAGE_HEADER);
+ fprintf(out, " %s [options]\n", program_invocation_short_name);
+ fprintf(out, " %s <shm|msg|sem> <id> [...]\n", program_invocation_short_name);
+ fprintf(out, USAGE_OPTIONS);
+ fputs(_(" -m, --shmem-id <id> remove shared memory segment by shmid\n"), out);
+ fputs(_(" -M, --shmem-key <key> remove shared memory segment by key\n"), out);
+ fputs(_(" -q, --queue-id <id> remove message queue by id\n"), out);
+ fputs(_(" -Q, --queue-key <key> remove message queue by key\n"), out);
+ fputs(_(" -s, --semaphore-id <id> remove semaphore by id\n"), out);
+ fputs(_(" -S, --semaphore-key <key> remove semaphore by key\n"), out);
+ fputs(_(" -a, --all[=<shm|msg|sem>] remove all\n"), out);
+ fputs(_(" -v, --verbose explain what is being done\n"), out);
+ fprintf(out, USAGE_SEPARATOR);
+ fprintf(out, USAGE_HELP);
+ fprintf(out, USAGE_VERSION);
+ fprintf(out, USAGE_MAN_TAIL("ipcrm(1)"));
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static int remove_id(int type, int iskey, int id)
+{
+ int ret;
+ char *errmsg;
+ /* needed to delete semaphores */
+ union semun arg;
+ arg.val = 0;
+
+ /* do the removal */
+ switch (type) {
+ case SHM:
+ if (verbose)
+ printf(_("removing shared memory segment id `%d'\n"), id);
+ ret = shmctl(id, IPC_RMID, NULL);
+ break;
+ case MSG:
+ if (verbose)
+ printf(_("removing message queue id `%d'\n"), id);
+ ret = msgctl(id, IPC_RMID, NULL);
+ break;
+ case SEM:
+ if (verbose)
+ printf(_("removing semaphore id `%d'\n"), id);
+ ret = semctl(id, 0, IPC_RMID, arg);
+ break;
+ default:
+ errx(EXIT_FAILURE, "impossible occurred");
+ }
+
+ /* how did the removal go? */
+ if (ret < 0) {
+ switch (errno) {
+ case EACCES:
+ case EPERM:
+ errmsg = iskey ? _("permission denied for key") : _("permission denied for id");
+ break;
+ case EINVAL:
+ errmsg = iskey ? _("invalid key") : _("invalid id");
+ break;
+ case EIDRM:
+ errmsg = iskey ? _("already removed key") : _("already removed id");
+ break;
+ default:
+ err(EXIT_FAILURE, iskey ? _("key failed") : _("id failed"));
+ }
+ warnx("%s (%d)", errmsg, id);
+ return 1;
+ }
+ return 0;
+}
+
+static int remove_arg_list(type_id type, int argc, char **argv)
+{
+ int id;
+ char *end;
+ int nb_errors = 0;
+
+ do {
+ id = strtoul(argv[0], &end, 10);
+ if (*end != 0) {
+ warnx(_("invalid id: %s"), argv[0]);
+ nb_errors++;
+ } else {
+ if (remove_id(type, 0, id))
+ nb_errors++;
+ }
+ argc--;
+ argv++;
+ } while (argc);
+ return (nb_errors);
+}
+
+static int deprecated_main(int argc, char **argv)
+{
+ type_id type;
+
+ if (!strcmp(argv[1], "shm"))
+ type = SHM;
+ else if (!strcmp(argv[1], "msg"))
+ type = MSG;
+ else if (!strcmp(argv[1], "sem"))
+ type = SEM;
+ else
+ return 0;
+
+ if (argc < 3) {
+ warnx(_("not enough arguments"));
+ usage(stderr);
+ }
+
+ if (remove_arg_list(type, argc - 2, &argv[2]))
+ exit(EXIT_FAILURE);
+
+ printf(_("resource(s) deleted\n"));
+ return 1;
+}
+
+static unsigned long strtokey(const char *str, const char *errmesg)
+{
+ unsigned long num;
+ char *end = NULL;
+
+ if (str == NULL || *str == '\0')
+ goto err;
+ errno = 0;
+ /* keys are in hex or decimal */
+ num = strtoul(str, &end, 0);
+
+ if (errno || str == end || (end && *end))
+ goto err;
+
+ return num;
+ err:
+ if (errno)
+ err(EXIT_FAILURE, "%s: '%s'", errmesg, str);
+ else
+ errx(EXIT_FAILURE, "%s: '%s'", errmesg, str);
+ return 0;
+}
+
+static int key_to_id(type_id type, char *optarg)
+{
+ int id;
+ /* keys are in hex or decimal */
+ key_t key = strtokey(optarg, "failed to parse argument");
+ if (key == IPC_PRIVATE) {
+ warnx(_("illegal key (%s)"), optarg);
+ return -1;
+ }
+ switch (type) {
+ case SHM:
+ id = shmget(key, 0, 0);
+ break;
+ case MSG:
+ id = msgget(key, 0);
+ break;
+ case SEM:
+ id = semget(key, 0, 0);
+ break;
+ case ALL:
+ abort();
+ default:
+ errx(EXIT_FAILURE, "impossible occurred");
+ }
+ if (id < 0) {
+ char *errmsg;
+ switch (errno) {
+ case EACCES:
+ errmsg = _("permission denied for key");
+ break;
+ case EIDRM:
+ errmsg = _("already removed key");
+ break;
+ case ENOENT:
+ errmsg = _("invalid key");
+ break;
+ default:
+ err(EXIT_FAILURE, _("key failed"));
+ }
+ warnx("%s (%s)", errmsg, optarg);
+ }
+ return id;
+}
+
+static int remove_all(type_id type)
+{
+ int ret = 0;
+ int id, rm_me, maxid;
+
+ struct shmid_ds shmseg;
+ struct shm_info shm_info;
+
+ struct semid_ds semary;
+ struct seminfo seminfo;
+ union semun arg;
+
+ struct msqid_ds msgque;
+ struct msginfo msginfo;
+
+ if (type == SHM || type == ALL) {
+ maxid =
+ shmctl(0, SHM_INFO, (struct shmid_ds *)(void *)&shm_info);
+ if (maxid < 0)
+ errx(EXIT_FAILURE,
+ _("kernel not configured for shared memory"));
+ for (id = 0; id <= maxid; id++) {
+ rm_me = shmctl(id, SHM_STAT, &shmseg);
+ if (rm_me < 0)
+ continue;
+ ret |= remove_id(SHM, 0, rm_me);
+ }
+ }
+ if (type == SEM || type == ALL) {
+ arg.array = (ushort *) (void *)&seminfo;
+ maxid = semctl(0, 0, SEM_INFO, arg);
+ if (maxid < 0)
+ errx(EXIT_FAILURE,
+ _("kernel not configured for semaphores"));
+ for (id = 0; id <= maxid; id++) {
+ arg.buf = (struct semid_ds *)&semary;
+ rm_me = semctl(id, 0, SEM_STAT, arg);
+ if (rm_me < 0)
+ continue;
+ ret |= remove_id(SEM, 0, rm_me);
+ }
+ }
+ if (type == MSG || type == ALL) {
+ maxid =
+ msgctl(0, MSG_INFO, (struct msqid_ds *)(void *)&msginfo);
+ if (maxid < 0)
+ errx(EXIT_FAILURE,
+ _("kernel not configured for message queues"));
+ for (id = 0; id <= maxid; id++) {
+ rm_me = msgctl(id, MSG_STAT, &msgque);
+ if (rm_me < 0)
+ continue;
+ ret |= remove_id(MSG, 0, rm_me);
+ }
+ }
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ int c;
+ int ret = 0;
+ int id = -1;
+ int iskey;
+ int rm_all = 0;
+ type_id what_all;
+
+ static const struct option longopts[] = {
+ {"shmem-id", required_argument, NULL, 'm'},
+ {"shmem-key", required_argument, NULL, 'M'},
+ {"queue-id", required_argument, NULL, 'q'},
+ {"queue-key", required_argument, NULL, 'Q'},
+ {"semaphore-id", required_argument, NULL, 's'},
+ {"semaphore-key", required_argument, NULL, 'S'},
+ {"all", optional_argument, NULL, 'a'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ /* if the command is executed without parameters, do nothing */
+ if (argc == 1)
+ return 0;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ /* check to see if the command is being invoked in the old way if so
+ * then remove argument list */
+ if (deprecated_main(argc, argv))
+ return EXIT_SUCCESS;
+
+ /* process new syntax to conform with SYSV ipcrm */
+ while((c = getopt_long(argc, argv, "q:m:s:Q:M:S:a::vhV", longopts, NULL)) != -1) {
+ iskey = 0;
+ switch (c) {
+ case 'M':
+ iskey = 1;
+ id = key_to_id(SHM, optarg);
+ if (id < 0) {
+ ret++;
+ break;
+ }
+ case 'm':
+ if (!iskey)
+ id = strtos32_or_err(optarg, _("failed to parse argument"));
+ if (remove_id(SHM, iskey, id))
+ ret++;
+ break;
+ case 'Q':
+ iskey = 1;
+ id = key_to_id(MSG, optarg);
+ if (id < 0) {
+ ret++;
+ break;
+ }
+ case 'q':
+ if (!iskey)
+ id = strtos32_or_err(optarg, _("failed to parse argument"));
+ if (remove_id(MSG, iskey, id))
+ ret++;
+ break;
+ case 'S':
+ iskey = 1;
+ id = key_to_id(SEM, optarg);
+ if (id < 0) {
+ ret++;
+ break;
+ }
+ case 's':
+ if (!iskey)
+ id = strtos32_or_err(optarg, _("failed to parse argument"));
+ if (remove_id(SEM, iskey, id))
+ ret++;
+ break;
+ case 'a':
+ rm_all = 1;
+ if (optarg) {
+ if (!strcmp(optarg, "shm"))
+ what_all = SHM;
+ else if (!strcmp(optarg, "msg"))
+ what_all = MSG;
+ else if (!strcmp(optarg, "sem"))
+ what_all = SEM;
+ else
+ errx(EXIT_FAILURE,
+ _("unknown argument: %s"), optarg);
+ } else {
+ what_all = ALL;
+ }
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'h':
+ usage(stdout);
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ default:
+ usage(stderr);
+ }
+ }
+
+ if (rm_all)
+ if (remove_all(what_all))
+ ret++;
+
+ /* print usage if we still have some arguments left over */
+ if (optind < argc) {
+ warnx(_("unknown argument: %s"), argv[optind]);
+ usage(stderr);
+ }
+
+ return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/sys-utils/ipcs.1 b/sys-utils/ipcs.1
new file mode 100644
index 0000000..7c094c8
--- /dev/null
+++ b/sys-utils/ipcs.1
@@ -0,0 +1,103 @@
+.\" Copyright 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH IPCS "1" "September 2011" "util-linux" "User Commands"
+.SH NAME
+ipcs \- provide information on ipc facilities
+.SH SYNOPSIS
+.B ipcs
+[resource] [...] [output\-format]
+.br
+.B ipcs
+[resource]
+.I \-i id
+.SH DESCRIPTION
+.B ipcs
+provides information on the ipc facilities for which the calling process
+has read access.
+.SH OPTIONS
+.TP
+\fB\-i\fR, \fB\-\-id\fR \fIid\fR
+Print details only on resource identified by
+.IR id .
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display this help and exit.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Output version information and exit.
+.SH "RESOURCE OPTIONS"
+.TP
+\fB\-m\fR, \fB\-\-shmems\fR
+Write information about active shared memory segments.
+.TP
+\fB\-q\fR, \fB\-\-queues\fR
+Write information about active message queues.
+.TP
+\fB\-s\fR, \fB\-\-semaphores\fR
+Write information about active semaphore sets.
+.TP
+\fB\-a\fR, \fB\-\-all\fR
+Write information about all resources (default).
+.SH "OUTPUT FORMATS"
+.TP
+\fB\-t\fR, \fB\-\-time\fR
+Write time information. Time of the last control operation that changed the
+access permissions for all facilities, time of the last
+.I msgsnd()
+and
+.I msgrcv()
+operations on message queues, time of the last
+.I shmat()
+and
+.I shmdt()
+operations on shared memory, and time of the last
+.I semop()
+operation on semaphores.
+.TP
+\fB\-p\fR, \fB\-\-pid\fR
+Show creator and last operations PIDs.
+.TP
+\fB\-c\fR, \fB\-\-creator\fR
+Show creator and owner.
+.TP
+\fB\-l\fR, \fB\-\-limits\fR
+Show resource limits.
+.TP
+\fB\-u\fR, \fB\-\-summary\fR
+Show status summary.
+.SH SEE ALSO
+.BR ipcrm (1),
+.BR ipcmk (1),
+.BR msgrcv (2),
+.BR msgsnd (2),
+.BR semget (2),
+.BR semop (2),
+.BR shmat (2),
+.BR shmdt (2),
+.BR shmget (2)
+.SH CONFORMING TO
+The Linux ipcs utility is not fully compatible to the POSIX ipcs utility.
+The Linux version does not support the
+.B \-b
+and
+.B \-o
+options, but does support the
+.B \-l
+and
+.B \-u
+options not defined by POSIX. The portable application shall not use the
+.BR \-b ,
+.BR \-o ,
+.BR \-l ,
+and
+.B \-u
+options.
+.SH AUTHOR
+.UR balasub@cis.ohio-state.edu
+Krishna Balasubramanian
+.UE
+.SH AVAILABILITY
+The ipcs 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/sys-utils/ipcs.c b/sys-utils/ipcs.c
new file mode 100644
index 0000000..2fbca08
--- /dev/null
+++ b/sys-utils/ipcs.c
@@ -0,0 +1,714 @@
+/* Original author unknown, may be "krishna balasub@cis.ohio-state.edu" */
+/*
+ * Modified Sat Oct 9 10:55:28 1993 for 0.99.13
+ *
+ * Patches from Mike Jagdis (jaggy@purplet.demon.co.uk) applied Wed Feb 8
+ * 12:12:21 1995 by faith@cs.unc.edu to print numeric uids if no passwd file
+ * entry.
+ *
+ * Patch from arnolds@ifns.de (Heinz-Ado Arnolds) applied Mon Jul 1 19:30:41
+ * 1996 by janl@math.uio.no to add code missing in case PID: clauses.
+ *
+ * Patched to display the key field -- hy@picksys.com 12/18/96
+ *
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ */
+
+#include <errno.h>
+#include <features.h>
+#include <getopt.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "nls.h"
+#include "closestream.h"
+
+/*
+ * SHM_DEST and SHM_LOCKED are defined in kernel headers, but inside
+ * #ifdef __KERNEL__ ... #endif
+ */
+#ifndef SHM_DEST
+ /* shm_mode upper byte flags */
+# define SHM_DEST 01000 /* segment will be destroyed on last detach */
+# define SHM_LOCKED 02000 /* segment will not be swapped */
+#endif
+
+/* For older kernels the same holds for the defines below */
+#ifndef MSG_STAT
+# define MSG_STAT 11
+# define MSG_INFO 12
+#endif
+
+#ifndef SHM_STAT
+# define SHM_STAT 13
+# define SHM_INFO 14
+struct shm_info {
+ int used_ids;
+ ulong shm_tot; /* total allocated shm */
+ ulong shm_rss; /* total resident shm */
+ ulong shm_swp; /* total swapped shm */
+ ulong swap_attempts;
+ ulong swap_successes;
+};
+#endif
+
+#ifndef SEM_STAT
+# define SEM_STAT 18
+# define SEM_INFO 19
+#endif
+
+/* Some versions of libc only define IPC_INFO when __USE_GNU is defined. */
+#ifndef IPC_INFO
+# define IPC_INFO 3
+#endif
+
+/*
+ * The last arg of semctl is a union semun, but where is it defined? X/OPEN
+ * tells us to define it ourselves, but until recently Linux include files
+ * would also define it.
+ */
+#ifndef HAVE_UNION_SEMUN
+/* according to X/OPEN we have to define it ourselves */
+union semun {
+ int val;
+ struct semid_ds *buf;
+ unsigned short int *array;
+ struct seminfo *__buf;
+};
+#endif
+
+/*
+ * X/OPEN (Jan 1987) does not define fields key, seq in struct ipc_perm;
+ * glibc-1.09 has no support for sysv ipc.
+ * glibc 2 uses __key, __seq
+ */
+#if defined (__GLIBC__) && __GLIBC__ >= 2
+# define KEY __key
+#else
+# define KEY key
+#endif
+
+#define LIMITS 1
+#define STATUS 2
+#define CREATOR 3
+#define TIME 4
+#define PID 5
+
+void do_shm (char format);
+void do_sem (char format);
+void do_msg (char format);
+void print_shm (int id);
+void print_msg (int id);
+void print_sem (int id);
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fprintf(out, USAGE_HEADER);
+ fprintf(out, " %s [resource ...] [output-format]\n", program_invocation_short_name);
+ fprintf(out, " %s [resource] -i <id>\n", program_invocation_short_name);
+ fprintf(out, USAGE_OPTIONS);
+ fputs(_(" -i, --id <id> print details on resource identified by id\n"), out);
+ fprintf(out, USAGE_HELP);
+ fprintf(out, USAGE_VERSION);
+ fputs(_("\n"), out);
+ fputs(_("Resource options:\n"), out);
+ fputs(_(" -m, --shmems shared memory segments\n"), out);
+ fputs(_(" -q, --queues message queues\n"), out);
+ fputs(_(" -s, --semaphores semaphores\n"), out);
+ fputs(_(" -a, --all all (default)\n"), out);
+ fputs(_("\n"), out);
+ fputs(_("Output format:\n"), out);
+ fputs(_(" -t, --time show attach, detach and change times\n"), out);
+ fputs(_(" -p, --pid show creator and last operations PIDs\n"), out);
+ fputs(_(" -c, --creator show creator and owner\n"), out);
+ fputs(_(" -l, --limits show resource limits\n"), out);
+ fputs(_(" -u, --summary show status summary\n"), out);
+ fprintf(out, USAGE_MAN_TAIL("ipcs(1)"));
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main (int argc, char **argv)
+{
+ int opt, msg = 0, sem = 0, shm = 0, id=0, print=0;
+ char format = 0;
+ static const struct option longopts[] = {
+ {"id", required_argument, NULL, 'i'},
+ {"shmems", no_argument, NULL, 'm'},
+ {"queues", no_argument, NULL, 'q'},
+ {"semaphores", no_argument, NULL, 's'},
+ {"all", no_argument, NULL, 'a'},
+ {"time", no_argument, NULL, 't'},
+ {"pid", no_argument, NULL, 'p'},
+ {"creator", no_argument, NULL, 'c'},
+ {"limits", no_argument, NULL, 'l'},
+ {"summary", no_argument, NULL, 'u'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+ char options[] = "i:mqsatpcluVh";
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while ((opt = getopt_long(argc, argv, options, longopts, NULL)) != -1) {
+ switch (opt) {
+ case 'i':
+ id = atoi (optarg);
+ print = 1;
+ break;
+ case 'a':
+ msg = shm = sem = 1;
+ break;
+ case 'q':
+ msg = 1;
+ break;
+ case 's':
+ sem = 1;
+ break;
+ case 'm':
+ shm = 1;
+ break;
+ case 't':
+ format = TIME;
+ break;
+ case 'c':
+ format = CREATOR;
+ break;
+ case 'p':
+ format = PID;
+ break;
+ case 'l':
+ format = LIMITS;
+ break;
+ case 'u':
+ format = STATUS;
+ break;
+ case 'h':
+ usage(stdout);
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ default:
+ usage(stderr);
+ }
+ }
+
+ if (print) {
+ if (shm)
+ print_shm (id);
+ if (sem)
+ print_sem (id);
+ if (msg)
+ print_msg (id);
+ if (!shm && !sem && !msg )
+ usage (stderr);
+ } else {
+ if ( !shm && !msg && !sem)
+ msg = sem = shm = 1;
+ printf ("\n");
+
+ if (shm) {
+ do_shm (format);
+ printf ("\n");
+ }
+ if (sem) {
+ do_sem (format);
+ printf ("\n");
+ }
+ if (msg) {
+ do_msg (format);
+ printf ("\n");
+ }
+ }
+ return EXIT_SUCCESS;
+}
+
+static void print_perms (int id, struct ipc_perm *ipcp)
+{
+ struct passwd *pw;
+ struct group *gr;
+
+ printf ("%-10d %-10o", id, ipcp->mode & 0777);
+
+ if ((pw = getpwuid(ipcp->cuid)))
+ printf(" %-10s", pw->pw_name);
+ else
+ printf(" %-10u", ipcp->cuid);
+ if ((gr = getgrgid(ipcp->cgid)))
+ printf(" %-10s", gr->gr_name);
+ else
+ printf(" %-10u", ipcp->cgid);
+
+ if ((pw = getpwuid(ipcp->uid)))
+ printf(" %-10s", pw->pw_name);
+ else
+ printf(" %-10u", ipcp->uid);
+ if ((gr = getgrgid(ipcp->gid)))
+ printf(" %-10s\n", gr->gr_name);
+ else
+ printf(" %-10u\n", ipcp->gid);
+}
+
+void do_shm (char format)
+{
+ int maxid, shmid, id;
+ struct shmid_ds shmseg;
+ struct shm_info shm_info;
+ struct shminfo shminfo;
+ struct ipc_perm *ipcp = &shmseg.shm_perm;
+ struct passwd *pw;
+
+ maxid = shmctl (0, SHM_INFO, (struct shmid_ds *) (void *) &shm_info);
+ if (maxid < 0) {
+ printf (_("kernel not configured for shared memory\n"));
+ return;
+ }
+
+ switch (format) {
+ case LIMITS:
+ printf (_("------ Shared Memory Limits --------\n"));
+ if ((shmctl (0, IPC_INFO, (struct shmid_ds *) (void *) &shminfo)) < 0 )
+ return;
+ /*
+ * glibc 2.1.3 and all earlier libc's have ints as fields of
+ * struct shminfo; glibc 2.1.91 has unsigned long; ach
+ */
+ printf (_("max number of segments = %lu\n"),
+ (unsigned long) shminfo.shmmni);
+ printf (_("max seg size (kbytes) = %lu\n"),
+ (unsigned long) (shminfo.shmmax >> 10));
+ printf (_("max total shared memory (kbytes) = %llu\n"),
+ getpagesize() / 1024 * (unsigned long long) shminfo.shmall);
+ printf (_("min seg size (bytes) = %lu\n"),
+ (unsigned long) shminfo.shmmin);
+ return;
+
+ case STATUS:
+ printf (_("------ Shared Memory Status --------\n"));
+ /*
+ * TRANSLATORS: This output format is maintained for backward
+ * compatibility as ipcs is used in scripts. For consistency
+ * with the rest, the translated form can follow this model:
+ *
+ * "segments allocated = %d\n"
+ * "pages allocated = %ld\n"
+ * "pages resident = %ld\n"
+ * "pages swapped = %ld\n"
+ * "swap performance = %ld attempts, %ld successes\n"
+ */
+ printf (_("segments allocated %d\n"
+ "pages allocated %ld\n"
+ "pages resident %ld\n"
+ "pages swapped %ld\n"
+ "Swap performance: %ld attempts\t %ld successes\n"),
+ shm_info.used_ids,
+ shm_info.shm_tot,
+ shm_info.shm_rss,
+ shm_info.shm_swp,
+ shm_info.swap_attempts, shm_info.swap_successes);
+ return;
+
+ case CREATOR:
+ printf (_("------ Shared Memory Segment Creators/Owners --------\n"));
+ printf ("%-10s %-10s %-10s %-10s %-10s %-10s\n",
+ _("shmid"),_("perms"),_("cuid"),_("cgid"),_("uid"),_("gid"));
+ break;
+
+ case TIME:
+ printf (_("------ Shared Memory Attach/Detach/Change Times --------\n"));
+ printf ("%-10s %-10s %-20s %-20s %-20s\n",
+ _("shmid"),_("owner"),_("attached"),_("detached"),
+ _("changed"));
+ break;
+
+ case PID:
+ printf (_("------ Shared Memory Creator/Last-op PIDs --------\n"));
+ printf ("%-10s %-10s %-10s %-10s\n",
+ _("shmid"),_("owner"),_("cpid"),_("lpid"));
+ break;
+
+ default:
+ printf (_("------ Shared Memory Segments --------\n"));
+ printf ("%-10s %-10s %-10s %-10s %-10s %-10s %-12s\n",
+ _("key"),_("shmid"),_("owner"),_("perms"),_("bytes"),
+ _("nattch"),_("status"));
+ break;
+ }
+
+ for (id = 0; id <= maxid; id++) {
+ shmid = shmctl (id, SHM_STAT, &shmseg);
+ if (shmid < 0)
+ continue;
+ if (format == CREATOR) {
+ print_perms (shmid, ipcp);
+ continue;
+ }
+ pw = getpwuid(ipcp->uid);
+ switch (format) {
+ case TIME:
+ if (pw)
+ printf ("%-10d %-10.10s", shmid, pw->pw_name);
+ else
+ printf ("%-10d %-10u", shmid, ipcp->uid);
+ /* ctime uses static buffer: use separate calls */
+ printf(" %-20.16s", shmseg.shm_atime
+ ? ctime(&shmseg.shm_atime) + 4 : _("Not set"));
+ printf(" %-20.16s", shmseg.shm_dtime
+ ? ctime(&shmseg.shm_dtime) + 4 : _("Not set"));
+ printf(" %-20.16s\n", shmseg.shm_ctime
+ ? ctime(&shmseg.shm_ctime) + 4 : _("Not set"));
+ break;
+ case PID:
+ if (pw)
+ printf ("%-10d %-10.10s", shmid, pw->pw_name);
+ else
+ printf ("%-10d %-10u", shmid, ipcp->uid);
+ printf (" %-10d %-10d\n",
+ shmseg.shm_cpid, shmseg.shm_lpid);
+ break;
+
+ default:
+ printf("0x%08x ",ipcp->KEY );
+ if (pw)
+ printf ("%-10d %-10.10s", shmid, pw->pw_name);
+ else
+ printf ("%-10d %-10u", shmid, ipcp->uid);
+ printf (" %-10o %-10lu %-10ld %-6s %-6s\n",
+ ipcp->mode & 0777,
+ /*
+ * earlier: int, Austin has size_t
+ */
+ (unsigned long) shmseg.shm_segsz,
+ /*
+ * glibc-2.1.3 and earlier has unsigned short;
+ * Austin has shmatt_t
+ */
+ (long) shmseg.shm_nattch,
+ ipcp->mode & SHM_DEST ? _("dest") : " ",
+ ipcp->mode & SHM_LOCKED ? _("locked") : " ");
+ break;
+ }
+ }
+ return;
+}
+
+void do_sem (char format)
+{
+ int maxid, semid, id;
+ struct semid_ds semary;
+ struct seminfo seminfo;
+ struct ipc_perm *ipcp = &semary.sem_perm;
+ struct passwd *pw;
+ union semun arg;
+
+ arg.array = (ushort *) (void *) &seminfo;
+ maxid = semctl (0, 0, SEM_INFO, arg);
+ if (maxid < 0) {
+ printf (_("kernel not configured for semaphores\n"));
+ return;
+ }
+
+ switch (format) {
+ case LIMITS:
+ printf (_("------ Semaphore Limits --------\n"));
+ arg.array = (ushort *) (void *) &seminfo; /* damn union */
+ if ((semctl (0, 0, IPC_INFO, arg)) < 0 )
+ return;
+ printf (_("max number of arrays = %d\n"), seminfo.semmni);
+ printf (_("max semaphores per array = %d\n"), seminfo.semmsl);
+ printf (_("max semaphores system wide = %d\n"), seminfo.semmns);
+ printf (_("max ops per semop call = %d\n"), seminfo.semopm);
+ printf (_("semaphore max value = %d\n"), seminfo.semvmx);
+ return;
+
+ case STATUS:
+ printf (_("------ Semaphore Status --------\n"));
+ printf (_("used arrays = %d\n"), seminfo.semusz);
+ printf (_("allocated semaphores = %d\n"), seminfo.semaem);
+ return;
+
+ case CREATOR:
+ printf (_("------ Semaphore Arrays Creators/Owners --------\n"));
+ printf ("%-10s %-10s %-10s %-10s %-10s %-10s\n",
+ _("semid"),_("perms"),_("cuid"),_("cgid"),_("uid"),_("gid"));
+ break;
+
+ case TIME:
+ printf (_("------ Semaphore Operation/Change Times --------\n"));
+ printf ("%-8s %-10s %-26.24s %-26.24s\n",
+ _("semid"),_("owner"),_("last-op"),_("last-changed"));
+ break;
+
+ case PID:
+ break;
+
+ default:
+ printf (_("------ Semaphore Arrays --------\n"));
+ printf ("%-10s %-10s %-10s %-10s %-10s\n",
+ _("key"),_("semid"),_("owner"),_("perms"),_("nsems"));
+ break;
+ }
+
+ for (id = 0; id <= maxid; id++) {
+ arg.buf = (struct semid_ds *) &semary;
+ semid = semctl (id, 0, SEM_STAT, arg);
+ if (semid < 0)
+ continue;
+ if (format == CREATOR) {
+ print_perms (semid, ipcp);
+ continue;
+ }
+ pw = getpwuid(ipcp->uid);
+ switch (format) {
+ case TIME:
+ if (pw)
+ printf ("%-8d %-10.10s", semid, pw->pw_name);
+ else
+ printf ("%-8d %-10u", semid, ipcp->uid);
+ printf (" %-26.24s", semary.sem_otime
+ ? ctime(&semary.sem_otime) : _("Not set"));
+ printf (" %-26.24s\n", semary.sem_ctime
+ ? ctime(&semary.sem_ctime) : _("Not set"));
+ break;
+ case PID:
+ break;
+
+ default:
+ printf("0x%08x ", ipcp->KEY);
+ if (pw)
+ printf ("%-10d %-10.10s", semid, pw->pw_name);
+ else
+ printf ("%-10d %-10u", semid, ipcp->uid);
+ printf (" %-10o %-10ld\n",
+ ipcp->mode & 0777,
+ /*
+ * glibc-2.1.3 and earlier has unsigned
+ * short. glibc-2.1.91 has variation between
+ * unsigned short and unsigned long. Austin
+ * prescribes unsigned short.
+ */
+ (long) semary.sem_nsems);
+ break;
+ }
+ }
+}
+
+void do_msg (char format)
+{
+ int maxid, msqid, id;
+ struct msqid_ds msgque;
+ struct msginfo msginfo;
+ struct ipc_perm *ipcp = &msgque.msg_perm;
+ struct passwd *pw;
+
+ maxid = msgctl (0, MSG_INFO, (struct msqid_ds *) (void *) &msginfo);
+ if (maxid < 0) {
+ printf (_("kernel not configured for message queues\n"));
+ return;
+ }
+
+ switch (format) {
+ case LIMITS:
+ if ((msgctl (0, IPC_INFO, (struct msqid_ds *) (void *) &msginfo)) < 0 )
+ return;
+ printf (_("------ Messages Limits --------\n"));
+ printf (_("max queues system wide = %d\n"), msginfo.msgmni);
+ printf (_("max size of message (bytes) = %d\n"), msginfo.msgmax);
+ printf (_("default max size of queue (bytes) = %d\n"), msginfo.msgmnb);
+ return;
+
+ case STATUS:
+ printf (_("------ Messages Status --------\n"));
+ printf (_("allocated queues = %d\n"), msginfo.msgpool);
+ printf (_("used headers = %d\n"), msginfo.msgmap);
+ printf (_("used space = %d bytes\n"), msginfo.msgtql);
+ return;
+
+ case CREATOR:
+ printf (_("------ Message Queues Creators/Owners --------\n"));
+ printf ("%-10s %-10s %-10s %-10s %-10s %-10s\n",
+ _("msqid"),_("perms"),_("cuid"),_("cgid"),_("uid"),_("gid"));
+ break;
+
+ case TIME:
+ printf (_("------ Message Queues Send/Recv/Change Times --------\n"));
+ printf ("%-8s %-10s %-20s %-20s %-20s\n",
+ _("msqid"),_("owner"),_("send"),_("recv"),_("change"));
+ break;
+
+ case PID:
+ printf (_("------ Message Queues PIDs --------\n"));
+ printf ("%-10s %-10s %-10s %-10s\n",
+ _("msqid"),_("owner"),_("lspid"),_("lrpid"));
+ break;
+
+ default:
+ printf (_("------ Message Queues --------\n"));
+ printf ("%-10s %-10s %-10s %-10s %-12s %-12s\n",
+ _("key"), _("msqid"), _("owner"), _("perms"),
+ _("used-bytes"), _("messages"));
+ break;
+ }
+
+ for (id = 0; id <= maxid; id++) {
+ msqid = msgctl (id, MSG_STAT, &msgque);
+ if (msqid < 0)
+ continue;
+ if (format == CREATOR) {
+ print_perms (msqid, ipcp);
+ continue;
+ }
+ pw = getpwuid(ipcp->uid);
+ switch (format) {
+ case TIME:
+ if (pw)
+ printf ("%-8d %-10.10s", msqid, pw->pw_name);
+ else
+ printf ("%-8d %-10u", msqid, ipcp->uid);
+ printf (" %-20.16s", msgque.msg_stime
+ ? ctime(&msgque.msg_stime) + 4 : _("Not set"));
+ printf (" %-20.16s", msgque.msg_rtime
+ ? ctime(&msgque.msg_rtime) + 4 : _("Not set"));
+ printf (" %-20.16s\n", msgque.msg_ctime
+ ? ctime(&msgque.msg_ctime) + 4 : _("Not set"));
+ break;
+ case PID:
+ if (pw)
+ printf ("%-8d %-10.10s", msqid, pw->pw_name);
+ else
+ printf ("%-8d %-10u", msqid, ipcp->uid);
+ printf (" %5d %5d\n",
+ msgque.msg_lspid, msgque.msg_lrpid);
+ break;
+
+ default:
+ printf( "0x%08x ",ipcp->KEY );
+ if (pw)
+ printf ("%-10d %-10.10s", msqid, pw->pw_name);
+ else
+ printf ("%-10d %-10u", msqid, ipcp->uid);
+ printf (" %-10o %-12ld %-12ld\n",
+ ipcp->mode & 0777,
+ /*
+ * glibc-2.1.3 and earlier has unsigned
+ * short. glibc-2.1.91 has variation between
+ * unsigned short, unsigned long. Austin has
+ * msgqnum_t
+ */
+ (long) msgque.msg_cbytes,
+ (long) msgque.msg_qnum);
+ break;
+ }
+ }
+ return;
+}
+
+void print_shm (int shmid)
+{
+ struct shmid_ds shmds;
+ struct ipc_perm *ipcp = &shmds.shm_perm;
+
+ if (shmctl (shmid, IPC_STAT, &shmds) == -1)
+ err(EXIT_FAILURE, _("shmctl failed"));
+
+ printf (_("\nShared memory Segment shmid=%d\n"), shmid);
+ printf (_("uid=%u\tgid=%u\tcuid=%u\tcgid=%u\n"),
+ ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid);
+ printf (_("mode=%#o\taccess_perms=%#o\n"),
+ ipcp->mode, ipcp->mode & 0777);
+ printf (_("bytes=%lu\tlpid=%d\tcpid=%d\tnattch=%ld\n"),
+ (unsigned long) shmds.shm_segsz, shmds.shm_lpid, shmds.shm_cpid,
+ (long) shmds.shm_nattch);
+ printf (_("att_time=%-26.24s\n"),
+ shmds.shm_atime ? ctime (&shmds.shm_atime) : _("Not set"));
+ printf (_("det_time=%-26.24s\n"),
+ shmds.shm_dtime ? ctime (&shmds.shm_dtime) : _("Not set"));
+ printf (_("change_time=%-26.24s\n"), ctime (&shmds.shm_ctime));
+ printf ("\n");
+ return;
+}
+
+
+void print_msg (int msqid)
+{
+ struct msqid_ds buf;
+ struct ipc_perm *ipcp = &buf.msg_perm;
+
+ if (msgctl (msqid, IPC_STAT, &buf) == -1)
+ err(EXIT_FAILURE, _("msgctl failed"));
+
+ printf (_("\nMessage Queue msqid=%d\n"), msqid);
+ printf (_("uid=%u\tgid=%u\tcuid=%u\tcgid=%u\tmode=%#o\n"),
+ ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid, ipcp->mode);
+ printf (_("cbytes=%ld\tqbytes=%ld\tqnum=%ld\tlspid=%d\tlrpid=%d\n"),
+ /*
+ * glibc-2.1.3 and earlier has unsigned short. glibc-2.1.91
+ * has variation between unsigned short, unsigned long.
+ * Austin has msgqnum_t (for msg_qbytes)
+ */
+ (long) buf.msg_cbytes, (long) buf.msg_qbytes,
+ (long) buf.msg_qnum, buf.msg_lspid, buf.msg_lrpid);
+ printf (_("send_time=%-26.24s\n"),
+ buf.msg_stime ? ctime (&buf.msg_stime) : _("Not set"));
+ printf (_("rcv_time=%-26.24s\n"),
+ buf.msg_rtime ? ctime (&buf.msg_rtime) : _("Not set"));
+ printf (_("change_time=%-26.24s\n"),
+ buf.msg_ctime ? ctime (&buf.msg_ctime) : _("Not set"));
+ printf ("\n");
+ return;
+}
+
+void print_sem (int semid)
+{
+ struct semid_ds semds;
+ struct ipc_perm *ipcp = &semds.sem_perm;
+ union semun arg;
+ size_t i;
+
+ arg.buf = &semds;
+ if (semctl (semid, 0, IPC_STAT, arg) < 0)
+ err(EXIT_FAILURE, _("semctl failed"));
+
+ printf (_("\nSemaphore Array semid=%d\n"), semid);
+ printf (_("uid=%u\t gid=%u\t cuid=%u\t cgid=%u\n"),
+ ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid);
+ printf (_("mode=%#o, access_perms=%#o\n"),
+ ipcp->mode, ipcp->mode & 0777);
+ printf (_("nsems = %ld\n"), (long) semds.sem_nsems);
+ printf (_("otime = %-26.24s\n"),
+ semds.sem_otime ? ctime (&semds.sem_otime) : _("Not set"));
+ printf (_("ctime = %-26.24s\n"), ctime (&semds.sem_ctime));
+
+ printf ("%-10s %-10s %-10s %-10s %-10s\n",
+ _("semnum"),_("value"),_("ncount"),_("zcount"),_("pid"));
+ arg.val = 0;
+ for (i=0; i< semds.sem_nsems; i++) {
+ int val, ncnt, zcnt, pid;
+ val = semctl (semid, i, GETVAL, arg);
+ ncnt = semctl (semid, i, GETNCNT, arg);
+ zcnt = semctl (semid, i, GETZCNT, arg);
+ pid = semctl (semid, i, GETPID, arg);
+ if (val < 0 || ncnt < 0 || zcnt < 0 || pid < 0)
+ err(EXIT_FAILURE, _("semctl failed"));
+
+ printf ("%-10zd %-10d %-10d %-10d %-10d\n",
+ i, val, ncnt, zcnt, pid);
+ }
+ printf ("\n");
+ return;
+}
diff --git a/sys-utils/ldattach.8 b/sys-utils/ldattach.8
new file mode 100644
index 0000000..8ce14f3
--- /dev/null
+++ b/sys-utils/ldattach.8
@@ -0,0 +1,144 @@
+.\" Copyright 2008 Tilman Schmidt (tilman@imap.cc)
+.\" May be distributed under the GNU General Public License version 2 or later
+.TH LDATTACH 8 "February 2010" "util-linux" "System Administration"
+.SH NAME
+ldattach \- attach a line discipline to a serial line
+.SH SYNOPSIS
+.B ldattach
+.RB [ \-dhV78neo12 ]
+.RB [ \-s
+.IR speed ]
+.RB [ \-i
+.IR iflag ]
+.I ldisc device
+.SH DESCRIPTION
+The
+.B ldattach
+daemon opens the specified
+.I device
+file
+(which should refer to a serial device)
+and attaches the line discipline
+.I ldisc
+to it for processing of the sent and/or received data.
+It then goes into the background keeping the device open so that the
+line discipline stays loaded.
+
+The line discipline
+.I ldisc
+may be specified either by name
+or by number.
+
+In order to detach the line discipline,
+.BR kill (1)
+the
+.B ldattach
+process.
+
+With no arguments,
+.B ldattach
+prints usage information.
+.SH LINE DISCIPLINES
+Depending on the kernel release, the following line disciplines are supported:
+.TP
+.BR TTY ( 0 )
+The default line discipline,
+providing transparent operation (raw mode)
+as well as the habitual terminal line editing capabilities (cooked mode).
+.TP
+.BR SLIP ( 1 )
+Serial Line IP (SLIP) protocol processor
+for transmitting TCP/IP packets over serial lines.
+.TP
+.BR MOUSE ( 2 )
+Device driver for RS232 connected pointing devices (serial mice).
+.TP
+.BR PPP ( 3 )
+Point to Point Protocol (PPP) processor
+for transmitting network packets over serial lines.
+.TP
+.BR STRIP ( 4 )
+.TP
+.BR AX25 ( 5 )
+.TP
+.BR X25 ( 6 )
+Line driver for transmitting X.25 packets over asynchronous serial lines.
+.TP
+.BR 6PACK ( 7 )
+.TP
+.BR R3964 ( 9 )
+Driver for Simatic R3964 module.
+.TP
+.BR IRDA ( 11 )
+Linux IrDa (infrared data transmission) driver -
+see http://irda.sourceforge.net/
+.TP
+.BR HDLC ( 13 )
+Synchronous HDLC driver.
+.TP
+.BR SYNC_PPP ( 14 )
+Synchronous PPP driver.
+.TP
+.BR HCI ( 15 )
+Bluetooth HCI UART driver.
+.TP
+.BR GIGASET_M101 ( 16 )
+Driver for Siemens Gigaset M101 serial DECT adapter.
+.TP
+.BR PPS ( 18 )
+Driver for serial line Pulse Per Second (PPS) source.
+.SH OPTIONS
+.TP
+\fB-d\fP | \fB--debug\fP
+Causes
+.B ldattach
+to stay in the foreground so that it can be interrupted or debugged,
+and to print verbose messages about its progress to the standard error output.
+.TP
+\fB-h\fP | \fB--help\fP
+Prints a usage message and exits.
+.TP
+\fB-V\fP | \fB--version\fP
+Prints the program version.
+.TP
+\fB-s\fP \fIvalue\fP | \fB--speed\fP \fIvalue\fP
+Set the speed of the serial line to the specified value.
+.TP
+\fB-7\fP | \fB--sevenbits\fP
+Sets the character size of the serial line to 7 bits.
+.TP
+\fB-8\fP | \fB--eightbits\fP
+Sets the character size of the serial line to 8 bits.
+.TP
+\fB-n\fP | \fB--noparity\fP
+Sets the parity of the serial line to none.
+.TP
+\fB-e\fP | \fB--evenparity\fP
+Sets the parity of the serial line to even.
+.TP
+\fB-o\fP | \fB--oddparity\fP
+Sets the parity of the serial line to odd.
+.TP
+\fB-1\fP | \fB--onestopbit\fP
+Sets the number of stop bits of the serial line to one.
+.TP
+\fB-2\fP | \fB--twostopbits\fP
+Sets the number of stop bits of the serial line to two.
+.TP
+\fB-i\fP \fIvalue\fP | \fB--iflag\fP [\fB-\fP]\fIvalue\fP{,...}
+Sets the specified bits in the c_iflag word of the serial line.
+\fIValue\fP may be a number or a symbolic name.
+If \fIvalue\fP is prefixed by a minus sign, clear the specified bits instead.
+Several comma separated \fIvalue\fPs may be given in order to
+set and clear multiple bits.
+.SH "SEE ALSO"
+.BR inputattach (1),
+.BR ttys (4)
+.SH AUTHOR
+.nf
+Tilman Schmidt (tilman@imap.cc)
+.fi
+.SH AVAILABILITY
+The ldattach command is part of the util-linux package
+and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/sys-utils/ldattach.c b/sys-utils/ldattach.c
new file mode 100644
index 0000000..c06aed2
--- /dev/null
+++ b/sys-utils/ldattach.c
@@ -0,0 +1,373 @@
+/* line discipline loading daemon
+ * open a serial device and attach a line discipline on it
+ *
+ * Usage:
+ * ldattach GIGASET_M101 /dev/ttyS0
+ *
+ * =====================================================================
+ * 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.
+ * =====================================================================
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "closestream.h"
+
+#ifndef N_GIGASET_M101
+# define N_GIGASET_M101 16
+#endif
+
+#ifndef N_PPS
+# define N_PPS 18
+#endif
+
+/* attach a line discipline ioctl */
+#ifndef TIOCSETD
+# define TIOCSETD 0x5423
+#endif
+
+static const char *progname;
+static int debug = 0;
+
+struct ld_table {
+ const char *name;
+ int value;
+};
+
+/* currently supported line disciplines, plus some aliases */
+static const struct ld_table ld_discs[] = {
+ { "TTY", N_TTY },
+ { "SLIP", N_SLIP },
+ { "MOUSE", N_MOUSE },
+ { "PPP", N_PPP },
+ { "STRIP", N_STRIP },
+ { "AX25", N_AX25 },
+ { "X25", N_X25 },
+ { "6PACK", N_6PACK },
+ { "R3964", N_R3964 },
+ { "IRDA", N_IRDA },
+ { "HDLC", N_HDLC },
+ { "SYNC_PPP", N_SYNC_PPP },
+ { "SYNCPPP", N_SYNC_PPP },
+ { "HCI", N_HCI },
+ { "PPS", N_PPS },
+ { "M101", N_GIGASET_M101 },
+ { "GIGASET", N_GIGASET_M101 },
+ { "GIGASET_M101", N_GIGASET_M101 },
+ { NULL, 0 }
+};
+
+/* known c_iflag names */
+static const struct ld_table ld_iflags[] =
+{
+ { "IGNBRK", IGNBRK },
+ { "BRKINT", BRKINT },
+ { "IGNPAR", IGNPAR },
+ { "PARMRK", PARMRK },
+ { "INPCK", INPCK },
+ { "ISTRIP", ISTRIP },
+ { "INLCR", INLCR },
+ { "IGNCR", IGNCR },
+ { "ICRNL", ICRNL },
+ { "IUCLC", IUCLC },
+ { "IXON", IXON },
+ { "IXANY", IXANY },
+ { "IXOFF", IXOFF },
+ { "IMAXBEL", IMAXBEL },
+ { "IUTF8", IUTF8 },
+ { NULL, 0 }
+};
+
+static void dbg(char *fmt, ...)
+{
+ va_list args;
+
+ if (debug == 0)
+ return;
+ fflush(NULL);
+ fprintf(stderr, "%s: ", program_invocation_short_name);
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ fflush(NULL);
+ return;
+}
+
+static int lookup_table(const struct ld_table *tab, const char *str)
+{
+ const struct ld_table *t;
+
+ for (t = tab; t && t->name; t++)
+ if (!strcasecmp(t->name, str))
+ return t->value;
+ return -1;
+}
+
+static void print_table(FILE * out, const struct ld_table *tab)
+{
+ const struct ld_table *t;
+ int i;
+
+ for (t = tab, i = 1; t && t->name; t++, i++) {
+ fprintf(out, " %-10s", t->name);
+ if (!(i % 6))
+ fputc('\n', out);
+ }
+}
+
+static int parse_iflag(char *str, int *set_iflag, int *clr_iflag)
+{
+ int iflag;
+ char *s;
+
+ for (s = strtok(str, ","); s != NULL; s = strtok(NULL, ",")) {
+ if (*s == '-')
+ s++;
+ if ((iflag = lookup_table(ld_iflags, s)) < 0)
+ iflag = strtos32_or_err(s, _("invalid iflag"));
+ if (s > str && *(s - 1) == '-')
+ *clr_iflag |= iflag;
+ else
+ *set_iflag |= iflag;
+ }
+ dbg("iflag (set/clear): %d/%d", *set_iflag, *clr_iflag);
+ return 0;
+}
+
+
+static void __attribute__ ((__noreturn__)) usage(int exitcode)
+{
+ FILE *out = exitcode == EXIT_SUCCESS ? stdout : stderr;
+
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(" %s [options] <ldisc> <device>\n"), program_invocation_short_name);
+ fputs(USAGE_OPTIONS, out);
+
+ fputs(_(" -d, --debug print verbose messages to stderr\n"), out);
+ fputs(_(" -s, --speed <value> set serial line speed\n"), out);
+ fputs(_(" -7, --sevenbits set character size to 7 bits\n"), out);
+ fputs(_(" -8, --eightbits set character size to 8 bits\n"), out);
+ fputs(_(" -n, --noparity set parity to none\n"), out);
+ fputs(_(" -e, --evenparity set parity to even\n"), out);
+ fputs(_(" -o, --oddparity set parity to odd\n"), out);
+ fputs(_(" -1, --onestopbit set stop bits to one\n"), out);
+ fputs(_(" -2, --twostopbits set stop bits to two\n"), out);
+ fputs(_(" -i, --iflag [-]<iflag> set input mode flag\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ fputs(_("\nKnown <ldisc> names:\n"), out);
+ print_table(out, ld_discs);
+ fputs(_("\nKnown <iflag> names:\n"), out);
+ print_table(out, ld_iflags);
+ fputc('\n', out);
+ fprintf(out, USAGE_MAN_TAIL("ldattach(8)"));
+ exit(exitcode);
+}
+
+static int my_cfsetspeed(struct termios *ts, int speed)
+{
+ /* Standard speeds
+ * -- cfsetspeed() is able to translate number to Bxxx constants
+ */
+ if (cfsetspeed(ts, speed) == 0)
+ return 0;
+
+ /* Nonstandard speeds
+ * -- we have to bypass glibc and set the speed manually (because glibc
+ * checks for speed and supports Bxxx bit rates only)...
+ */
+#ifdef _HAVE_STRUCT_TERMIOS_C_ISPEED
+# define BOTHER 0010000 /* non standard rate */
+ dbg("using non-standard speeds");
+ ts->c_ospeed = ts->c_ispeed = speed;
+ ts->c_cflag &= ~CBAUD;
+ ts->c_cflag |= BOTHER;
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+int main(int argc, char **argv)
+{
+ int tty_fd;
+ struct termios ts;
+ int speed = 0, bits = '-', parity = '-', stop = '-';
+ int set_iflag = 0, clr_iflag = 0;
+ int ldisc;
+ int optc;
+ char *dev;
+ static const struct option opttbl[] = {
+ {"speed", required_argument, NULL, 's'},
+ {"sevenbits", no_argument, NULL, '7'},
+ {"eightbits", no_argument, NULL, '8'},
+ {"noparity", no_argument, NULL, 'n'},
+ {"evenparity", no_argument, NULL, 'e'},
+ {"oddparity", no_argument, NULL, 'o'},
+ {"onestopbit", no_argument, NULL, '1'},
+ {"twostopbits", no_argument, NULL, '2'},
+ {"iflag", required_argument, NULL, 'i'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {"debug", no_argument, NULL, 'd'},
+ {NULL, 0, NULL, 0}
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ /* parse options */
+ progname = program_invocation_short_name;
+
+ if (argc == 0)
+ usage(EXIT_SUCCESS);
+ while ((optc =
+ getopt_long(argc, argv, "dhV78neo12s:i:", opttbl,
+ NULL)) >= 0) {
+ switch (optc) {
+ case 'd':
+ debug = 1;
+ break;
+ case '1':
+ case '2':
+ stop = optc;
+ break;
+ case '7':
+ case '8':
+ bits = optc;
+ break;
+ case 'n':
+ case 'e':
+ case 'o':
+ parity = optc;
+ break;
+ case 's':
+ speed = strtos32_or_err(optarg, _("invalid speed argument"));
+ break;
+ case 'i':
+ parse_iflag(optarg, &set_iflag, &clr_iflag);
+ break;
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(EXIT_SUCCESS);
+ default:
+ warnx(_("invalid option"));
+ usage(EXIT_FAILURE);
+ }
+ }
+
+ if (argc - optind != 2)
+ usage(EXIT_FAILURE);
+
+ /* parse line discipline specification */
+ ldisc = lookup_table(ld_discs, argv[optind]);
+ if (ldisc < 0)
+ ldisc = strtos32_or_err(argv[optind], _("invalid line discipline argument"));
+
+ /* open device */
+ dev = argv[optind + 1];
+ if ((tty_fd = open(dev, O_RDWR | O_NOCTTY)) < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), dev);
+ if (!isatty(tty_fd))
+ errx(EXIT_FAILURE, _("%s is not a serial line"), dev);
+
+ dbg("opened %s", dev);
+
+ /* set line speed and format */
+ if (tcgetattr(tty_fd, &ts) < 0)
+ err(EXIT_FAILURE,
+ _("cannot get terminal attributes for %s"), dev);
+ cfmakeraw(&ts);
+ if (speed && my_cfsetspeed(&ts, speed) < 0)
+ errx(EXIT_FAILURE, _("speed %d unsupported"), speed);
+
+ switch (stop) {
+ case '1':
+ ts.c_cflag &= ~CSTOPB;
+ break;
+ case '2':
+ ts.c_cflag |= CSTOPB;
+ break;
+ case '-':
+ break;
+ default:
+ abort();
+ }
+ switch (bits) {
+ case '7':
+ ts.c_cflag = (ts.c_cflag & ~CSIZE) | CS7;
+ break;
+ case '8':
+ ts.c_cflag = (ts.c_cflag & ~CSIZE) | CS8;
+ break;
+ case '-':
+ break;
+ default:
+ abort();
+ }
+ switch (parity) {
+ case 'n':
+ ts.c_cflag &= ~(PARENB | PARODD);
+ break;
+ case 'e':
+ ts.c_cflag |= PARENB;
+ ts.c_cflag &= ~PARODD;
+ break;
+ case 'o':
+ ts.c_cflag |= (PARENB | PARODD);
+ break;
+ case '-':
+ break;
+ default:
+ abort();
+ }
+
+ ts.c_cflag |= CREAD; /* just to be on the safe side */
+ ts.c_iflag |= set_iflag;
+ ts.c_iflag &= ~clr_iflag;
+
+ if (tcsetattr(tty_fd, TCSAFLUSH, &ts) < 0)
+ err(EXIT_FAILURE,
+ _("cannot set terminal attributes for %s"), dev);
+
+ dbg("set to raw %d %c%c%c: cflag=0x%x",
+ speed, bits, parity, stop, ts.c_cflag);
+
+ /* Attach the line discpline. */
+ if (ioctl(tty_fd, TIOCSETD, &ldisc) < 0)
+ err(EXIT_FAILURE, _("cannot set line discipline"));
+
+ dbg("line discipline set to %d", ldisc);
+
+ /* Go into background if not in debug mode. */
+ if (!debug && daemon(0, 0) < 0)
+ err(EXIT_FAILURE, _("cannot daemonize"));
+
+ /* Sleep to keep the line discipline active. */
+ pause();
+
+ exit(EXIT_SUCCESS);
+}
diff --git a/sys-utils/losetup.8 b/sys-utils/losetup.8
new file mode 100644
index 0000000..9b5fe61
--- /dev/null
+++ b/sys-utils/losetup.8
@@ -0,0 +1,174 @@
+.TH LOSETUP 8 "July 2003" "util-linux" "System Administration"
+.SH NAME
+losetup \- set up and control loop devices
+.SH SYNOPSIS
+.ad l
+Get info:
+.sp
+.in +5
+.B losetup
+.I loopdev
+.sp
+.B losetup -a
+.sp
+.B losetup -j
+.I file
+.RB [ \-o
+.IR offset ]
+.sp
+.in -5
+Delete loop:
+.sp
+.in +5
+.B "losetup \-d"
+.IR loopdev ...
+.sp
+.in -5
+Delete all used loop devices:
+.sp
+.in +5
+.B "losetup \-D"
+.sp
+.in -5
+Print name of first unused loop device:
+.sp
+.in +5
+.B "losetup \-f"
+.sp
+.in -5
+Setup loop device:
+.sp
+.in +5
+.B losetup
+.RB [{ \-e | \-E }
+.IR encryption ]
+.RB [ \-o
+.IR offset ]
+.RB [ \-\-sizelimit
+.IR size ]
+.in +8
+.RB [ \-p
+.IR pfd ]
+.RB [ \-rP ]
+.RB { \-f [ \-\-show ]| \fIloopdev\fP }
+.I file
+.sp
+.in -13
+Resize loop device:
+.sp
+.in +5
+.B "losetup \-c"
+.I loopdev
+.in -5
+.ad b
+.SH DESCRIPTION
+.B losetup
+is used to associate loop devices with regular files or block devices,
+to detach loop devices and to query the status of a loop device. If only the
+\fIloopdev\fP argument is given, the status of the corresponding loop
+device is shown.
+
+.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.
+
+.IP "\fB\-a, \-\-all\fP"
+show status of all loop devices. Note that not all information are accessible
+for non-root users.
+.IP "\fB\-c, \-\-set-capacity\fP \fIloopdev\fP
+force loop driver to reread size of the file associated with the specified loop device
+.IP "\fB\-d, \-\-detach\fP \fIloopdev\fP..."
+detach the file or device associated with the specified loop device(s)
+.IP "\fB\-D, \-\-detach-all\fP"
+detach all associated loop devices
+.IP "\fB\-e, \-E, \-\-encryption \fIencryption_type\fP"
+enable data encryption with specified name or number
+.IP "\fB\-f, \-\-find\fP"
+find the first unused loop device. If a
+.I file
+argument is present, use this device. Otherwise, print its name
+.IP "\fB\-h, \-\-help\fP"
+print help
+.IP "\fB\-j, \-\-associated \fIfile\fP"
+show status of all loop devices associated with given
+.I file
+.IP "\fB\-o, \-\-offset \fIoffset\fP"
+the data start is moved \fIoffset\fP bytes into the specified file or
+device
+.IP "\fB\-\-sizelimit \fIsize\fP"
+the data end is set to no more than \fIsize\fP bytes after the data start
+.IP "\fB\-p, \-\-pass-fd \fInum\fP"
+read the passphrase from file descriptor with number
+.I num
+instead of from the terminal
+.IP "\fB\-P, \-\-partscan\fP"
+force kernel to scan partition table on newly created loop device
+.IP "\fB\-r, \-\-read-only\fP"
+setup read-only loop device
+.IP "\fB\-\-show\fP"
+print device name if the
+.I -f
+option and a
+.I file
+argument are present.
+.IP "\fB\-v, \-\-verbose\fP"
+verbose mode
+
+.SH ENCRYPTION
+.B Cryptoloop is deprecated in favor of dm-crypt. For more details see
+.B cryptsetup (8). It is possible that all bug reports regarding to -E/-e
+.B options will be ignored.
+
+
+It is possible to specify transfer functions (for encryption/decryption
+or other purposes) using one of the
+.B \-E
+and
+.B \-e
+options.
+There are two mechanisms to specify the desired encryption: by number
+and by name. If an encryption is specified by number then one
+has to make sure that the Linux kernel knows about the encryption with that
+number, probably by patching the kernel. Standard numbers that are
+always present are 0 (no encryption) and 1 (XOR encryption).
+When the cryptoloop module is loaded (or compiled in), it uses number 18.
+This cryptoloop module will take the name of an arbitrary encryption type
+and find the module that knows how to perform that encryption.
+
+.SH RETURN VALUE
+.B losetup
+returns 0 on success, nonzero on failure. When
+.B losetup
+displays the status of a loop device, it returns 1 if the device
+is not configured and 2 if an error occurred which prevented
+from determining the status of the device.
+
+.SH FILES
+.TP
+.I /dev/loop[0..N]
+loop block devices
+.TP
+.I /dev/loop-cotrol
+loop control device
+
+.SH EXAMPLE
+The following commands can be used as an example of using the loop device.
+.nf
+.IP
+# dd if=/dev/zero of=~/file.img bs=1MiB count=10
+# losetup --find --show ~/file.img
+/dev/loop0
+# mkfs -t ext2 /dev/loop0
+# mount /dev/loop0 /mnt
+ ...
+# umount /dev/loop0
+# losetup --detach /dev/loop0
+.fi
+.SH AUTHORS
+Karel Zak <kzak@redhat.com>, based on original version from
+Theodore Ts'o <tytso@athena.mit.edu>
+.SH AVAILABILITY
+The losetup command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/sys-utils/losetup.c b/sys-utils/losetup.c
new file mode 100644
index 0000000..fdcc0be
--- /dev/null
+++ b/sys-utils/losetup.c
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ * Originally from Ted's losetup.c
+ *
+ * losetup.c - setup and control loop devices
+ */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <inttypes.h>
+#include <getopt.h>
+
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "loopdev.h"
+#include "xgetpass.h"
+#include "closestream.h"
+#include "optutils.h"
+
+enum {
+ A_CREATE = 1, /* setup a new device */
+ A_DELETE, /* delete given device(s) */
+ A_DELETE_ALL, /* delete all devices */
+ A_SHOW, /* list devices */
+ A_SHOW_ONE, /* print info about one device */
+ A_FIND_FREE, /* find first unused */
+ A_SET_CAPACITY, /* set device capacity */
+};
+
+static int verbose;
+
+
+static int printf_loopdev(struct loopdev_cxt *lc)
+{
+ uint64_t x;
+ dev_t dev = 0;
+ ino_t ino = 0;
+ char *fname = NULL;
+ uint32_t type;
+
+ fname = loopcxt_get_backing_file(lc);
+ if (!fname)
+ return -EINVAL;
+
+ if (loopcxt_get_backing_devno(lc, &dev) == 0)
+ loopcxt_get_backing_inode(lc, &ino);
+
+ if (!dev && !ino) {
+ /*
+ * Probably non-root user (no permissions to
+ * call LOOP_GET_STATUS ioctls).
+ */
+ printf("%s: []: (%s)",
+ loopcxt_get_device(lc), fname);
+
+ if (loopcxt_get_offset(lc, &x) == 0 && x)
+ printf(_(", offset %ju"), x);
+
+ if (loopcxt_get_sizelimit(lc, &x) == 0 && x)
+ printf(_(", sizelimit %ju"), x);
+ printf("\n");
+ return 0;
+ }
+
+ printf("%s: [%04d]:%" PRIu64 " (%s)",
+ loopcxt_get_device(lc), (int) dev, ino, fname);
+
+ if (loopcxt_get_offset(lc, &x) == 0 && x)
+ printf(_(", offset %ju"), x);
+
+ if (loopcxt_get_sizelimit(lc, &x) == 0 && x)
+ printf(_(", sizelimit %ju"), x);
+
+ if (loopcxt_get_encrypt_type(lc, &type) == 0) {
+ const char *e = loopcxt_get_crypt_name(lc);
+
+ if ((!e || !*e) && type == 1)
+ e = "XOR";
+ if (e && *e)
+ printf(_(", encryption %s (type %u)"), e, type);
+ }
+ printf("\n");
+ return 0;
+}
+
+static int show_all_loops(struct loopdev_cxt *lc, const char *file,
+ uint64_t offset, int flags)
+{
+ struct stat sbuf, *st = &sbuf;
+
+ if (loopcxt_init_iterator(lc, LOOPITER_FL_USED))
+ return -1;
+
+ if (!file || stat(file, st))
+ st = NULL;
+
+ while (loopcxt_next(lc) == 0) {
+
+ if (file && !loopcxt_is_used(lc, st, file, offset, flags))
+ continue;
+
+ printf_loopdev(lc);
+ }
+ loopcxt_deinit_iterator(lc);
+ return 0;
+}
+
+static int set_capacity(struct loopdev_cxt *lc)
+{
+ int fd = loopcxt_get_fd(lc);
+
+ if (fd < 0)
+ warn(_("cannot open %s"), loopcxt_get_device(lc));
+ else if (ioctl(fd, LOOP_SET_CAPACITY) != 0)
+ warnx(_("%s: set capacity failed"), loopcxt_get_device(lc));
+ else
+ return 0;
+
+ return -1;
+}
+
+static int delete_loop(struct loopdev_cxt *lc)
+{
+ if (loopcxt_delete_device(lc))
+ warn(_("%s: detach failed"), loopcxt_get_device(lc));
+ else
+ return 0;
+
+ return -1;
+}
+
+static int delete_all_loops(struct loopdev_cxt *lc)
+{
+ int res = 0;
+
+ if (loopcxt_init_iterator(lc, LOOPITER_FL_USED))
+ return -1;
+
+ while (loopcxt_next(lc) == 0)
+ res += delete_loop(lc);
+
+ loopcxt_deinit_iterator(lc);
+ return res;
+}
+
+static void usage(FILE *out)
+{
+ fputs(USAGE_HEADER, out);
+
+ fprintf(out,
+ _(" %1$s [options] [<loopdev>]\n"
+ " %1$s [options] -f | <loopdev> <file>\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -a, --all list all used devices\n"
+ " -d, --detach <loopdev> [...] detach one or more devices\n"
+ " -D, --detach-all detach all used devices\n"
+ " -f, --find find first unused device\n"
+ " -c, --set-capacity <loopdev> resize device\n"
+ " -j, --associated <file> list all devices associated with <file>\n"), out);
+ fputs(USAGE_SEPARATOR, out);
+
+ fputs(_(" -e, --encryption <type> enable encryption with specified <name/num>\n"
+ " -o, --offset <num> start at offset <num> into file\n"
+ " --sizelimit <num> device limited to <num> bytes of the file\n"
+ " -p, --pass-fd <num> read passphrase from file descriptor <num>\n"
+ " -P, --partscan create partitioned loop device\n"
+ " -r, --read-only setup read-only loop device\n"
+ " --show print device name after setup (with -f)\n"
+ " -v, --verbose verbose mode\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+
+ fprintf(out, USAGE_MAN_TAIL("losetup(8)"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static void warn_size(const char *filename, uint64_t size)
+{
+ struct stat st;
+
+ if (!size) {
+ if (stat(filename, &st))
+ return;
+ size = st.st_size;
+ }
+
+ if (size < 512)
+ warnx(_("%s: warning: file smaller than 512 bytes, the loop device "
+ "maybe be useless or invisible for system tools."),
+ filename);
+ else if (size % 512)
+ warnx(_("%s: warning: file does not fit into a 512-byte sector "
+ "the end of the file will be ignored."),
+ filename);
+}
+
+int main(int argc, char **argv)
+{
+ struct loopdev_cxt lc;
+ int act = 0, flags = 0, passfd = -1, c;
+ char *file = NULL, *encryption = NULL;
+ uint64_t offset = 0, sizelimit = 0;
+ int res = 0, showdev = 0, lo_flags = 0;
+
+ enum {
+ OPT_SIZELIMIT = CHAR_MAX + 1,
+ OPT_SHOW
+ };
+ static const struct option longopts[] = {
+ { "all", 0, 0, 'a' },
+ { "set-capacity", 1, 0, 'c' },
+ { "detach", 1, 0, 'd' },
+ { "detach-all", 0, 0, 'D' },
+ { "encryption", 1, 0, 'e' },
+ { "find", 0, 0, 'f' },
+ { "help", 0, 0, 'h' },
+ { "associated", 1, 0, 'j' },
+ { "offset", 1, 0, 'o' },
+ { "sizelimit", 1, 0, OPT_SIZELIMIT },
+ { "pass-fd", 1, 0, 'p' },
+ { "partscan", 0, 0, 'P' },
+ { "read-only", 0, 0, 'r' },
+ { "show", 0, 0, OPT_SHOW },
+ { "verbose", 0, 0, 'v' },
+ { "version", 0, 0, 'V' },
+ { NULL, 0, 0, 0 }
+ };
+
+ static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
+ { 'D','a','c','d','f','j' },
+ { 0 }
+ };
+ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ if (loopcxt_init(&lc, 0))
+ err(EXIT_FAILURE, _("failed to initialize loopcxt"));
+
+ loopcxt_enable_debug(&lc, getenv("LOOPDEV_DEBUG") ? TRUE : FALSE);
+
+ while ((c = getopt_long(argc, argv, "ac:d:De:E:fhj:o:p:PrvV",
+ longopts, NULL)) != -1) {
+
+ err_exclusive_options(c, longopts, excl, excl_st);
+
+ switch (c) {
+ case 'a':
+ act = A_SHOW;
+ break;
+ case 'c':
+ act = A_SET_CAPACITY;
+ if (loopcxt_set_device(&lc, optarg))
+ err(EXIT_FAILURE, _("%s: failed to use device"),
+ optarg);
+ break;
+ case 'r':
+ lo_flags |= LO_FLAGS_READ_ONLY;
+ break;
+ case 'd':
+ act = A_DELETE;
+ if (loopcxt_set_device(&lc, optarg))
+ err(EXIT_FAILURE, _("%s: failed to use device"),
+ optarg);
+ break;
+ case 'D':
+ act = A_DELETE_ALL;
+ break;
+ case 'E':
+ case 'e':
+ encryption = optarg;
+ break;
+ case 'f':
+ act = A_FIND_FREE;
+ break;
+ case 'h':
+ usage(stdout);
+ break;
+ case 'j':
+ act = A_SHOW;
+ file = optarg;
+ break;
+ case 'o':
+ offset = strtosize_or_err(optarg, _("failed to parse offset"));
+ flags |= LOOPDEV_FL_OFFSET;
+ break;
+ case 'p':
+ passfd = strtou32_or_err(optarg,
+ _("invalid passphrase file descriptor"));
+ break;
+ case 'P':
+ lo_flags |= LO_FLAGS_PARTSCAN;
+ break;
+ case OPT_SHOW:
+ showdev = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case OPT_SIZELIMIT: /* --sizelimit */
+ sizelimit = strtosize_or_err(optarg, _("failed to parse size"));
+ flags |= LOOPDEV_FL_SIZELIMIT;
+ break;
+ default:
+ usage(stderr);
+ }
+ }
+
+ if (argc == 1)
+ usage(stderr);
+
+ if (act == A_FIND_FREE && optind < argc) {
+ /*
+ * losetup -f <backing_file>
+ */
+ act = A_CREATE;
+ file = argv[optind++];
+ }
+ if (!act && optind + 1 == argc) {
+ /*
+ * losetup <device>
+ */
+ act = A_SHOW_ONE;
+ if (loopcxt_set_device(&lc, argv[optind]))
+ err(EXIT_FAILURE, _("%s: failed to use device"),
+ argv[optind]);
+ optind++;
+ }
+ if (!act) {
+ /*
+ * losetup <loopdev> <backing_file>
+ */
+ act = A_CREATE;
+
+ if (optind >= argc)
+ errx(EXIT_FAILURE, _("no loop device specified"));
+ if (loopcxt_set_device(&lc, argv[optind]))
+ err(EXIT_FAILURE, _("%s failed to use device"),
+ argv[optind]);
+ optind++;
+
+ if (optind >= argc)
+ errx(EXIT_FAILURE, _("no file specified"));
+ file = argv[optind++];
+ }
+
+ if (act != A_CREATE &&
+ (encryption || sizelimit || passfd != -1 || lo_flags || showdev))
+ errx(EXIT_FAILURE,
+ _("the options %s are allowed to loop device setup only"),
+ "--{encryption,sizelimit,pass-fd,read-only,show}");
+
+ if ((flags & LOOPDEV_FL_OFFSET) &&
+ act != A_CREATE && (act != A_SHOW || !file))
+ errx(EXIT_FAILURE, _("the option --offset is not allowed in this context."));
+
+ switch (act) {
+ case A_CREATE:
+ {
+ char *pass = NULL;
+ int hasdev = loopcxt_has_device(&lc);
+
+ if (encryption) {
+#ifdef MCL_FUTURE
+ if(mlockall(MCL_CURRENT | MCL_FUTURE))
+ err(EXIT_FAILURE, _("couldn't lock into memory"));
+#endif
+ pass = xgetpass(passfd, _("Password: "));
+ }
+ do {
+ /* Note that loopcxt_{find_unused,set_device}() resets
+ * loopcxt struct.
+ */
+ if (!hasdev && (res = loopcxt_find_unused(&lc))) {
+ warnx(_("not found unused device"));
+ break;
+ }
+ if (encryption && pass)
+ loopcxt_set_encryption(&lc, encryption, pass);
+ if (flags & LOOPDEV_FL_OFFSET)
+ loopcxt_set_offset(&lc, offset);
+ if (flags & LOOPDEV_FL_SIZELIMIT)
+ loopcxt_set_sizelimit(&lc, sizelimit);
+ if (lo_flags)
+ loopcxt_set_flags(&lc, lo_flags);
+ if ((res = loopcxt_set_backing_file(&lc, file))) {
+ warn(_("%s: failed to use backing file"), file);
+ break;
+ }
+ errno = 0;
+ res = loopcxt_setup_device(&lc);
+ if (res == 0)
+ break; /* success */
+ if (errno != EBUSY) {
+ warn(_("%s: failed to setup loop device"),
+ hasdev && loopcxt_get_fd(&lc) < 0 ?
+ loopcxt_get_device(&lc) : file);
+ break;
+ }
+ } while (hasdev == 0);
+
+ free(pass);
+
+ if (res == 0) {
+ if (showdev)
+ printf("%s\n", loopcxt_get_device(&lc));
+ warn_size(file, sizelimit);
+ }
+ break;
+ }
+ case A_DELETE:
+ res = delete_loop(&lc);
+ while (optind < argc) {
+ if (loopcxt_set_device(&lc, argv[optind]))
+ warn(_("%s: failed to use device"),
+ argv[optind]);
+ optind++;
+ res += delete_loop(&lc);
+ }
+ break;
+ case A_DELETE_ALL:
+ res = delete_all_loops(&lc);
+ break;
+ case A_FIND_FREE:
+ if (loopcxt_find_unused(&lc))
+ warn(_("find unused loop device failed"));
+ else
+ printf("%s\n", loopcxt_get_device(&lc));
+ break;
+ case A_SHOW:
+ res = show_all_loops(&lc, file, offset, flags);
+ break;
+ case A_SHOW_ONE:
+ res = printf_loopdev(&lc);
+ if (res)
+ warn(_("%s"), loopcxt_get_device(&lc));
+ break;
+ case A_SET_CAPACITY:
+ res = set_capacity(&lc);
+ break;
+ default:
+ usage(stderr);
+ break;
+ }
+
+ loopcxt_deinit(&lc);
+ return res ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
diff --git a/sys-utils/lp.h b/sys-utils/lp.h
new file mode 100644
index 0000000..7fa83dd
--- /dev/null
+++ b/sys-utils/lp.h
@@ -0,0 +1,83 @@
+/* Line printer stuff mostly follows the original Centronics printers. See
+ IEEE Std.1284-1994 Standard Signaling Method for a Bi-directional Parallel
+ Peripheral Interface for Personal Computers for 5 modes of data transfer. */
+
+/* Parallel port registers: data (0x3bc, 0x378, 0x278), status=data+1, control=data+2 */
+
+/* Parallel port status register (read only):
+bit 7: NBSY (1: ready, 0: busy or error or off-line)
+bit 6: NACK (if NBSY=1, then 1; if NBSY=0 then 1: sending data, 0: ready with data)
+bit 5: PAP (1: out-of-paper)
+bit 4: OFON (1: on-line)
+bit 3: NFEH (1: OK, 0: printer error)
+bits 2-0: 07
+
+On out-of-paper: PAP=1, OFON=0, NFEH=1.
+
+"When reading the busy status, read twice in a row and use the value
+obtained from the second read. This improves the reliability on some
+parallel ports when the busy line may not be connected and is floating."
+"On some Okidata printers when the busy signal switches from high to low,
+the printer sends a 50 ns glitch on the paper out signal line. Before
+declaring a paper out condition, check the status again."
+(The Undocumented PC, F. van Gilluwe, p. 711)
+*/
+
+/* Parallel port control register (read/write):
+bit 4: IRQ (1: we want an interrupt when NACK goes from 1 to 0)
+bit 3: DSL (1: activate printer)
+bit 2: NINI (0: initialise printer)
+bit 1: ALF (1: printer performs automatic linefeed after each line)
+bit 0: STR (0->1: generate a Strobe pulse: transport data to printer)
+*/
+
+/* Parallel port timing:
+ 1. wait for NBSY=1
+ 2. outb(data byte, data port)
+ 3. wait for at least 0.5us
+ 4. read control port, OR with STR=0x1, output to control port - purpose:
+ generate strobe pulse; this will make the busy line go high
+ 5. wait for at least 0.5us
+ 6. read control port, AND with !STR, output to control port
+ 7. wait for at least 0.5us
+ 8. in a loop: read status register until NACK bit is 0 or NBSY=1
+ (the printer will keep NACK=0 for at least 0.5us, then NACK=NBSY=1).
+*/
+
+
+/* lp ioctls */
+#define LPCHAR 0x0601 /* specify the number of times we ask for the status
+ (waiting for ready) before giving up with a timeout
+ The duration may mainly depend on the timing of an inb. */
+#define LPTIME 0x0602 /* time to sleep after each timeout (in units of 0.01 sec) */
+#define LPABORT 0x0604 /* call with TRUE arg to abort on error,
+ FALSE to retry. Default is retry. */
+#define LPSETIRQ 0x0605 /* call with new IRQ number,
+ or 0 for polling (no IRQ) */
+#define LPGETIRQ 0x0606 /* get the current IRQ number */
+#define LPWAIT 0x0608 /* #of loops to wait before taking strobe high */
+#define LPCAREFUL 0x0609 /* call with TRUE arg to require out-of-paper, off-
+ line, and error indicators good on all writes,
+ FALSE to ignore them. Default is ignore. */
+#define LPABORTOPEN 0x060a /* call with TRUE arg to abort open() on error,
+ FALSE to ignore error. Default is ignore. */
+#define LPGETSTATUS 0x060b /* return LP_S(minor) */
+#define LPRESET 0x060c /* reset printer */
+#define LPGETFLAGS 0x060e /* get status flags */
+
+#define LPSTRICT 0x060f /* enable/disable strict compliance (2.0.36) */
+ /* Strict: wait until !READY before taking strobe low;
+ this may be bad for the Epson Stylus 800.
+ Not strict: wait a constant time as given by LPWAIT */
+#define LPTRUSTIRQ 0x060f /* set/unset the LP_TRUST_IRQ flag (2.1.131) */
+
+/*
+ * bit defines for 8255 status port
+ * base + 1
+ * accessed with LP_S(minor), which gets the byte...
+ */
+#define LP_PBUSY 0x80 /* inverted input, active high */
+#define LP_PACK 0x40 /* unchanged input, active low */
+#define LP_POUTPA 0x20 /* unchanged input, active high */
+#define LP_PSELECD 0x10 /* unchanged input, active high */
+#define LP_PERRORP 0x08 /* unchanged input, active low */
diff --git a/sys-utils/lscpu.1 b/sys-utils/lscpu.1
new file mode 100644
index 0000000..f7e34b9
--- /dev/null
+++ b/sys-utils/lscpu.1
@@ -0,0 +1,149 @@
+.\" Process this file with
+.\" groff -man -Tascii lscpu.1
+.\"
+.TH LSCPU 1 "February 2011" "util-linux" "User Commands"
+.SH NAME
+lscpu \- display information about the CPU architecture
+.SH SYNOPSIS
+.B lscpu
+.RB [ \-a | \-b | \-c "] [" \-x "] [" \-s " \fIdirectory\fP] [" \-e " [\fI=list\fP]|" \-p " [\fI=list\fP]]"
+.br
+.B lscpu
+.BR \-h | \-V
+.SH DESCRIPTION
+.B lscpu
+gathers CPU architecture information from sysfs and /proc/cpuinfo. The
+command output can be optimized for parsing or for easy readability by humans.
+.br
+The information includes, for example, the number of CPUs, threads, cores,
+sockets, and Non-Uniform Memory Access (NUMA) nodes. There is also information
+about the CPU caches and cache sharing, family, model, bogoMIPS, byte order,
+and stepping.
+
+Options that result in an output table have a \fIlist\fP argument. Use this
+argument to customize the command output. Specify a comma-separated list of
+column labels to limit the output table to only the specified columns, arranged
+in the specified order. See \fBCOLUMNS\fP for a list of valid column labels. The
+column labels are not case sensitive.
+
+Not all columns are supported on all architectures. If an unsupported column is
+specified, \fBlscpu\fP prints the column but does not provide any data for it.
+
+.SS COLUMNS
+.TP
+.B CPU
+The logical CPU number of a CPU as used by the Linux kernel.
+.TP
+.B CORE
+The logical core number. A core can contain several CPUs.
+.TP
+.B SOCKET
+The logical socket number. A socket can contain several cores.
+.TP
+.B BOOK
+The logical book number. A book can contain several sockets.
+.TP
+.B NODE
+The logical NUMA node number. A node may contain several books.
+.TP
+.B CACHE
+Information about how caches are shared between CPUs.
+.TP
+.B ADDRESS
+The physical address of a CPU.
+.TP
+.B ONLINE
+Indicator that shows whether the Linux instance currently makes use of the CPU.
+.TP
+.B CONFIGURED
+Indicator that shows if the hypervisor has allocated the CPU to the virtual
+hardware on which the Linux instance runs. CPUs that are configured can be set
+online by the Linux instance.
+This column contains data only if your hardware system and hypervisor support
+dynamic CPU resource allocation.
+.TP
+.B POLARIZATION
+This column contains data for Linux instances that run on virtual hardware with
+a hypervisor that can switch the CPU dispatching mode (polarization). The
+polarization can be:
+.RS
+.TP 12
+.B horizontal\fP
+The workload is spread across all available CPUs.
+.TP 12
+.B vertical
+The workload is concentrated on few CPUs.
+.P
+For vertical polarization, the column also shows the degree of concentration,
+high, medium, or low. This column contains data only if your hardware system
+and hypervisor support CPU polarization.
+.RE
+.SH OPTIONS
+.TP
+.BR \-a , " \-\-all"
+Include lines for online and offline CPUs in the output (default for -e). This
+option may only specified together with option -e or -p.
+.TP
+.BR \-b , " \-\-online"
+Limit the output to online CPUs (default for -p). This option may only
+be specified together with option -e or -p.
+.TP
+.BR \-c , " \-\-offline"
+Limit the output to offline CPUs. This option may only be specified together
+with option -e or -p.
+.TP
+.BR \-e , " \-\-extended " \fI[=list]\fP
+Display the CPU information in human readable format.
+
+If the \fIlist\fP argument is omitted, all columns for which data is available
+are included in the command output.
+
+When specifying the \fIlist\fP argument, the string of option, equal sign (=), and
+\fIlist\fP must not contain any blanks or other white space.
+Examples: '\fB-e=cpu,node\fP' or '\fB--extended=cpu,node\fP'.
+.TP
+.BR \-h , " \-\-help"
+Display help information and exit.
+.TP
+.BR \-p , " \-\-parse " \fI[=list]\fP
+Optimize the command output for easy parsing.
+
+If the \fIlist\fP argument is omitted, the command output is compatible with earlier
+versions of \fBlscpu\fP. In this compatible format, two commas are used to separate
+CPU cache columns. If no CPU caches are identified the cache column is omitted.
+.br
+If the \fIlist\fP argument is used, cache columns are separated with a colon (:).
+
+When specifying the \fIlist\fP argument, the string of option, equal sign (=), and
+\fIlist\fP must not contain any blanks or other white space.
+Examples: '\fB-p=cpu,node\fP' or '\fB--parse=cpu,node\fP'.
+.TP
+.BR \-s , " \-\-sysroot " \fIdirectory\fP
+Gather CPU data for a Linux instance other than the instance from which the
+\fBlscpu\fP command is issued. The specified \fIdirectory\fP is the system root
+of the Linux instance to be inspected.
+.TP
+.BR \-x , " \-\-hex"
+Use hexadecimal masks for CPU sets (for example 0x3). The default is to print
+the sets in list format (for example 0,1).
+.TP
+.BR \-V , " \-\-version"
+Display version information and exit.
+.SH BUGS
+The basic overview about CPU family, model, etc. is always based on the first
+CPU only.
+
+Sometimes in Xen Dom0 the kernel reports wrong data.
+
+On virtual hardware the number of cores per socket, etc. can be wrong.
+.SH AUTHOR
+.nf
+Cai Qian <qcai@redhat.com>
+Karel Zak <kzak@redhat.com>
+Heiko Carstens <heiko.carstens@de.ibm.com>
+.fi
+.SH "SEE ALSO"
+.BR chcpu (8)
+.SH AVAILABILITY
+The lscpu command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/sys-utils/lscpu.c b/sys-utils/lscpu.c
new file mode 100644
index 0000000..25a0273
--- /dev/null
+++ b/sys-utils/lscpu.c
@@ -0,0 +1,1400 @@
+/*
+ * lscpu - CPU architecture information helper
+ *
+ * Copyright (C) 2008 Cai Qian <qcai@redhat.com>
+ * Copyright (C) 2008 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 <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "cpuset.h"
+#include "nls.h"
+#include "xalloc.h"
+#include "c.h"
+#include "strutils.h"
+#include "bitops.h"
+#include "tt.h"
+#include "path.h"
+#include "closestream.h"
+#include "optutils.h"
+
+#define CACHE_MAX 100
+
+/* /sys paths */
+#define _PATH_SYS_SYSTEM "/sys/devices/system"
+#define _PATH_SYS_CPU _PATH_SYS_SYSTEM "/cpu"
+#define _PATH_PROC_XEN "/proc/xen"
+#define _PATH_PROC_XENCAP _PATH_PROC_XEN "/capabilities"
+#define _PATH_PROC_CPUINFO "/proc/cpuinfo"
+#define _PATH_PROC_PCIDEVS "/proc/bus/pci/devices"
+#define _PATH_PROC_SYSINFO "/proc/sysinfo"
+
+/* virtualization types */
+enum {
+ VIRT_NONE = 0,
+ VIRT_PARA,
+ VIRT_FULL
+};
+const char *virt_types[] = {
+ [VIRT_NONE] = N_("none"),
+ [VIRT_PARA] = N_("para"),
+ [VIRT_FULL] = N_("full")
+};
+
+/* hypervisor vendors */
+enum {
+ HYPER_NONE = 0,
+ HYPER_XEN,
+ HYPER_KVM,
+ HYPER_MSHV,
+ HYPER_VMWARE,
+ HYPER_IBM
+};
+const char *hv_vendors[] = {
+ [HYPER_NONE] = NULL,
+ [HYPER_XEN] = "Xen",
+ [HYPER_KVM] = "KVM",
+ [HYPER_MSHV] = "Microsoft",
+ [HYPER_VMWARE] = "VMware",
+ [HYPER_IBM] = "IBM"
+};
+
+/* CPU modes */
+enum {
+ MODE_32BIT = (1 << 1),
+ MODE_64BIT = (1 << 2)
+};
+
+/* cache(s) description */
+struct cpu_cache {
+ char *name;
+ char *size;
+
+ int nsharedmaps;
+ cpu_set_t **sharedmaps;
+};
+
+/* dispatching modes */
+enum {
+ DISP_HORIZONTAL = 0,
+ DISP_VERTICAL = 1
+};
+
+const char *disp_modes[] = {
+ [DISP_HORIZONTAL] = N_("horizontal"),
+ [DISP_VERTICAL] = N_("vertical")
+};
+
+/* cpu polarization */
+enum {
+ POLAR_UNKNOWN = 0,
+ POLAR_VLOW,
+ POLAR_VMEDIUM,
+ POLAR_VHIGH,
+ POLAR_HORIZONTAL
+};
+
+struct polarization_modes {
+ char *parsable;
+ char *readable;
+};
+
+struct polarization_modes polar_modes[] = {
+ [POLAR_UNKNOWN] = {"U", "-"},
+ [POLAR_VLOW] = {"VL", "vert-low"},
+ [POLAR_VMEDIUM] = {"VM", "vert-medium"},
+ [POLAR_VHIGH] = {"VH", "vert-high"},
+ [POLAR_HORIZONTAL] = {"H", "horizontal"},
+};
+
+/* global description */
+struct lscpu_desc {
+ char *arch;
+ char *vendor;
+ char *family;
+ char *model;
+ char *virtflag; /* virtualization flag (vmx, svm) */
+ char *hypervisor; /* hypervisor software */
+ int hyper; /* hypervisor vendor ID */
+ int virtype; /* VIRT_PARA|FULL|NONE ? */
+ char *mhz;
+ char *stepping;
+ char *bogomips;
+ char *flags;
+ int dispatching; /* none, horizontal or vertical */
+ int mode; /* rm, lm or/and tm */
+
+ int ncpus; /* number of CPUs */
+ cpu_set_t *online; /* mask with online CPUs */
+
+ int nnodes; /* number of NUMA modes */
+ cpu_set_t **nodemaps; /* array with NUMA nodes */
+
+ /* books -- based on book_siblings (internal kernel map of cpuX's
+ * hardware threads within the same book */
+ int nbooks; /* number of all online books */
+ cpu_set_t **bookmaps; /* unique book_siblings */
+
+ /* sockets -- based on core_siblings (internal kernel map of cpuX's
+ * hardware threads within the same physical_package_id (socket)) */
+ int nsockets; /* number of all online sockets */
+ cpu_set_t **socketmaps; /* unique core_siblings */
+
+ /* cores -- based on thread_siblings (internel kernel map of cpuX's
+ * hardware threads within the same core as cpuX) */
+ int ncores; /* number of all online cores */
+ cpu_set_t **coremaps; /* unique thread_siblings */
+
+ int nthreads; /* number of online threads */
+
+ int ncaches;
+ struct cpu_cache *caches;
+
+ int *polarization; /* cpu polarization */
+ int *addresses; /* physical cpu addresses */
+ int *configured; /* cpu configured */
+};
+
+enum {
+ OUTPUT_SUMMARY = 0, /* default */
+ OUTPUT_PARSABLE, /* -p */
+ OUTPUT_READABLE, /* -e */
+};
+
+enum {
+ SYSTEM_LIVE = 0, /* analyzing a live system */
+ SYSTEM_SNAPSHOT, /* analyzing a snapshot of a different system */
+};
+
+struct lscpu_modifier {
+ int mode; /* OUTPUT_* */
+ int system; /* SYSTEM_* */
+ unsigned int hex:1, /* print CPU masks rather than CPU lists */
+ compat:1, /* use backwardly compatible format */
+ online:1, /* print online CPUs */
+ offline:1; /* print offline CPUs */
+};
+
+static int maxcpus; /* size in bits of kernel cpu mask */
+
+#define is_cpu_online(_d, _cpu) \
+ ((_d) && (_d)->online ? \
+ CPU_ISSET_S((_cpu), CPU_ALLOC_SIZE(maxcpus), (_d)->online) : 0)
+
+/*
+ * IDs
+ */
+enum {
+ COL_CPU,
+ COL_CORE,
+ COL_SOCKET,
+ COL_NODE,
+ COL_BOOK,
+ COL_CACHE,
+ COL_POLARIZATION,
+ COL_ADDRESS,
+ COL_CONFIGURED,
+ COL_ONLINE,
+};
+
+/* column description
+ */
+struct lscpu_coldesc {
+ const char *name;
+ const char *help;
+
+ unsigned int is_abbr:1; /* name is abbreviation */
+};
+
+static struct lscpu_coldesc coldescs[] =
+{
+ [COL_CPU] = { "CPU", N_("logical CPU number"), 1 },
+ [COL_CORE] = { "CORE", N_("logical core number") },
+ [COL_SOCKET] = { "SOCKET", N_("logical socket number") },
+ [COL_NODE] = { "NODE", N_("logical NUMA node number") },
+ [COL_BOOK] = { "BOOK", N_("logical book number") },
+ [COL_CACHE] = { "CACHE", N_("shows how caches are shared between CPUs") },
+ [COL_POLARIZATION] = { "POLARIZATION", N_("CPU dispatching mode on virtual hardware") },
+ [COL_ADDRESS] = { "ADDRESS", N_("physical address of a CPU") },
+ [COL_CONFIGURED] = { "CONFIGURED", N_("shows if the hypervisor has allocated the CPU") },
+ [COL_ONLINE] = { "ONLINE", N_("shows if Linux currently makes use of the CPU") }
+};
+
+static int
+column_name_to_id(const char *name, size_t namesz)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(coldescs); i++) {
+ const char *cn = coldescs[i].name;
+
+ if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
+ return i;
+ }
+ warnx(_("unknown column: %s"), name);
+ return -1;
+}
+
+/* Lookup a pattern and get the value from cpuinfo.
+ * Format is:
+ *
+ * "<pattern> : <key>"
+ */
+static int
+lookup(char *line, char *pattern, char **value)
+{
+ char *p, *v;
+ int len = strlen(pattern);
+
+ if (!*line)
+ return 0;
+
+ /* pattern */
+ if (strncmp(line, pattern, len))
+ return 0;
+
+ /* white spaces */
+ for (p = line + len; isspace(*p); p++);
+
+ /* separator */
+ if (*p != ':')
+ return 0;
+
+ /* white spaces */
+ for (++p; isspace(*p); p++);
+
+ /* value */
+ if (!*p)
+ return 0;
+ v = p;
+
+ /* end of value */
+ len = strlen(line) - 1;
+ for (p = line + len; isspace(*(p-1)); p--);
+ *p = '\0';
+
+ *value = xstrdup(v);
+ return 1;
+}
+
+/* Don't init the mode for platforms where we are not able to
+ * detect that CPU supports 64-bit mode.
+ */
+static int
+init_mode(struct lscpu_modifier *mod)
+{
+ int m = 0;
+
+ if (mod->system == SYSTEM_SNAPSHOT)
+ /* reading info from any /{sys,proc} dump, don't mix it with
+ * information about our real CPU */
+ return 0;
+
+#if defined(__alpha__) || defined(__ia64__)
+ m |= MODE_64BIT; /* 64bit platforms only */
+#endif
+ /* platforms with 64bit flag in /proc/cpuinfo, define
+ * 32bit default here */
+#if defined(__i386__) || defined(__x86_64__) || \
+ defined(__s390x__) || defined(__s390__) || defined(__sparc_v9__)
+ m |= MODE_32BIT;
+#endif
+ return m;
+}
+
+static void
+read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod)
+{
+ FILE *fp = path_fopen("r", 1, _PATH_PROC_CPUINFO);
+ char buf[BUFSIZ];
+ struct utsname utsbuf;
+
+ /* architecture */
+ if (uname(&utsbuf) == -1)
+ err(EXIT_FAILURE, _("error: uname failed"));
+ desc->arch = xstrdup(utsbuf.machine);
+
+ /* count CPU(s) */
+ while(path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d", desc->ncpus))
+ desc->ncpus++;
+
+ /* details */
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ if (lookup(buf, "vendor", &desc->vendor)) ;
+ else if (lookup(buf, "vendor_id", &desc->vendor)) ;
+ else if (lookup(buf, "family", &desc->family)) ;
+ else if (lookup(buf, "cpu family", &desc->family)) ;
+ else if (lookup(buf, "model", &desc->model)) ;
+ else if (lookup(buf, "stepping", &desc->stepping)) ;
+ else if (lookup(buf, "cpu MHz", &desc->mhz)) ;
+ else if (lookup(buf, "flags", &desc->flags)) ; /* x86 */
+ else if (lookup(buf, "features", &desc->flags)) ; /* s390 */
+ else if (lookup(buf, "type", &desc->flags)) ; /* sparc64 */
+ else if (lookup(buf, "bogomips", &desc->bogomips)) ;
+ else if (lookup(buf, "bogomips per cpu", &desc->bogomips)) ; /* s390 */
+ else
+ continue;
+ }
+
+ desc->mode = init_mode(mod);
+
+ if (desc->flags) {
+ snprintf(buf, sizeof(buf), " %s ", desc->flags);
+ if (strstr(buf, " svm "))
+ desc->virtflag = xstrdup("svm");
+ else if (strstr(buf, " vmx "))
+ desc->virtflag = xstrdup("vmx");
+ if (strstr(buf, " lm "))
+ desc->mode |= MODE_32BIT | MODE_64BIT; /* x86_64 */
+ if (strstr(buf, " zarch "))
+ desc->mode |= MODE_32BIT | MODE_64BIT; /* s390x */
+ if (strstr(buf, " sun4v ") || strstr(buf, " sun4u "))
+ desc->mode |= MODE_32BIT | MODE_64BIT; /* sparc64 */
+ }
+
+ fclose(fp);
+
+ if (path_exist(_PATH_SYS_SYSTEM "/cpu/kernel_max"))
+ /* note that kernel_max is maximum index [NR_CPUS-1] */
+ maxcpus = path_getnum(_PATH_SYS_SYSTEM "/cpu/kernel_max") + 1;
+
+ else if (mod->system == SYSTEM_LIVE)
+ /* the root is '/' so we are working with data from the current kernel */
+ maxcpus = get_max_number_of_cpus();
+
+ if (maxcpus <= 0)
+ /* error or we are reading some /sys snapshot instead of the
+ * real /sys, let's use any crazy number... */
+ maxcpus = desc->ncpus > 2048 ? desc->ncpus : 2048;
+
+ /* get mask for online CPUs */
+ if (path_exist(_PATH_SYS_SYSTEM "/cpu/online")) {
+ size_t setsize = CPU_ALLOC_SIZE(maxcpus);
+ desc->online = path_cpulist(maxcpus, _PATH_SYS_SYSTEM "/cpu/online");
+ desc->nthreads = CPU_COUNT_S(setsize, desc->online);
+ }
+
+ /* get dispatching mode */
+ if (path_exist(_PATH_SYS_SYSTEM "/cpu/dispatching"))
+ desc->dispatching = path_getnum(_PATH_SYS_SYSTEM "/cpu/dispatching");
+ else
+ desc->dispatching = -1;
+}
+
+static int
+has_pci_device(unsigned int vendor, unsigned int device)
+{
+ FILE *f;
+ unsigned int num, fn, ven, dev;
+ int res = 1;
+
+ f = path_fopen("r", 0, _PATH_PROC_PCIDEVS);
+ if (!f)
+ return 0;
+
+ /* for more details about bus/pci/devices format see
+ * drivers/pci/proc.c in linux kernel
+ */
+ while(fscanf(f, "%02x%02x\t%04x%04x\t%*[^\n]",
+ &num, &fn, &ven, &dev) == 4) {
+
+ if (ven == vendor && dev == device)
+ goto found;
+ }
+
+ res = 0;
+found:
+ fclose(f);
+ return res;
+}
+
+#if defined(__x86_64__) || defined(__i386__)
+
+/*
+ * This CPUID leaf returns the information about the hypervisor.
+ * EAX : maximum input value for CPUID supported by the hypervisor.
+ * EBX, ECX, EDX : Hypervisor vendor ID signature. E.g. VMwareVMware.
+ */
+#define HYPERVISOR_INFO_LEAF 0x40000000
+
+static inline void
+cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx,
+ unsigned int *ecx, unsigned int *edx)
+{
+ __asm__(
+#if defined(__PIC__) && defined(__i386__)
+ /* x86 PIC cannot clobber ebx -- gcc bitches */
+ "pushl %%ebx;"
+ "cpuid;"
+ "movl %%ebx, %%esi;"
+ "popl %%ebx;"
+ : "=S" (*ebx),
+#else
+ "cpuid;"
+ : "=b" (*ebx),
+#endif
+ "=a" (*eax),
+ "=c" (*ecx),
+ "=d" (*edx)
+ : "1" (op), "c"(0));
+}
+
+static void
+read_hypervisor_cpuid(struct lscpu_desc *desc)
+{
+ unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0;
+ char hyper_vendor_id[13];
+
+ memset(hyper_vendor_id, 0, sizeof(hyper_vendor_id));
+
+ cpuid(HYPERVISOR_INFO_LEAF, &eax, &ebx, &ecx, &edx);
+ memcpy(hyper_vendor_id + 0, &ebx, 4);
+ memcpy(hyper_vendor_id + 4, &ecx, 4);
+ memcpy(hyper_vendor_id + 8, &edx, 4);
+ hyper_vendor_id[12] = '\0';
+
+ if (!hyper_vendor_id[0])
+ return;
+
+ if (!strncmp("XenVMMXenVMM", hyper_vendor_id, 12))
+ desc->hyper = HYPER_XEN;
+ else if (!strncmp("KVMKVMKVM", hyper_vendor_id, 9))
+ desc->hyper = HYPER_KVM;
+ else if (!strncmp("Microsoft Hv", hyper_vendor_id, 12))
+ desc->hyper = HYPER_MSHV;
+ else if (!strncmp("VMwareVMware", hyper_vendor_id, 12))
+ desc->hyper = HYPER_VMWARE;
+}
+
+#else /* ! __x86_64__ */
+static void
+read_hypervisor_cpuid(struct lscpu_desc *desc)
+{
+}
+#endif
+
+static void
+read_hypervisor(struct lscpu_desc *desc)
+{
+ read_hypervisor_cpuid(desc);
+
+ if (desc->hyper)
+ /* hvm */
+ desc->virtype = VIRT_FULL;
+
+ else if (path_exist(_PATH_PROC_XEN)) {
+ /* Xen para-virt or dom0 */
+ FILE *fd = path_fopen("r", 0, _PATH_PROC_XENCAP);
+ int dom0 = 0;
+
+ if (fd) {
+ char buf[256];
+
+ if (fscanf(fd, "%s", buf) == 1 &&
+ !strcmp(buf, "control_d"))
+ dom0 = 1;
+ fclose(fd);
+ }
+ desc->virtype = dom0 ? VIRT_NONE : VIRT_PARA;
+ desc->hyper = HYPER_XEN;
+
+ } else if (has_pci_device(0x5853, 0x0001)) {
+ /* Xen full-virt on non-x86_64 */
+ desc->hyper = HYPER_XEN;
+ desc->virtype = VIRT_FULL;
+ } else if (path_exist(_PATH_PROC_SYSINFO)) {
+ FILE *fd = path_fopen("r", 0, _PATH_PROC_SYSINFO);
+ char buf[BUFSIZ];
+
+ desc->hyper = HYPER_IBM;
+ desc->hypervisor = "PR/SM";
+ desc->virtype = VIRT_FULL;
+ while (fgets(buf, sizeof(buf), fd) != NULL) {
+ char *str;
+
+ if (!strstr(buf, "Control Program:"))
+ continue;
+ if (!strstr(buf, "KVM"))
+ desc->hyper = HYPER_IBM;
+ else
+ desc->hyper = HYPER_KVM;
+ str = strchr(buf, ':');
+ if (!str)
+ continue;
+ if (xasprintf(&str, "%s", str + 1) == -1)
+ errx(EXIT_FAILURE, _("failed to allocate memory"));
+ /* remove leading, trailing and repeating whitespace */
+ while (*str == ' ')
+ str++;
+ desc->hypervisor = str;
+ str += strlen(str) - 1;
+ while ((*str == '\n') || (*str == ' '))
+ *(str--) = '\0';
+ while ((str = strstr(desc->hypervisor, " ")))
+ memmove(str, str + 1, strlen(str));
+ }
+ fclose(fd);
+ }
+}
+
+/* add @set to the @ary, unnecessary set is deallocated. */
+static int add_cpuset_to_array(cpu_set_t **ary, int *items, cpu_set_t *set)
+{
+ int i;
+ size_t setsize = CPU_ALLOC_SIZE(maxcpus);
+
+ if (!ary)
+ return -1;
+
+ for (i = 0; i < *items; i++) {
+ if (CPU_EQUAL_S(setsize, set, ary[i]))
+ break;
+ }
+ if (i == *items) {
+ ary[*items] = set;
+ ++*items;
+ return 0;
+ }
+ CPU_FREE(set);
+ return 1;
+}
+
+static void
+read_topology(struct lscpu_desc *desc, int num)
+{
+ cpu_set_t *thread_siblings, *core_siblings, *book_siblings;
+
+ if (!path_exist(_PATH_SYS_CPU "/cpu%d/topology/thread_siblings", num))
+ return;
+
+ thread_siblings = path_cpuset(maxcpus, _PATH_SYS_CPU
+ "/cpu%d/topology/thread_siblings", num);
+ core_siblings = path_cpuset(maxcpus, _PATH_SYS_CPU
+ "/cpu%d/topology/core_siblings", num);
+ book_siblings = NULL;
+ if (path_exist(_PATH_SYS_CPU "/cpu%d/topology/book_siblings", num)) {
+ book_siblings = path_cpuset(maxcpus, _PATH_SYS_CPU
+ "/cpu%d/topology/book_siblings", num);
+ }
+
+ if (!desc->coremaps) {
+ int nbooks, nsockets, ncores, nthreads;
+ size_t setsize = CPU_ALLOC_SIZE(maxcpus);
+
+ /* threads within one core */
+ nthreads = CPU_COUNT_S(setsize, thread_siblings);
+ /* cores within one socket */
+ ncores = CPU_COUNT_S(setsize, core_siblings) / nthreads;
+ /* number of sockets within one book.
+ * Because of odd / non-present cpu maps and to keep
+ * calculation easy we make sure that nsockets and
+ * nbooks is at least 1.
+ */
+ nsockets = desc->ncpus / nthreads / ncores ?: 1;
+ /* number of books */
+ nbooks = desc->ncpus / nthreads / ncores / nsockets ?: 1;
+
+ /* all threads, see also read_basicinfo()
+ * -- fallback for kernels without
+ * /sys/devices/system/cpu/online.
+ */
+ if (!desc->nthreads)
+ desc->nthreads = nbooks * nsockets * ncores * nthreads;
+ /* For each map we make sure that it can have up to ncpus
+ * entries. This is because we cannot reliably calculate the
+ * number of cores, sockets and books on all architectures.
+ * E.g. completely virtualized architectures like s390 may
+ * have multiple sockets of different sizes.
+ */
+ desc->coremaps = xcalloc(desc->ncpus, sizeof(cpu_set_t *));
+ desc->socketmaps = xcalloc(desc->ncpus, sizeof(cpu_set_t *));
+ if (book_siblings)
+ desc->bookmaps = xcalloc(desc->ncpus, sizeof(cpu_set_t *));
+ }
+
+ add_cpuset_to_array(desc->socketmaps, &desc->nsockets, core_siblings);
+ add_cpuset_to_array(desc->coremaps, &desc->ncores, thread_siblings);
+ if (book_siblings)
+ add_cpuset_to_array(desc->bookmaps, &desc->nbooks, book_siblings);
+}
+static void
+read_polarization(struct lscpu_desc *desc, int num)
+{
+ char mode[64];
+
+ if (desc->dispatching < 0)
+ return;
+ if (!path_exist(_PATH_SYS_CPU "/cpu%d/polarization", num))
+ return;
+ if (!desc->polarization)
+ desc->polarization = xcalloc(desc->ncpus, sizeof(int));
+ path_getstr(mode, sizeof(mode), _PATH_SYS_CPU "/cpu%d/polarization", num);
+ if (strncmp(mode, "vertical:low", sizeof(mode)) == 0)
+ desc->polarization[num] = POLAR_VLOW;
+ else if (strncmp(mode, "vertical:medium", sizeof(mode)) == 0)
+ desc->polarization[num] = POLAR_VMEDIUM;
+ else if (strncmp(mode, "vertical:high", sizeof(mode)) == 0)
+ desc->polarization[num] = POLAR_VHIGH;
+ else if (strncmp(mode, "horizontal", sizeof(mode)) == 0)
+ desc->polarization[num] = POLAR_HORIZONTAL;
+ else
+ desc->polarization[num] = POLAR_UNKNOWN;
+}
+
+static void
+read_address(struct lscpu_desc *desc, int num)
+{
+ if (!path_exist(_PATH_SYS_CPU "/cpu%d/address", num))
+ return;
+ if (!desc->addresses)
+ desc->addresses = xcalloc(desc->ncpus, sizeof(int));
+ desc->addresses[num] = path_getnum(_PATH_SYS_CPU "/cpu%d/address", num);
+}
+
+static void
+read_configured(struct lscpu_desc *desc, int num)
+{
+ if (!path_exist(_PATH_SYS_CPU "/cpu%d/configure", num))
+ return;
+ if (!desc->configured)
+ desc->configured = xcalloc(desc->ncpus, sizeof(int));
+ desc->configured[num] = path_getnum(_PATH_SYS_CPU "/cpu%d/configure", num);
+}
+
+static int
+cachecmp(const void *a, const void *b)
+{
+ struct cpu_cache *c1 = (struct cpu_cache *) a;
+ struct cpu_cache *c2 = (struct cpu_cache *) b;
+
+ return strcmp(c2->name, c1->name);
+}
+
+static void
+read_cache(struct lscpu_desc *desc, int num)
+{
+ char buf[256];
+ int i;
+
+ if (!desc->ncaches) {
+ while(path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d/cache/index%d",
+ num, desc->ncaches))
+ desc->ncaches++;
+
+ if (!desc->ncaches)
+ return;
+
+ desc->caches = xcalloc(desc->ncaches, sizeof(*desc->caches));
+ }
+ for (i = 0; i < desc->ncaches; i++) {
+ struct cpu_cache *ca = &desc->caches[i];
+ cpu_set_t *map;
+
+ if (!path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d/cache/index%d",
+ num, i))
+ continue;
+ if (!ca->name) {
+ int type, level;
+
+ /* cache type */
+ path_getstr(buf, sizeof(buf),
+ _PATH_SYS_CPU "/cpu%d/cache/index%d/type",
+ num, i);
+ if (!strcmp(buf, "Data"))
+ type = 'd';
+ else if (!strcmp(buf, "Instruction"))
+ type = 'i';
+ else
+ type = 0;
+
+ /* cache level */
+ level = path_getnum(_PATH_SYS_CPU "/cpu%d/cache/index%d/level",
+ num, i);
+ if (type)
+ snprintf(buf, sizeof(buf), "L%d%c", level, type);
+ else
+ snprintf(buf, sizeof(buf), "L%d", level);
+
+ ca->name = xstrdup(buf);
+
+ /* cache size */
+ path_getstr(buf, sizeof(buf),
+ _PATH_SYS_CPU "/cpu%d/cache/index%d/size",
+ num, i);
+ ca->size = xstrdup(buf);
+ }
+
+ /* information about how CPUs share different caches */
+ map = path_cpuset(maxcpus,
+ _PATH_SYS_CPU "/cpu%d/cache/index%d/shared_cpu_map",
+ num, i);
+
+ if (!ca->sharedmaps)
+ ca->sharedmaps = xcalloc(desc->ncpus, sizeof(cpu_set_t *));
+ add_cpuset_to_array(ca->sharedmaps, &ca->nsharedmaps, map);
+ }
+}
+
+static void
+read_nodes(struct lscpu_desc *desc)
+{
+ int i;
+
+ /* number of NUMA node */
+ while (path_exist(_PATH_SYS_SYSTEM "/node/node%d", desc->nnodes))
+ desc->nnodes++;
+
+ if (!desc->nnodes)
+ return;
+
+ desc->nodemaps = xcalloc(desc->nnodes, sizeof(cpu_set_t *));
+
+ /* information about how nodes share different CPUs */
+ for (i = 0; i < desc->nnodes; i++)
+ desc->nodemaps[i] = path_cpuset(maxcpus,
+ _PATH_SYS_SYSTEM "/node/node%d/cpumap",
+ i);
+}
+
+static char *
+get_cell_data(struct lscpu_desc *desc, int cpu, int col,
+ struct lscpu_modifier *mod,
+ char *buf, size_t bufsz)
+{
+ size_t setsize = CPU_ALLOC_SIZE(maxcpus);
+ size_t idx;
+
+ *buf = '\0';
+
+ switch (col) {
+ case COL_CPU:
+ snprintf(buf, bufsz, "%d", cpu);
+ break;
+ case COL_CORE:
+ if (cpuset_ary_isset(cpu, desc->coremaps,
+ desc->ncores, setsize, &idx) == 0)
+ snprintf(buf, bufsz, "%zd", idx);
+ break;
+ case COL_SOCKET:
+ if (cpuset_ary_isset(cpu, desc->socketmaps,
+ desc->nsockets, setsize, &idx) == 0)
+ snprintf(buf, bufsz, "%zd", idx);
+ break;
+ case COL_NODE:
+ if (cpuset_ary_isset(cpu, desc->nodemaps,
+ desc->nnodes, setsize, &idx) == 0)
+ snprintf(buf, bufsz, "%zd", idx);
+ break;
+ case COL_BOOK:
+ if (cpuset_ary_isset(cpu, desc->bookmaps,
+ desc->nbooks, setsize, &idx) == 0)
+ snprintf(buf, bufsz, "%zd", idx);
+ break;
+ case COL_CACHE:
+ {
+ char *p = buf;
+ size_t sz = bufsz;
+ int j;
+
+ for (j = desc->ncaches - 1; j >= 0; j--) {
+ struct cpu_cache *ca = &desc->caches[j];
+
+ if (cpuset_ary_isset(cpu, ca->sharedmaps,
+ ca->nsharedmaps, setsize, &idx) == 0) {
+ int x = snprintf(p, sz, "%zd", idx);
+ if (x <= 0 || (size_t) x + 2 >= sz)
+ return NULL;
+ p += x;
+ sz -= x;
+ }
+ if (j != 0) {
+ *p++ = mod->compat ? ',' : ':';
+ *p = '\0';
+ sz++;
+ }
+ }
+ break;
+ }
+ case COL_POLARIZATION:
+ if (desc->polarization) {
+ int x = desc->polarization[cpu];
+
+ snprintf(buf, bufsz, "%s",
+ mod->mode == OUTPUT_PARSABLE ?
+ polar_modes[x].parsable :
+ polar_modes[x].readable);
+ }
+ break;
+ case COL_ADDRESS:
+ if (desc->addresses)
+ snprintf(buf, bufsz, "%d", desc->addresses[cpu]);
+ break;
+ case COL_CONFIGURED:
+ if (!desc->configured)
+ break;
+ if (mod->mode == OUTPUT_PARSABLE)
+ snprintf(buf, bufsz,
+ desc->configured[cpu] ? _("Y") : _("N"));
+ else
+ snprintf(buf, bufsz,
+ desc->configured[cpu] ? _("yes") : _("no"));
+ break;
+ case COL_ONLINE:
+ if (!desc->online)
+ break;
+ if (mod->mode == OUTPUT_PARSABLE)
+ snprintf(buf, bufsz,
+ is_cpu_online(desc, cpu) ? _("Y") : _("N"));
+ else
+ snprintf(buf, bufsz,
+ is_cpu_online(desc, cpu) ? _("yes") : _("no"));
+ break;
+ }
+ return buf;
+}
+
+static char *
+get_cell_header(struct lscpu_desc *desc, int col,
+ struct lscpu_modifier *mod,
+ char *buf, size_t bufsz)
+{
+ *buf = '\0';
+
+ if (col == COL_CACHE) {
+ char *p = buf;
+ size_t sz = bufsz;
+ int i;
+
+ for (i = desc->ncaches - 1; i >= 0; i--) {
+ int x = snprintf(p, sz, "%s", desc->caches[i].name);
+ if (x <= 0 || (size_t) x + 2 > sz)
+ return NULL;
+ sz -= x;
+ p += x;
+ if (i > 0) {
+ *p++ = mod->compat ? ',' : ':';
+ *p = '\0';
+ sz++;
+ }
+ }
+ if (desc->ncaches)
+ return buf;
+ }
+ snprintf(buf, bufsz, "%s", coldescs[col].name);
+ return buf;
+}
+
+/*
+ * [-p] backend, we support two parsable formats:
+ *
+ * 1) "compatible" -- this format is compatible with the original lscpu(1)
+ * output and it contains fixed set of the columns. The CACHE columns are at
+ * the end of the line and the CACHE is not printed if the number of the caches
+ * is zero. The CACHE columns are separated by two commas, for example:
+ *
+ * $ lscpu --parse
+ * # CPU,Core,Socket,Node,,L1d,L1i,L2
+ * 0,0,0,0,,0,0,0
+ * 1,1,0,0,,1,1,0
+ *
+ * 2) "user defined output" -- this format prints always all columns without
+ * special prefix for CACHE column. If there are not CACHEs then the column is
+ * empty and the header "Cache" is printed rather than a real name of the cache.
+ * The CACHE columns are separated by ':'.
+ *
+ * $ lscpu --parse=CPU,CORE,SOCKET,NODE,CACHE
+ * # CPU,Core,Socket,Node,L1d:L1i:L2
+ * 0,0,0,0,0:0:0
+ * 1,1,0,0,1:1:0
+ */
+static void
+print_parsable(struct lscpu_desc *desc, int cols[], int ncols,
+ struct lscpu_modifier *mod)
+{
+ char buf[BUFSIZ], *data;
+ int i;
+
+ /*
+ * Header
+ */
+ printf(_(
+ "# The following is the parsable format, which can be fed to other\n"
+ "# programs. Each different item in every column has an unique ID\n"
+ "# starting from zero.\n"));
+
+ fputs("# ", stdout);
+ for (i = 0; i < ncols; i++) {
+ int col = cols[i];
+
+ if (col == COL_CACHE) {
+ if (mod->compat && !desc->ncaches)
+ continue;
+ if (mod->compat && i != 0)
+ putchar(',');
+ }
+ if (i > 0)
+ putchar(',');
+
+ data = get_cell_header(desc, col, mod, buf, sizeof(buf));
+
+ if (data && * data && col != COL_CACHE &&
+ !coldescs[col].is_abbr) {
+ /*
+ * For normal column names use mixed case (e.g. "Socket")
+ */
+ char *p = data + 1;
+
+ while (p && *p != '\0') {
+ *p = tolower((unsigned int) *p);
+ p++;
+ }
+ }
+ fputs(data && *data ? data : "", stdout);
+ }
+ putchar('\n');
+
+ /*
+ * Data
+ */
+ for (i = 0; i < desc->ncpus; i++) {
+ int c;
+
+ if (!mod->offline && desc->online && !is_cpu_online(desc, i))
+ continue;
+ if (!mod->online && desc->online && is_cpu_online(desc, i))
+ continue;
+ for (c = 0; c < ncols; c++) {
+ if (mod->compat && cols[c] == COL_CACHE) {
+ if (!desc->ncaches)
+ continue;
+ if (c > 0)
+ putchar(',');
+ }
+ if (c > 0)
+ putchar(',');
+
+ data = get_cell_data(desc, i, cols[c], mod,
+ buf, sizeof(buf));
+ fputs(data && *data ? data : "", stdout);
+ }
+ putchar('\n');
+ }
+}
+
+/*
+ * [-e] backend
+ */
+static void
+print_readable(struct lscpu_desc *desc, int cols[], int ncols,
+ struct lscpu_modifier *mod)
+{
+ int i;
+ char buf[BUFSIZ], *data;
+ struct tt *tt = tt_new_table(0);
+
+ if (!tt)
+ err(EXIT_FAILURE, _("failed to initialize output table"));
+
+ for (i = 0; i < ncols; i++) {
+ data = get_cell_header(desc, cols[i], mod, buf, sizeof(buf));
+ tt_define_column(tt, xstrdup(data), 0, 0);
+ }
+
+ for (i = 0; i < desc->ncpus; i++) {
+ int c;
+ struct tt_line *line;
+
+ if (!mod->offline && desc->online && !is_cpu_online(desc, i))
+ continue;
+ if (!mod->online && desc->online && is_cpu_online(desc, i))
+ continue;
+
+ line = tt_add_line(tt, NULL);
+
+ for (c = 0; c < ncols; c++) {
+ data = get_cell_data(desc, i, cols[c], mod,
+ buf, sizeof(buf));
+ tt_line_set_data(line, c, data && *data ? xstrdup(data) : "-");
+ }
+ }
+
+ tt_print_table(tt);
+}
+
+/* output formats "<key> <value>"*/
+#define print_s(_key, _val) printf("%-23s%s\n", _key, _val)
+#define print_n(_key, _val) printf("%-23s%d\n", _key, _val)
+
+static void
+print_cpuset(const char *key, cpu_set_t *set, int hex)
+{
+ size_t setsize = CPU_ALLOC_SIZE(maxcpus);
+ size_t setbuflen = 7 * maxcpus;
+ char setbuf[setbuflen], *p;
+
+ if (hex) {
+ p = cpumask_create(setbuf, setbuflen, set, setsize);
+ printf("%-23s0x%s\n", key, p);
+ } else {
+ p = cpulist_create(setbuf, setbuflen, set, setsize);
+ print_s(key, p);
+ }
+
+}
+
+/*
+ * default output
+ */
+static void
+print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod)
+{
+ char buf[512];
+ int i;
+ size_t setsize = CPU_ALLOC_SIZE(maxcpus);
+
+ print_s(_("Architecture:"), desc->arch);
+
+ if (desc->mode) {
+ char mbuf[64], *p = mbuf;
+
+ if (desc->mode & MODE_32BIT) {
+ strcpy(p, "32-bit, ");
+ p += 8;
+ }
+ if (desc->mode & MODE_64BIT) {
+ strcpy(p, "64-bit, ");
+ p += 8;
+ }
+ *(p - 2) = '\0';
+ print_s(_("CPU op-mode(s):"), mbuf);
+ }
+#if !defined(WORDS_BIGENDIAN)
+ print_s(_("Byte Order:"), "Little Endian");
+#else
+ print_s(_("Byte Order:"), "Big Endian");
+#endif
+ print_n(_("CPU(s):"), desc->ncpus);
+
+ if (desc->online)
+ print_cpuset(mod->hex ? _("On-line CPU(s) mask:") :
+ _("On-line CPU(s) list:"),
+ desc->online, mod->hex);
+
+ if (desc->online && CPU_COUNT_S(setsize, desc->online) != desc->ncpus) {
+ cpu_set_t *set;
+
+ /* Linux kernel provides cpuset of off-line CPUs that contains
+ * all configured CPUs (see /sys/devices/system/cpu/offline),
+ * but want to print real (present in system) off-line CPUs only.
+ */
+ set = cpuset_alloc(maxcpus, NULL, NULL);
+ if (!set)
+ err(EXIT_FAILURE, _("failed to callocate cpu set"));
+ CPU_ZERO_S(setsize, set);
+ for (i = 0; i < desc->ncpus; i++) {
+ if (!is_cpu_online(desc, i))
+ CPU_SET_S(i, setsize, set);
+ }
+ print_cpuset(mod->hex ? _("Off-line CPU(s) mask:") :
+ _("Off-line CPU(s) list:"),
+ set, mod->hex);
+ cpuset_free(set);
+ }
+
+ if (desc->nsockets) {
+ int cores_per_socket, sockets_per_book, books;
+
+ cores_per_socket = sockets_per_book = books = 0;
+ /* s390 detects its cpu topology via /proc/sysinfo, if present.
+ * Using simply the cpu topology masks in sysfs will not give
+ * usable results since everything is virtualized. E.g.
+ * virtual core 0 may have only 1 cpu, but virtual core 2 may
+ * five cpus.
+ * If the cpu topology is not exported (e.g. 2nd level guest)
+ * fall back to old calculation scheme.
+ */
+ if (path_exist(_PATH_PROC_SYSINFO)) {
+ FILE *fd = path_fopen("r", 0, _PATH_PROC_SYSINFO);
+ char pbuf[BUFSIZ];
+ int t0, t1, t2;
+
+ while (fd && fgets(pbuf, sizeof(pbuf), fd) != NULL) {
+ if (sscanf(pbuf, "CPU Topology SW:%d%d%d%d%d%d",
+ &t0, &t1, &t2, &books, &sockets_per_book,
+ &cores_per_socket) == 6)
+ break;
+ }
+ if (fd)
+ fclose(fd);
+ }
+ print_n(_("Thread(s) per core:"), desc->nthreads / desc->ncores);
+ print_n(_("Core(s) per socket:"),
+ cores_per_socket ?: desc->ncores / desc->nsockets);
+ if (desc->nbooks) {
+ print_n(_("Socket(s) per book:"),
+ sockets_per_book ?: desc->nsockets / desc->nbooks);
+ print_n(_("Book(s):"), books ?: desc->nbooks);
+ } else {
+ print_n(_("Socket(s):"), sockets_per_book ?: desc->nsockets);
+ }
+ }
+ if (desc->nnodes)
+ print_n(_("NUMA node(s):"), desc->nnodes);
+ if (desc->vendor)
+ print_s(_("Vendor ID:"), desc->vendor);
+ if (desc->family)
+ print_s(_("CPU family:"), desc->family);
+ if (desc->model)
+ print_s(_("Model:"), desc->model);
+ if (desc->stepping)
+ print_s(_("Stepping:"), desc->stepping);
+ if (desc->mhz)
+ print_s(_("CPU MHz:"), desc->mhz);
+ if (desc->bogomips)
+ print_s(_("BogoMIPS:"), desc->bogomips);
+ if (desc->virtflag) {
+ if (!strcmp(desc->virtflag, "svm"))
+ print_s(_("Virtualization:"), "AMD-V");
+ else if (!strcmp(desc->virtflag, "vmx"))
+ print_s(_("Virtualization:"), "VT-x");
+ }
+ if (desc->hypervisor)
+ print_s(_("Hypervisor:"), desc->hypervisor);
+ if (desc->hyper) {
+ print_s(_("Hypervisor vendor:"), hv_vendors[desc->hyper]);
+ print_s(_("Virtualization type:"), _(virt_types[desc->virtype]));
+ }
+ if (desc->dispatching >= 0)
+ print_s(_("Dispatching mode:"), _(disp_modes[desc->dispatching]));
+ if (desc->ncaches) {
+ char cbuf[512];
+
+ for (i = desc->ncaches - 1; i >= 0; i--) {
+ snprintf(cbuf, sizeof(cbuf),
+ _("%s cache:"), desc->caches[i].name);
+ print_s(cbuf, desc->caches[i].size);
+ }
+ }
+
+ for (i = 0; i < desc->nnodes; i++) {
+ snprintf(buf, sizeof(buf), _("NUMA node%d CPU(s):"), i);
+ print_cpuset(buf, desc->nodemaps[i], mod->hex);
+ }
+}
+
+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(_(" -a, --all print online and offline CPUs (default for -e)\n"
+ " -b, --online print online CPUs only (default for -p)\n"
+ " -c, --offline print offline CPUs only\n"
+ " -e, --extended[=<list>] print out an extended readable format\n"
+ " -h, --help print this help\n"
+ " -p, --parse[=<list>] print out a parsable format\n"
+ " -s, --sysroot <dir> use directory DIR as system root\n"
+ " -V, --version print version information and exit\n"
+ " -x, --hex print hexadecimal masks rather than lists of CPUs\n"), out);
+
+ fprintf(out, _("\nAvailable columns:\n"));
+
+ for (i = 0; i < ARRAY_SIZE(coldescs); i++)
+ fprintf(out, " %13s %s\n", coldescs[i].name, _(coldescs[i].help));
+
+ fprintf(out, _("\nFor more details see lscpu(1).\n"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+ struct lscpu_modifier _mod = { .mode = OUTPUT_SUMMARY }, *mod = &_mod;
+ struct lscpu_desc _desc = { .flags = 0 }, *desc = &_desc;
+ int c, i;
+ int columns[ARRAY_SIZE(coldescs)], ncolumns = 0;
+ int cpu_modifier_specified = 0;
+
+ static const struct option longopts[] = {
+ { "all", no_argument, 0, 'a' },
+ { "online", no_argument, 0, 'b' },
+ { "offline", no_argument, 0, 'c' },
+ { "help", no_argument, 0, 'h' },
+ { "extended", optional_argument, 0, 'e' },
+ { "parse", optional_argument, 0, 'p' },
+ { "sysroot", required_argument, 0, 's' },
+ { "hex", no_argument, 0, 'x' },
+ { "version", no_argument, 0, 'V' },
+ { NULL, 0, 0, 0 }
+ };
+
+ static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
+ { 'a','b','c' },
+ { 'e','p' },
+ { 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, "abce::hp::s:xV", longopts, NULL)) != -1) {
+
+ err_exclusive_options(c, longopts, excl, excl_st);
+
+ switch (c) {
+ case 'a':
+ mod->online = mod->offline = 1;
+ cpu_modifier_specified = 1;
+ break;
+ case 'b':
+ mod->online = 1;
+ cpu_modifier_specified = 1;
+ break;
+ case 'c':
+ mod->offline = 1;
+ cpu_modifier_specified = 1;
+ break;
+ case 'h':
+ usage(stdout);
+ case 'p':
+ goto hop_over;
+ case 'e':
+ hop_over:
+ if (optarg) {
+ if (*optarg == '=')
+ optarg++;
+ ncolumns = string_to_idarray(optarg,
+ columns, ARRAY_SIZE(columns),
+ column_name_to_id);
+ if (ncolumns < 0)
+ return EXIT_FAILURE;
+ }
+ mod->mode = c == 'p' ? OUTPUT_PARSABLE : OUTPUT_READABLE;
+ break;
+ case 's':
+ path_setprefix(optarg);
+ mod->system = SYSTEM_SNAPSHOT;
+ break;
+ case 'x':
+ mod->hex = 1;
+ break;
+ case 'V':
+ printf(_("%s from %s\n"), program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ default:
+ usage(stderr);
+ }
+ }
+
+ if (cpu_modifier_specified && mod->mode == OUTPUT_SUMMARY) {
+ fprintf(stderr,
+ _("%s: options --all, --online and --offline may only "
+ "be used with options --extended or --parsable.\n"),
+ program_invocation_short_name);
+ return EXIT_FAILURE;
+ }
+
+ if (argc != optind)
+ usage(stderr);
+
+ /* set default cpu display mode if none was specified */
+ if (!mod->online && !mod->offline) {
+ mod->online = 1;
+ mod->offline = mod->mode == OUTPUT_READABLE ? 1 : 0;
+ }
+
+ read_basicinfo(desc, mod);
+
+ for (i = 0; i < desc->ncpus; i++) {
+ read_topology(desc, i);
+ read_cache(desc, i);
+ read_polarization(desc, i);
+ read_address(desc, i);
+ read_configured(desc, i);
+ }
+
+ if (desc->caches)
+ qsort(desc->caches, desc->ncaches,
+ sizeof(struct cpu_cache), cachecmp);
+
+ read_nodes(desc);
+ read_hypervisor(desc);
+
+ switch(mod->mode) {
+ case OUTPUT_SUMMARY:
+ print_summary(desc, mod);
+ break;
+ case OUTPUT_PARSABLE:
+ if (!ncolumns) {
+ columns[ncolumns++] = COL_CPU;
+ columns[ncolumns++] = COL_CORE;
+ columns[ncolumns++] = COL_SOCKET;
+ columns[ncolumns++] = COL_NODE;
+ columns[ncolumns++] = COL_CACHE;
+ mod->compat = 1;
+ }
+ print_parsable(desc, columns, ncolumns, mod);
+ break;
+ case OUTPUT_READABLE:
+ if (!ncolumns) {
+ /* No list was given. Just print whatever is there. */
+ columns[ncolumns++] = COL_CPU;
+ if (desc->nodemaps)
+ columns[ncolumns++] = COL_NODE;
+ if (desc->bookmaps)
+ columns[ncolumns++] = COL_BOOK;
+ if (desc->socketmaps)
+ columns[ncolumns++] = COL_SOCKET;
+ if (desc->coremaps)
+ columns[ncolumns++] = COL_CORE;
+ if (desc->caches)
+ columns[ncolumns++] = COL_CACHE;
+ if (desc->online)
+ columns[ncolumns++] = COL_ONLINE;
+ if (desc->configured)
+ columns[ncolumns++] = COL_CONFIGURED;
+ if (desc->polarization)
+ columns[ncolumns++] = COL_POLARIZATION;
+ if (desc->addresses)
+ columns[ncolumns++] = COL_ADDRESS;
+ }
+ print_readable(desc, columns, ncolumns, mod);
+ break;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/sys-utils/mount.8 b/sys-utils/mount.8
new file mode 100644
index 0000000..fc4622d
--- /dev/null
+++ b/sys-utils/mount.8
@@ -0,0 +1,2935 @@
+.\" Copyright (c) 1996-2004 Andries Brouwer
+.\" Copyright (C) 2006-2012 Karel Zak <kzak@redhat.com>
+.\"
+.\" This page is somewhat derived from a page that was
+.\" (c) 1980, 1989, 1991 The Regents of the University of California
+.\" and had been heavily modified by Rik Faith and myself.
+.\" (Probably no BSD text remains.)
+.\" Fragments of text were written by Werner Almesberger, Remy Card,
+.\" Stephen Tweedie and Eric Youngdale.
+.\"
+.\" This is free documentation; 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.
+.\"
+.\" The GNU General Public License's references to "object code"
+.\" and "executables" are to be interpreted as the output of any
+.\" document formatting or typesetting system, including
+.\" intermediate and printed output.
+.\"
+.\" This manual 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.
+.\"
+.\"
+.TH MOUNT 8 "January 2012" "util-linux" "System Administration"
+.SH NAME
+mount \- mount a filesystem
+.SH SYNOPSIS
+.B mount
+.RB [ \-lhV ]
+.LP
+.BI "mount \-a
+.RB [ \-fFnrsvw ]
+.RB [ \-t
+.IR vfstype ]
+.RB [ \-O
+.IR optlist ]
+.LP
+.B mount
+.RB [ \-fnrsvw ]
+.RB [ \-o
+.IR option [ \fB,\fPoption ]...]
+.IR device | dir
+.LP
+.B mount
+.RB [ \-fnrsvw ]
+.RB [ \-t
+.IB vfstype ]
+.RB [ \-o
+.IR options ]
+.I device dir
+.SH DESCRIPTION
+All files accessible in a Unix system are arranged in one big
+tree, the file hierarchy, rooted at
+.BR / .
+These files can be spread out over several devices. The
+.B mount
+command serves to attach the filesystem found on some device
+to the big file tree. Conversely, the
+.BR umount (8)
+command will detach it again.
+
+The standard form of the
+.B mount
+command, is
+.RS
+
+.br
+.BI "mount \-t" " type device dir"
+.br
+
+.RE
+This tells the kernel to attach the filesystem found on
+.I device
+(which is of type
+.IR type )
+at the directory
+.IR dir .
+The previous contents (if any) and owner and mode of
+.I dir
+become invisible, and as long as this filesystem remains mounted,
+the pathname
+.I dir
+refers to the root of the filesystem on
+.IR device .
+
+If only directory or device is given, for example:
+.RS
+
+.br
+.BI "mount /dir"
+.br
+
+.RE
+then mount looks for a mountpoint and if not found then for a device in the
+/etc/fstab file. It's possible to use
+.B --target
+or
+.B --source
+options to avoid ambivalent interpretation of the given argument. For example
+.RS
+
+.br
+.BI "mount --target /mountpoint"
+.br
+
+.RE
+
+
+.B The listing and help.
+.RS
+The listing mode is maintained for backward compatibility only.
+
+For more robust and definable output use
+.BR findmnt (8),
+\fBespecially in your scripts\fP. Note that control characters in the
+mountpoint name are replaced with '?'.
+
+.TP
+.BR "mount " [ -l "] [" "-t \fItype\fP" ]
+lists all mounted filesystems (of type
+.IR type ).
+The option \-l adds the labels in this listing.
+See below.
+.RE
+
+.B The device indication.
+.RS
+Most devices are indicated by a file name (of a block special device), like
+.IR /dev/sda1 ,
+but there are other possibilities. For example, in the case of an NFS mount,
+.I device
+may look like
+.IR knuth.cwi.nl:/dir .
+It is possible to indicate a block special device using its
+filesystem
+.B LABEL
+or
+.B UUID
+(see the \-L and \-U options below) and
+partition
+.B PARTUUID
+or
+.B PARTLABEL
+(partition identifiers are supported for GUID Partition Table (GPT) and MAC
+partition tables only).
+
+The recommended setup is to use tags (e.g. LABEL=<label>) rather than
+.B /dev/disk/by-{label,uuid,partuuid,partlabel}
+udev symlinks in the /etc/fstab file. The tags are
+more readable, robust and portable. The
+.BR mount (8)
+command internally uses udev
+symlinks, so use the symlinks in /etc/fstab has no advantage over the tags.
+For more details see
+.BR libblkid (3).
+
+Note that
+.BR mount (8)
+uses UUIDs as strings. The UUIDs from command line or
+.BR fstab (5)
+are not converted to internal binary representation. The string representation
+of the UUID should be based on lower case characters.
+
+The
+.I proc
+filesystem is not associated with a special device, and when
+mounting it, an arbitrary keyword, such as
+.I proc
+can be used instead of a device specification.
+(The customary choice
+.I none
+is less fortunate: the error message `none busy' from
+.B umount
+can be confusing.)
+.RE
+
+.B The /etc/fstab, /etc/mtab and /proc/mounts files.
+.RS
+The file
+.I /etc/fstab
+(see
+.BR fstab (5)),
+may contain lines describing what devices are usually
+mounted where, using which options. The default location of the
+.BR fstab (5)
+file could be overrided by --fstab <path> command line option (see below for
+more details).
+.LP
+The command
+.RS
+.sp
+.B mount \-a
+.RB [ \-t
+.IR type ]
+.RB [ \-O
+.IR optlist ]
+.sp
+.RE
+(usually given in a bootscript) causes all filesystems mentioned in
+.I fstab
+(of the proper type and/or having or not having the proper options)
+to be mounted as indicated, except for those whose line contains the
+.B noauto
+keyword. Adding the
+.B \-F
+option will make mount fork, so that the
+filesystems are mounted simultaneously.
+.LP
+When mounting a filesystem mentioned in
+.IR fstab
+or
+.IR mtab,
+it suffices to give only the device, or only the mount point.
+
+
+The programs
+.B mount
+and
+.B umount
+maintain a list of currently mounted filesystems in the file
+.IR /etc/mtab .
+If no arguments are given to
+.BR mount ,
+this list is printed.
+
+The
+.B mount
+program does not read the
+.I /etc/fstab
+file if
+.I device
+(or LABEL, UUID, PARTUUID or PARTLABEL) and
+.I dir
+are specified. For example:
+.RS
+.sp
+.B "mount /dev/foo /dir"
+.sp
+.RE
+If you want to override mount options from
+.I /etc/fstab
+you have to use:
+.RS
+.sp
+.B "mount device|dir -o <options>"
+.sp
+.RE
+and then the mount options from command line will be appended to
+the list of options from
+.IR /etc/fstab .
+The usual behaviour is that the last option wins if there is more duplicated
+options.
+
+When the
+.I proc
+filesystem is mounted (say at
+.IR /proc ),
+the files
+.I /etc/mtab
+and
+.I /proc/mounts
+have very similar contents. The former has somewhat
+more information, such as the mount options used,
+but is not necessarily up-to-date (cf. the
+.B \-n
+option below). It is possible to replace
+.I /etc/mtab
+by a symbolic link to
+.IR /proc/mounts ,
+and especially when you have very large numbers of mounts
+things will be much faster with that symlink,
+but some information is lost that way, and in particular
+using the "user" option will fail.
+.RE
+
+.B The non-superuser mounts.
+.RS
+Normally, only the superuser can mount filesystems.
+However, when
+.I fstab
+contains the
+.B user
+option on a line, anybody can mount the corresponding system.
+.LP
+Thus, given a line
+.RS
+.sp
+.B "/dev/cdrom /cd iso9660 ro,user,noauto,unhide"
+.sp
+.RE
+any user can mount the iso9660 filesystem found on his CDROM
+using the command
+.RS
+.sp
+.B "mount /dev/cdrom"
+.sp
+.RE
+or
+.RS
+.sp
+.B "mount /cd"
+.sp
+.RE
+For more details, see
+.BR fstab (5).
+Only the user that mounted a filesystem can unmount it again.
+If any user should be able to unmount, then use
+.B users
+instead of
+.B user
+in the
+.I fstab
+line.
+The
+.B owner
+option is similar to the
+.B user
+option, with the restriction that the user must be the owner
+of the special file. This may be useful e.g. for
+.I /dev/fd
+if a login script makes the console user owner of this device.
+The
+.B group
+option is similar, with the restriction that the user must be
+member of the group of the special file.
+.RE
+
+
+.B The bind mounts.
+.RS
+.\" In fact since 2.3.99. At first the syntax was mount -t bind.
+Since Linux 2.4.0 it is possible to remount part of the
+file hierarchy somewhere else. The call is
+.RS
+.br
+.B mount --bind
+.I olddir newdir
+.RE
+or shortoption
+.RS
+.br
+.B mount -B
+.I olddir newdir
+.RE
+or fstab entry is:
+.RS
+.br
+.I /olddir
+.I /newdir
+.B none bind
+.RE
+
+After this call the same contents is accessible in two places.
+One can also remount a single file (on a single file). It's also
+possible to use the bind mount to create a mountpoint from a regular
+directory, for example:
+
+.RS
+.br
+.B mount --bind
+.I foo foo
+.RE
+
+The bind mount call attaches only (part of) a single filesystem, not possible
+submounts. The entire file hierarchy including submounts is attached
+a second place using
+
+.RS
+.br
+.B mount --rbind
+.I olddir newdir
+.RE
+
+or shortoption
+
+.RS
+.br
+.B mount -R
+.I olddir newdir
+.RE
+.\" available since Linux 2.4.11.
+
+Note that the filesystem mount options will remain the same as those
+on the original mount point, and cannot be changed by passing the -o
+option along with --bind/--rbind. The mount options can be
+changed by a separate remount command, for example:
+
+.RS
+.br
+.B mount --bind
+.I olddir newdir
+.br
+.B mount -o remount,ro
+.I newdir
+.RE
+
+Note that behavior of the remount operation depends on the /etc/mtab file. The
+first command stores the 'bind' flag to the /etc/mtab file and the second
+command reads the flag from the file. If you have a system without the
+/etc/mtab file or if you explicitly define source and target for the remount
+command (then mount(8) does not read /etc/mtab), then you have to use bind flag
+(or option) for the remount command too. For example:
+
+.RS
+.br
+.B mount --bind
+.I olddir newdir
+.br
+.B mount -o remount,ro,bind
+.I olddir newdir
+.RE
+
+Note that
+.I remount,ro,bind
+will create a read-only mountpoint (VFS entry), but the original filesystem suberblock
+will be still writable, it means that the
+.I olddir
+will be writable, but the
+.I newdir
+will be read-only.
+.RE
+
+.B The move operation.
+.RS
+Since Linux 2.5.1 it is possible to atomically move a
+.B mounted tree
+to another place. The call is
+.RS
+.br
+.B mount --move
+.I olddir newdir
+.RE
+or shortoption
+.RS
+.br
+.B mount -M
+.I olddir newdir
+.RE
+This will cause the contents which previously appeared under olddir to be
+accessed under newdir. The physical location of the files is not changed.
+Note that the
+.I olddir
+has to be a mountpoint.
+.RE
+
+.B The shared subtrees operations.
+.RS
+Since Linux 2.6.15 it is possible to mark a mount and its submounts as shared,
+private, slave or unbindable. A shared mount provides ability to create mirrors
+of that mount such that mounts and umounts within any of the mirrors propagate
+to the other mirror. A slave mount receives propagation from its master, but
+any not vice-versa. A private mount carries no propagation abilities. A
+unbindable mount is a private mount which cannot be cloned through a bind
+operation. Detailed semantics is documented in Documentation/filesystems/sharedsubtree.txt
+file in the kernel source tree.
+
+Note that Linux kernel does not allow to change more propagation flags by one
+.B mount (2)
+syscall and the flags cannot be mixed with another mount options. It means that
+more --make-* options cannot be used together or with another mount options.
+
+.RS
+.nf
+.BI "mount --make-shared " mountpoint
+.BI "mount --make-slave " mountpoint
+.BI "mount --make-private " mountpoint
+.BI "mount --make-unbindable " mountpoint
+.fi
+.RE
+
+The following commands allows one to recursively change the type of all the
+mounts under a given mountpoint.
+
+.RS
+.nf
+.BI "mount --make-rshared " mountpoint
+.BI "mount --make-rslave " mountpoint
+.BI "mount --make-rprivate " mountpoint
+.BI "mount --make-runbindable " mountpoint
+.fi
+.RE
+.RE
+
+.SH COMMAND LINE OPTIONS
+The full set of mount options used by an invocation of
+.B mount
+is determined by first extracting the
+mount options for the filesystem from the
+.I fstab
+table, then applying any options specified by the
+.B \-o
+argument, and finally applying a
+.BR \-r " or " \-w
+option, when present.
+
+Command line options available for the
+.B mount
+command:
+.IP "\fB\-V, \-\-version\fP"
+Output version.
+.IP "\fB\-h, \-\-help\fP"
+Print a help message.
+.IP "\fB\-v, \-\-verbose\fP"
+Verbose mode.
+.IP "\fB\-a, \-\-all\fP"
+Mount all filesystems (of the given types) mentioned in
+.IR fstab .
+.IP "\fB\-F, \-\-fork\fP"
+(Used in conjunction with
+.BR \-a .)
+Fork off a new incarnation of mount for each device.
+This will do the mounts on different devices or different NFS servers
+in parallel.
+This has the advantage that it is faster; also NFS timeouts go in
+parallel. A disadvantage is that the mounts are done in undefined order.
+Thus, you cannot use this option if you want to mount both
+.I /usr
+and
+.IR /usr/spool .
+.IP "\fB\-f, \-\-fake\fP"
+Causes everything to be done except for the actual system call; if it's not
+obvious, this ``fakes'' mounting the filesystem. This option is useful in
+conjunction with the
+.B \-v
+flag to determine what the
+.B mount
+command is trying to do. It can also be used to add entries for devices
+that were mounted earlier with the -n option. The -f option checks for
+existing record in /etc/mtab and fails when the record already
+exists (with regular non-fake mount, this check is done by kernel).
+.IP "\fB\-i, \-\-internal\-only\fP"
+Don't call the /sbin/mount.<filesystem> helper even if it exists.
+.IP "\fB\-l\fP"
+Add the labels in the mount output. Mount must have
+permission to read the disk device (e.g. be suid root) for this to work.
+One can set such a label for ext2, ext3 or ext4 using the
+.BR e2label (8)
+utility, or for XFS using
+.BR xfs_admin (8),
+or for reiserfs using
+.BR reiserfstune (8).
+.IP "\fB\-n, \-\-no\-mtab\fP"
+Mount without writing in
+.IR /etc/mtab .
+This is necessary for example when
+.I /etc
+is on a read-only filesystem.
+.IP "\fB\-\-no\-canonicalize\fP"
+Don't canonicalize paths. The mount command canonicalizes all paths
+(from command line or fstab) and stores canonicalized paths to the
+.IR /etc/mtab
+file. This option can be used together with the
+.B \-f
+flag for already canonicalized absolut paths.
+.IP "\fB\-p, \-\-pass\-fd \fInum\fP"
+In case of a loop mount with encryption, read the passphrase from
+file descriptor
+.I num
+instead of from the terminal.
+.IP "\fB\-s\fP"
+Tolerate sloppy mount options rather than failing. This will ignore
+mount options not supported by a filesystem type. Not all filesystems
+support this option. This option exists for support of the Linux
+autofs\-based automounter.
+.IP "\fB\-\-source \fIsrc\fP"
+If only one argument for the mount command is given then the argument might be
+interpreted as target (mountpoint) or source (device). This option allows to
+explicitly define that the argument is mount source.
+.IP "\fB\-r, \-\-read\-only\fP"
+Mount the filesystem read-only. A synonym is
+.BR "\-o ro" .
+
+Note that, depending on the filesystem type, state and kernel behavior, the
+system may still write to the device. For example, Ext3 or ext4 will replay its
+journal if the filesystem is dirty. To prevent this kind of write access, you
+may want to mount ext3 or ext4 filesystem with "ro,noload" mount options or
+set the block device to read-only mode, see command
+.BR blockdev (8).
+.IP "\fB\-w, \-\-rw\fP"
+Mount the filesystem read/write. This is the default. A synonym is
+.BR "\-o rw" .
+.IP "\fB\-L \fIlabel\fP"
+Mount the partition that has the specified
+.IR label .
+.IP "\fB\-U \fIuuid\fP"
+Mount the partition that has the specified
+.IR uuid .
+These two options require the file
+.I /proc/partitions
+(present since Linux 2.1.116) to exist.
+.IP "\fB\-T, \-\-fstab \fIpath\fP"
+Specifies alternative fstab file. If the \fIpath\fP is directory then the files
+in the directory are sorted by
+.BR strverscmp (3),
+files that starts with "." or without .fstab extension are ignored. The option
+can be specified more than once. This option is mostly designed for initramfs
+or chroot scripts where additional configuration is specified outside standard
+system configuration.
+
+Note that mount(8) does not pass the option \fB\-\-fstab\fP to
+/sbin/mount.<type> helpers, it means that the alternative fstab files will be
+invisible for the helpers. This is no problem for normal mounts, but user
+(non-root) mounts always require fstab to verify user's rights.
+.IP "\fB\-t, \-\-types \fIvfstype\fP"
+The argument following the
+.B \-t
+is used to indicate the filesystem type. The filesystem types which are
+currently supported include:
+.IR adfs ,
+.IR affs ,
+.IR autofs ,
+.IR cifs ,
+.IR coda ,
+.IR coherent ,
+.IR cramfs ,
+.IR debugfs ,
+.IR devpts ,
+.IR efs ,
+.IR ext ,
+.IR ext2 ,
+.IR ext3 ,
+.IR ext4 ,
+.IR hfs ,
+.IR hfsplus ,
+.IR hpfs ,
+.IR iso9660 ,
+.IR jfs ,
+.IR minix ,
+.IR msdos ,
+.IR ncpfs ,
+.IR nfs ,
+.IR nfs4 ,
+.IR ntfs ,
+.IR proc ,
+.IR qnx4 ,
+.IR ramfs ,
+.IR reiserfs ,
+.IR romfs ,
+.IR squashfs ,
+.IR smbfs ,
+.IR sysv ,
+.IR tmpfs ,
+.IR ubifs ,
+.IR udf ,
+.IR ufs ,
+.IR umsdos ,
+.IR usbfs ,
+.IR vfat ,
+.IR xenix ,
+.IR xfs ,
+.IR xiafs .
+Note that coherent, sysv and xenix are equivalent and that
+.I xenix
+and
+.I coherent
+will be removed at some point in the future \(em use
+.I sysv
+instead. Since kernel version 2.1.21 the types
+.I ext
+and
+.I xiafs
+do not exist anymore. Earlier,
+.I usbfs
+was known as
+.IR usbdevfs .
+Note, the real list of all supported filesystems depends on your
+kernel.
+
+The programs
+.B mount
+and
+.B umount
+support filesystem subtypes. The subtype is defined by '.subtype' suffix. For
+example 'fuse.sshfs'. It's recommended to use subtype notation rather than add
+any prefix to the mount source (for example 'sshfs#example.com' is
+depreacated).
+
+For most types all the
+.B mount
+program has to do is issue a simple
+.IR mount (2)
+system call, and no detailed knowledge of the filesystem type is required.
+For a few types however (like nfs, nfs4, cifs, smbfs, ncpfs) ad hoc code is
+necessary. The nfs, nfs4, cifs, smbfs, and ncpfs filesystems
+have a separate mount program. In order to make it possible to
+treat all types in a uniform way, mount will execute the program
+.BI /sbin/mount. TYPE
+(if that exists) when called with type
+.IR TYPE .
+Since various versions of the
+.B smbmount
+program have different calling conventions,
+.B /sbin/mount.smbfs
+may have to be a shell script that sets up the desired call.
+
+If no
+.B \-t
+option is given, or if the
+.B auto
+type is specified, mount will try to guess the desired type.
+Mount uses the blkid library for guessing the filesystem
+type; if that does not turn up anything that looks familiar,
+mount will try to read the file
+.IR /etc/filesystems ,
+or, if that does not exist,
+.IR /proc/filesystems .
+All of the filesystem types listed there will be tried,
+except for those that are labeled "nodev" (e.g.,
+.IR devpts ,
+.I proc
+and
+.IR nfs ).
+If
+.I /etc/filesystems
+ends in a line with a single * only, mount will read
+.I /proc/filesystems
+afterwards.
+
+The
+.B auto
+type may be useful for user-mounted floppies.
+Creating a file
+.I /etc/filesystems
+can be useful to change the probe order (e.g., to try vfat before msdos
+or ext3 before ext2) or if you use a kernel module autoloader.
+
+More than one type may be specified in a comma separated
+list. The list of filesystem types can be prefixed with
+.B no
+to specify the filesystem types on which no action should be taken.
+(This can be meaningful with the
+.B \-a
+option.) For example, the command:
+.RS
+.RS
+.sp
+.B "mount \-a \-t nomsdos,ext"
+.sp
+.RE
+mounts all filesystems except those of type
+.I msdos
+and
+.IR ext .
+.RE
+.IP "\fB\-\-target \fIdir\fP"
+If only one argument for the mount command is given then the argument might be
+interpreted as target (mountpoint) or source (device). This option allows to
+explicitly define that the argument is mount target.
+.IP "\fB\-O, \-\-test-opts \fIopts\fP"
+Used in conjunction with
+.BR \-a ,
+to limit the set of filesystems to which the
+.B \-a
+is applied. Like
+.B \-t
+in this regard except that it is useless except in the context of
+.BR \-a .
+For example, the command:
+.RS
+.RS
+.sp
+.B "mount \-a \-O no_netdev"
+.sp
+.RE
+mounts all filesystems except those which have the option
+.I _netdev
+specified in the options field in the
+.I /etc/fstab
+file.
+
+It is different from
+.B \-t
+in that each option is matched exactly; a leading
+.B no
+at the beginning of one option does not negate the rest.
+
+The
+.B \-t
+and
+.B \-O
+options are cumulative in effect; that is, the command
+.RS
+.sp
+.B "mount \-a \-t ext2 \-O _netdev"
+.sp
+.RE
+mounts all ext2 filesystems with the _netdev option, not all filesystems
+that are either ext2 or have the _netdev option specified.
+.RE
+.IP "\fB\-o, \-\-options \fIopts\fP"
+Options are specified with a
+.B \-o
+flag followed by a comma separated string of options. For example:
+.RS
+.RS
+.sp
+.B "mount LABEL=mydisk \-o noatime,nouser"
+.sp
+.RE
+
+For more details, see
+.B FILESYSTEM INDEPENDENT MOUNT OPTIONS
+and
+.B FILESYSTEM SPECIFIC MOUNT OPTIONS
+sections.
+.RE
+.IP "\fB\-B, \-\-bind\fP"
+Remount a subtree somewhere else (so that its contents are available
+in both places). See above.
+.IP "\fB\-R, \-\-rbind\fP"
+Remount a subtree and all possible submounts somewhere else (so that its
+contents are available in both places). See above.
+.IP "\fB\-M, \-\-move\fP"
+Move a subtree to some other place. See above.
+
+.SH FILESYSTEM INDEPENDENT MOUNT OPTIONS
+Some of these options are only useful when they appear in the
+.I /etc/fstab
+file.
+
+Some of these options could be enabled or disabled by default
+in the system kernel. To check the current setting see the options
+in /proc/mounts.
+
+The following options apply to any filesystem that is being
+mounted (but not every filesystem actually honors them - e.g., the
+.B sync
+option today has effect only for ext2, ext3, fat, vfat and ufs):
+
+.TP
+.B async
+All I/O to the filesystem should be done asynchronously. (See also the
+.B sync
+option.)
+.TP
+.B atime
+Do not use noatime feature, then the inode access time is controlled by kernel
+defaults. See also the description for
+.B strictatime
+and
+.B relatime
+mount options.
+.TP
+.B noatime
+Do not update inode access times on this filesystem (e.g., for faster
+access on the news spool to speed up news servers).
+.TP
+.B auto
+Can be mounted with the
+.B \-a
+option.
+.TP
+.B noauto
+Can only be mounted explicitly (i.e., the
+.B \-a
+option will not cause the filesystem to be mounted).
+.TP
+\fBcontext=\fP\fIcontext\fP, \fBfscontext=\fP\fIcontext\fP, \fBdefcontext=\fP\fIcontext\fP and \fBrootcontext=\fP\fIcontext\fP
+The
+.BR context=
+option is useful when mounting filesystems that do not support
+extended attributes, such as a floppy or hard disk formatted with VFAT, or
+systems that are not normally running under SELinux, such as an ext3 formatted
+disk from a non-SELinux workstation. You can also use
+.BR context=
+on filesystems you do not trust, such as a floppy. It also helps in compatibility with
+xattr-supporting filesystems on earlier 2.4.<x> kernel versions. Even where
+xattrs are supported, you can save time not having to label every file by
+assigning the entire disk one security context.
+
+A commonly used option for removable media is
+.BR context="system_u:object_r:removable_t" .
+
+Two other options are
+.BR fscontext=
+and
+.BR defcontext= ,
+both of which are mutually exclusive of the context option. This means you
+can use fscontext and defcontext with each other, but neither can be used with
+context.
+
+The
+.BR fscontext=
+option works for all filesystems, regardless of their xattr
+support. The fscontext option sets the overarching filesystem label to a
+specific security context. This filesystem label is separate from the
+individual labels on the files. It represents the entire filesystem for
+certain kinds of permission checks, such as during mount or file creation.
+Individual file labels are still obtained from the xattrs on the files
+themselves. The context option actually sets the aggregate context that
+fscontext provides, in addition to supplying the same label for individual
+files.
+
+You can set the default security context for unlabeled files using
+.BR defcontext=
+option. This overrides the value set for unlabeled files in the policy and requires a
+filesystem that supports xattr labeling.
+
+The
+.BR rootcontext=
+option allows you to explicitly label the root inode of a FS being mounted
+before that FS or inode because visible to userspace. This was found to be
+useful for things like stateless linux.
+
+Note that kernel rejects any remount request that includes the context
+option even if unchanged from the current context.
+
+.B Warning that \fIcontext\fP value might contains comma
+and in this case the value has to be properly quoted otherwise
+.BR mount (8)
+will interpret the comma as separator between mount options. Don't forget that
+shell strips off quotes and
+.BR "double quoting is required" ,
+for example:
+.RS
+.RS
+.sp
+mount -t tmpfs none /mnt \-o 'context="system_u:object_r:tmp_t:s0:c127,c456",noexec'
+.sp
+.RE
+
+For more details, see
+.BR selinux (8)
+.RE
+
+.TP
+.B defaults
+Use default options:
+.BR rw ", " suid ", " dev ", " exec ", " auto ", " nouser ", and " async.
+.TP
+.B dev
+Interpret character or block special devices on the filesystem.
+.TP
+.B nodev
+Do not interpret character or block special devices on the file
+system.
+.TP
+.B diratime
+Update directory inode access times on this filesystem. This is the default.
+.TP
+.B nodiratime
+Do not update directory inode access times on this filesystem.
+.TP
+.B dirsync
+All directory updates within the filesystem should be done synchronously.
+This affects the following system calls: creat, link, unlink, symlink,
+mkdir, rmdir, mknod and rename.
+.TP
+.B exec
+Permit execution of binaries.
+.TP
+.B noexec
+Do not allow direct execution of any binaries on the mounted filesystem.
+(Until recently it was possible to run binaries anyway using a command like
+/lib/ld*.so /mnt/binary. This trick fails since Linux 2.4.25 / 2.6.0.)
+.TP
+.B group
+Allow an ordinary (i.e., non-root) user to mount the filesystem if one
+of his groups matches the group of the device.
+This option implies the options
+.BR nosuid " and " nodev
+(unless overridden by subsequent options, as in the option line
+.BR group,dev,suid ).
+.TP
+.B iversion
+Every time the inode is modified, the i_version field will be incremented.
+.TP
+.B noiversion
+Do not increment the i_version inode field.
+.TP
+.B mand
+Allow mandatory locks on this filesystem. See
+.BR fcntl (2).
+.TP
+.B nomand
+Do not allow mandatory locks on this filesystem.
+.TP
+.B _netdev
+The filesystem resides on a device that requires network access
+(used to prevent the system from attempting to mount these filesystems
+until the network has been enabled on the system).
+.TP
+.B nofail
+Do not report errors for this device if it does not exist.
+.TP
+.B relatime
+Update inode access times relative to modify or change time. Access
+time is only updated if the previous access time was earlier than the
+current modify or change time. (Similar to noatime, but doesn't break
+mutt or other applications that need to know if a file has been read
+since the last time it was modified.)
+
+Since Linux 2.6.30, the kernel defaults to the behavior provided by this
+option (unless
+.B noatime
+was specified), and the
+.B strictatime
+option is required to obtain traditional semantics. In addition, since Linux
+2.6.30, the file's last access time is always updated if it is more than 1
+day old.
+.TP
+.B norelatime
+Do not use
+.B relatime
+feature. See also the
+.B strictatime
+mount option.
+.TP
+.B strictatime
+Allows to explicitly requesting full atime updates. This makes it
+possible for kernel to defaults to
+.B relatime
+or
+.B noatime
+but still allow userspace to override it. For more details about the default
+system mount options see /proc/mounts.
+.TP
+.B nostrictatime
+Use the kernel's default behaviour for inode access time updates.
+.TP
+.B suid
+Allow set-user-identifier or set-group-identifier bits to take
+effect.
+.TP
+.B nosuid
+Do not allow set-user-identifier or set-group-identifier bits to take
+effect. (This seems safe, but is in fact rather unsafe if you have
+suidperl(1) installed.)
+.TP
+.B silent
+Turn on the silent flag.
+.TP
+.B loud
+Turn off the silent flag.
+.TP
+.B owner
+Allow an ordinary (i.e., non-root) user to mount the filesystem if he
+is the owner of the device.
+This option implies the options
+.BR nosuid " and " nodev
+(unless overridden by subsequent options, as in the option line
+.BR owner,dev,suid ).
+.TP
+.B remount
+Attempt to remount an already-mounted filesystem. This is commonly
+used to change the mount flags for a filesystem, especially to make a
+readonly filesystem writable. It does not change device or mount point.
+
+The remount functionality follows the standard way how the mount command works
+with options from fstab. It means the mount command doesn't read fstab (or
+mtab) only when a
+.IR device
+and
+.IR dir
+are fully specified.
+
+.BR "mount -o remount,rw /dev/foo /dir"
+
+After this call all old mount options are replaced and arbitrary stuff from
+fstab is ignored, except the loop= option which is internally generated and
+maintained by the mount command.
+
+.BR "mount -o remount,rw /dir"
+
+After this call mount reads fstab (or mtab) and merges these options with
+options from command line (
+.B -o
+).
+.TP
+.B ro
+Mount the filesystem read-only.
+.TP
+.B rw
+Mount the filesystem read-write.
+.TP
+.B sync
+All I/O to the filesystem should be done synchronously. In case of media with limited number of write cycles
+(e.g. some flash drives) "sync" may cause life-cycle shortening.
+.TP
+.B user
+Allow an ordinary user to mount the filesystem.
+The name of the mounting user is written to mtab so that he can unmount
+the filesystem again.
+This option implies the options
+.BR noexec ", " nosuid ", and " nodev
+(unless overridden by subsequent options, as in the option line
+.BR user,exec,dev,suid ).
+.TP
+.B nouser
+Forbid an ordinary (i.e., non-root) user to mount the filesystem.
+This is the default.
+.TP
+.B users
+Allow every user to mount and unmount the filesystem.
+This option implies the options
+.BR noexec ", " nosuid ", and " nodev
+(unless overridden by subsequent options, as in the option line
+.BR users,exec,dev,suid ).
+
+.SH "FILESYSTEM SPECIFIC MOUNT OPTIONS"
+The following options apply only to certain filesystems.
+We sort them by filesystem. They all follow the
+.B \-o
+flag.
+
+What options are supported depends a bit on the running kernel.
+More info may be found in the kernel source subdirectory
+.IR Documentation/filesystems .
+
+.SH "Mount options for adfs"
+.TP
+\fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP
+Set the owner and group of the files in the filesystem (default: uid=gid=0).
+.TP
+\fBownmask=\fP\fIvalue\fP and \fBothmask=\fP\fIvalue\fP
+Set the permission mask for ADFS 'owner' permissions and 'other' permissions,
+respectively (default: 0700 and 0077, respectively).
+See also
+.IR /usr/src/linux/Documentation/filesystems/adfs.txt .
+.SH "Mount options for affs"
+.TP
+\fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP
+Set the owner and group of the root of the filesystem (default: uid=gid=0,
+but with option
+.B uid
+or
+.B gid
+without specified value, the uid and gid of the current process are taken).
+.TP
+\fBsetuid=\fP\fIvalue\fP and \fBsetgid=\fP\fIvalue\fP
+Set the owner and group of all files.
+.TP
+.BI mode= value
+Set the mode of all files to
+.IR value " & 0777"
+disregarding the original permissions.
+Add search permission to directories that have read permission.
+The value is given in octal.
+.TP
+.B protect
+Do not allow any changes to the protection bits on the filesystem.
+.TP
+.B usemp
+Set uid and gid of the root of the filesystem to the uid and gid
+of the mount point upon the first sync or umount, and then
+clear this option. Strange...
+.TP
+.B verbose
+Print an informational message for each successful mount.
+.TP
+.BI prefix= string
+Prefix used before volume name, when following a link.
+.TP
+.BI volume= string
+Prefix (of length at most 30) used before '/' when following a symbolic link.
+.TP
+.BI reserved= value
+(Default: 2.) Number of unused blocks at the start of the device.
+.TP
+.BI root= value
+Give explicitly the location of the root block.
+.TP
+.BI bs= value
+Give blocksize. Allowed values are 512, 1024, 2048, 4096.
+.TP
+.BR grpquota | noquota | quota | usrquota
+These options are accepted but ignored.
+(However, quota utilities may react to such strings in
+.IR /etc/fstab .)
+
+.SH "Mount options for cifs"
+See the options section of the
+.BR mount.cifs (8)
+man page (cifs-utils package must be installed).
+
+.SH "Mount options for coherent"
+None.
+
+.SH "Mount options for debugfs"
+The debugfs filesystem is a pseudo filesystem, traditionally mounted on
+.IR /sys/kernel/debug .
+.\" or just /debug
+.\" present since 2.6.11
+There are no mount options.
+
+.SH "Mount options for devpts"
+The devpts filesystem is a pseudo filesystem, traditionally mounted on
+.IR /dev/pts .
+In order to acquire a pseudo terminal, a process opens
+.IR /dev/ptmx ;
+the number of the pseudo terminal is then made available to the process
+and the pseudo terminal slave can be accessed as
+.IR /dev/pts/ <number>.
+.TP
+\fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP
+This sets the owner or the group of newly created PTYs to
+the specified values. When nothing is specified, they will
+be set to the UID and GID of the creating process.
+For example, if there is a tty group with GID 5, then
+.B gid=5
+will cause newly created PTYs to belong to the tty group.
+.TP
+.BI mode= value
+Set the mode of newly created PTYs to the specified value.
+The default is 0600.
+A value of
+.B mode=620
+and
+.B gid=5
+makes "mesg y" the default on newly created PTYs.
+.TP
+\fBnewinstance
+Create a private instance of devpts filesystem, such that
+indices of ptys allocated in this new instance are
+independent of indices created in other instances of devpts.
+
+All mounts of devpts without this
+.B newinstance
+option share the same set of pty indices (i.e legacy mode).
+Each mount of devpts with the
+.B newinstance
+option has a private set of pty indices.
+
+This option is mainly used to support containers in the
+linux kernel. It is implemented in linux kernel versions
+starting with 2.6.29. Further, this mount option is valid
+only if CONFIG_DEVPTS_MULTIPLE_INSTANCES is enabled in the
+kernel configuration.
+
+To use this option effectively,
+.IR /dev/ptmx
+must be a symbolic link to
+.IR pts/ptmx.
+See
+.IR Documentation/filesystems/devpts.txt
+in the linux kernel source tree for details.
+.TP
+.BI ptmxmode= value
+
+Set the mode for the new
+.IR ptmx
+device node in the devpts filesystem.
+
+With the support for multiple instances of devpts (see
+.B newinstance
+option above), each instance has a private
+.IR ptmx
+node in the root of the devpts filesystem (typically
+.IR /dev/pts/ptmx).
+
+For compatibility with older versions of the kernel, the
+default mode of the new
+.IR ptmx
+node is 0000.
+.BI ptmxmode= value
+specifies a more useful mode for the
+.IR ptmx
+node and is highly recommended when the
+.B newinstance
+option is specified.
+
+This option is only implemented in linux kernel versions
+starting with 2.6.29. Further this option is valid only if
+CONFIG_DEVPTS_MULTIPLE_INSTANCES is enabled in the kernel
+configuration.
+
+.SH "Mount options for ext"
+None.
+Note that the `ext' filesystem is obsolete. Don't use it.
+Since Linux version 2.1.21 extfs is no longer part of the kernel source.
+
+.SH "Mount options for ext2"
+The `ext2' filesystem is the standard Linux filesystem.
+.\" Due to a kernel bug, it may be mounted with random mount options
+.\" (fixed in Linux 2.0.4).
+Since Linux 2.5.46, for most mount options the default
+is determined by the filesystem superblock. Set them with
+.BR tune2fs (8).
+.TP
+.BR acl | noacl
+Support POSIX Access Control Lists (or not).
+.\" requires CONFIG_EXT2_FS_POSIX_ACL
+.TP
+.BR bsddf | minixdf
+Set the behaviour for the
+.I statfs
+system call. The
+.B minixdf
+behaviour is to return in the
+.I f_blocks
+field the total number of blocks of the filesystem, while the
+.B bsddf
+behaviour (which is the default) is to subtract the overhead blocks
+used by the ext2 filesystem and not available for file storage. Thus
+.nf
+
+% mount /k -o minixdf; df /k; umount /k
+Filesystem 1024-blocks Used Available Capacity Mounted on
+/dev/sda6 2630655 86954 2412169 3% /k
+% mount /k -o bsddf; df /k; umount /k
+Filesystem 1024-blocks Used Available Capacity Mounted on
+/dev/sda6 2543714 13 2412169 0% /k
+
+.fi
+(Note that this example shows that one can add command line options
+to the options given in
+.IR /etc/fstab .)
+
+.TP
+.BR check=none " or " nocheck
+No checking is done at mount time. This is the default. This is fast.
+It is wise to invoke
+.BR e2fsck (8)
+every now and then, e.g. at boot time. The non-default behavior is unssuported
+(check=normal and check=strict options have been removed). Note that these mount options
+don't have to be supported if ext4 kernel driver is used for ext2 and ext3 filesystems.
+.TP
+.B debug
+Print debugging info upon each (re)mount.
+.TP
+.BR errors= { continue | remount-ro | panic }
+Define the behaviour when an error is encountered.
+(Either ignore errors and just mark the filesystem erroneous and continue,
+or remount the filesystem read-only, or panic and halt the system.)
+The default is set in the filesystem superblock, and can be
+changed using
+.BR tune2fs (8).
+.TP
+.BR grpid | bsdgroups " and " nogrpid | sysvgroups
+These options define what group id a newly created file gets.
+When
+.BR grpid
+is set, it takes the group id of the directory in which it is created;
+otherwise (the default) it takes the fsgid of the current process, unless
+the directory has the setgid bit set, in which case it takes the gid
+from the parent directory, and also gets the setgid bit set
+if it is a directory itself.
+.TP
+.BR grpquota | noquota | quota | usrquota
+The usrquota (same as quota) mount option enables user quota support on the
+filesystem. grpquota enables group quotas support. You need the quota utilities
+to actually enable and manage the quota system.
+.TP
+.BR nouid32
+Disables 32-bit UIDs and GIDs. This is for interoperability with older
+kernels which only store and expect 16-bit values.
+.TP
+.BR oldalloc " or " orlov
+Use old allocator or Orlov allocator for new inodes. Orlov is default.
+.TP
+\fBresgid=\fP\fIn\fP and \fBresuid=\fP\fIn\fP
+The ext2 filesystem reserves a certain percentage of the available
+space (by default 5%, see
+.BR mke2fs (8)
+and
+.BR tune2fs (8)).
+These options determine who can use the reserved blocks.
+(Roughly: whoever has the specified uid, or belongs to the specified group.)
+.TP
+.BI sb= n
+Instead of block 1, use block
+.I n
+as superblock. This could be useful when the filesystem has been damaged.
+(Earlier, copies of the superblock would be made every 8192 blocks: in
+block 1, 8193, 16385, ... (and one got thousands of copies on
+a big filesystem). Since version 1.08,
+.B mke2fs
+has a \-s (sparse superblock) option to reduce the number of backup
+superblocks, and since version 1.15 this is the default. Note
+that this may mean that ext2 filesystems created by a recent
+.B mke2fs
+cannot be mounted r/w under Linux 2.0.*.)
+The block number here uses 1k units. Thus, if you want to use logical
+block 32768 on a filesystem with 4k blocks, use "sb=131072".
+.TP
+.BR user_xattr | nouser_xattr
+Support "user." extended attributes (or not).
+.\" requires CONFIG_EXT2_FS_XATTR
+
+
+.SH "Mount options for ext3"
+The ext3 filesystem is a version of the ext2 filesystem which has been
+enhanced with journalling. It supports the same options as ext2 as
+well as the following additions:
+.\" .TP
+.\" .BR abort
+.\" Mount the filesystem in abort mode, as if a fatal error has occurred.
+.TP
+.BR journal=update
+Update the ext3 filesystem's journal to the current format.
+.TP
+.BR journal=inum
+When a journal already exists, this option is ignored. Otherwise, it
+specifies the number of the inode which will represent the ext3 filesystem's
+journal file; ext3 will create a new journal, overwriting the old contents
+of the file whose inode number is
+.IR inum .
+.TP
+.BR journal_dev=devnum
+When the external journal device's major/minor numbers
+have changed, this option allows the user to specify
+the new journal location. The journal device is
+identified through its new major/minor numbers encoded
+in devnum.
+.TP
+.BR norecovery / noload
+Don't load the journal on mounting. Note that
+if the filesystem was not unmounted cleanly,
+skipping the journal replay will lead to the
+filesystem containing inconsistencies that can
+lead to any number of problems.
+.TP
+.BR data= { journal | ordered | writeback }
+Specifies the journalling mode for file data. Metadata is always journaled.
+To use modes other than
+.B ordered
+on the root filesystem, pass the mode to the kernel as boot parameter, e.g.
+.IR rootflags=data=journal .
+.RS
+.TP
+.B journal
+All data is committed into the journal prior to being written into the
+main filesystem.
+.TP
+.B ordered
+This is the default mode. All data is forced directly out to the main file
+system prior to its metadata being committed to the journal.
+.TP
+.B writeback
+Data ordering is not preserved - data may be written into the main
+filesystem after its metadata has been committed to the journal.
+This is rumoured to be the highest-throughput option. It guarantees
+internal filesystem integrity, however it can allow old data to appear
+in files after a crash and journal recovery.
+.RE
+.TP
+.BR barrier=0 " / " barrier=1 "
+This enables/disables barriers. barrier=0 disables it, barrier=1 enables it.
+Write barriers enforce proper on-disk ordering of journal commits, making
+volatile disk write caches safe to use, at some performance penalty. The ext3
+filesystem does not enable write barriers by default. Be sure to enable
+barriers unless your disks are battery-backed one way or another. Otherwise
+you risk filesystem corruption in case of power failure.
+.TP
+.BI commit= nrsec
+Sync all data and metadata every
+.I nrsec
+seconds. The default value is 5 seconds. Zero means default.
+.TP
+.BR user_xattr
+Enable Extended User Attributes. See the
+.BR attr (5)
+manual page.
+.TP
+.BR acl
+Enable POSIX Access Control Lists. See the
+.BR acl (5)
+manual page.
+.TP
+.BR usrjquot=aquota.user | grpjquota=aquota.group | jqfmt=vfsv0
+Apart from the old quota system (as in ext2, jqfmt=vfsold aka version 1 quota)
+ext3 also supports journaled quotas (version 2 quota). jqfmt=vfsv0
+enables journaled quotas. For journaled quotas the mount options
+usrjquot=aquota.user and grpjquota=aquota.group are required to tell the
+quota system which quota database files to use. Journaled quotas have the
+advantage that even after a crash no quota check is required.
+
+.SH "Mount options for ext4"
+The ext4 filesystem is an advanced level of the ext3 filesystem which
+incorporates scalability and reliability enhancements for supporting large
+filesystem.
+
+The options
+.B journal_dev, noload, data, commit, orlov, oldalloc, [no]user_xattr
+.B [no]acl, bsddf, minixdf, debug, errors, data_err, grpid, bsdgroups, nogrpid
+.B sysvgroups, resgid, resuid, sb, quota, noquota, grpquota, usrquota
+.B usrjquot, grpjquota and jqfmt
+are backwardly compatible with ext3 or ext2.
+.TP
+.BR journal_checksum
+Enable checksumming of the journal transactions. This will allow the recovery
+code in e2fsck and the kernel to detect corruption in the kernel. It is a
+compatible change and will be ignored by older kernels.
+.TP
+.BR journal_async_commit
+Commit block can be written to disk without waiting for descriptor blocks. If
+enabled older kernels cannot mount the device.
+This will enable 'journal_checksum' internally.
+.TP
+.BR journal=update
+Update the ext4 filesystem's journal to the current format.
+.TP
+.BR barrier=0 " / " barrier=1 " / " barrier " / " nobarrier
+This enables/disables the use of write barriers in the jbd code. barrier=0
+disables, barrier=1 enables. This also requires an IO stack which can support
+barriers, and if jbd gets an error on a barrier write, it will disable again
+with a warning. Write barriers enforce proper on-disk ordering of journal
+commits, making volatile disk write caches safe to use, at some performance
+penalty. If your disks are battery-backed in one way or another, disabling
+barriers may safely improve performance. The mount options "barrier" and
+"nobarrier" can also be used to enable or disable barriers, for consistency
+with other ext4 mount options.
+
+The ext4 filesystem enables write barriers by default.
+.TP
+.BI inode_readahead_blks= n
+This tuning parameter controls the maximum number of inode table blocks that
+ext4's inode table readahead algorithm will pre-read into the buffer cache.
+The value must be a power of 2. The default value is 32 blocks.
+.TP
+.BI stripe= n
+Number of filesystem blocks that mballoc will try to use for allocation size
+and alignment. For RAID5/6 systems this should be the number of data disks *
+RAID chunk size in filesystem blocks.
+.TP
+.BR delalloc
+Deferring block allocation until write-out time.
+.TP
+.BR nodelalloc
+Disable delayed allocation. Blocks are allocated when data is copied from user
+to page cache.
+.TP
+.BI max_batch_time= usec
+Maximum amount of time ext4 should wait for additional filesystem operations to
+be batch together with a synchronous write operation. Since a synchronous
+write operation is going to force a commit and then a wait for the I/O
+complete, it doesn't cost much, and can be a huge throughput win, we wait for a
+small amount of time to see if any other transactions can piggyback on the
+synchronous write. The algorithm used is designed to automatically tune for
+the speed of the disk, by measuring the amount of time (on average) that it
+takes to finish committing a transaction. Call this time the "commit time".
+If the time that the transaction has been running is less than the commit time,
+ext4 will try sleeping for the commit time to see if other operations will join
+the transaction. The commit time is capped by the max_batch_time, which
+defaults to 15000us (15ms). This optimization can be turned off entirely by
+setting max_batch_time to 0.
+.TP
+.BI min_batch_time= usec
+This parameter sets the commit time (as described above) to be at least
+min_batch_time. It defaults to zero microseconds. Increasing this parameter
+may improve the throughput of multi-threaded, synchronous workloads on very
+fast disks, at the cost of increasing latency.
+.TP
+.BI journal_ioprio= prio
+The I/O priority (from 0 to 7, where 0 is the highest priorty) which should be
+used for I/O operations submitted by kjournald2 during a commit operation.
+This defaults to 3, which is a slightly higher priority than the default I/O
+priority.
+.TP
+.BR abort
+Simulate the effects of calling ext4_abort() for
+debugging purposes. This is normally used while
+remounting a filesystem which is already mounted.
+.TP
+.BR auto_da_alloc | noauto_da_alloc
+Many broken applications don't use fsync() when
+replacing existing files via patterns such as
+
+fd = open("foo.new")/write(fd,..)/close(fd)/ rename("foo.new", "foo")
+
+or worse yet
+
+fd = open("foo", O_TRUNC)/write(fd,..)/close(fd).
+
+If auto_da_alloc is enabled, ext4 will detect the replace-via-rename and
+replace-via-truncate patterns and force that any delayed allocation blocks are
+allocated such that at the next journal commit, in the default data=ordered
+mode, the data blocks of the new file are forced to disk before the rename()
+operation is committed. This provides roughly the same level of guarantees as
+ext3, and avoids the "zero-length" problem that can happen when a system
+crashes before the delayed allocation blocks are forced to disk.
+.TP
+.BR discard / nodiscard
+Controls whether ext4 should issue discard/TRIM commands to the underlying
+block device when blocks are freed. This is useful for SSD devices and
+sparse/thinly-provisioned LUNs, but it is off by default until sufficient
+testing has been done.
+.TP
+.BR nouid32
+Disables 32-bit UIDs and GIDs. This is for
+interoperability with older kernels which only
+store and expect 16-bit values.
+.TP
+.BR resize
+Allows to resize filesystem to the end of the last
+existing block group, further resize has to be done
+with resize2fs either online, or offline. It can be
+used only with conjunction with remount.
+.TP
+.BR block_validity / noblock_validity
+This options allows to enables/disables the in-kernel facility for tracking
+filesystem metadata blocks within internal data structures. This allows multi-
+block allocator and other routines to quickly locate extents which might
+overlap with filesystem metadata blocks. This option is intended for debugging
+purposes and since it negatively affects the performance, it is off by default.
+.TP
+.BR dioread_lock / dioread_nolock
+Controls whether or not ext4 should use the DIO read locking. If the
+dioread_nolock option is specified ext4 will allocate uninitialized extent
+before buffer write and convert the extent to initialized after IO completes.
+This approach allows ext4 code to avoid using inode mutex, which improves
+scalability on high speed storages. However this does not work with data
+journaling and dioread_nolock option will be ignored with kernel warning.
+Note that dioread_nolock code path is only used for extent-based files.
+Because of the restrictions this options comprises it is off by default
+(e.g. dioread_lock).
+.TP
+.BR i_version
+Enable 64-bit inode version support. This option is off by default.
+
+.SH "Mount options for fat"
+(Note:
+.I fat
+is not a separate filesystem, but a common part of the
+.IR msdos ,
+.I umsdos
+and
+.I vfat
+filesystems.)
+.TP
+.BR blocksize= { 512 | 1024 | 2048 }
+Set blocksize (default 512). This option is obsolete.
+.TP
+\fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP
+Set the owner and group of all files.
+(Default: the uid and gid of the current process.)
+.TP
+.BI umask= value
+Set the umask (the bitmask of the permissions that are
+.B not
+present). The default is the umask of the current process.
+The value is given in octal.
+.TP
+.BI dmask= value
+Set the umask applied to directories only.
+The default is the umask of the current process.
+The value is given in octal.
+.\" Present since Linux 2.5.43.
+.TP
+.BI fmask= value
+Set the umask applied to regular files only.
+The default is the umask of the current process.
+The value is given in octal.
+.\" Present since Linux 2.5.43.
+.TP
+.BI allow_utime= value
+This option controls the permission check of mtime/atime.
+.RS
+.TP
+.B 20
+If current process is in group of file's group ID, you can change timestamp.
+.TP
+.B 2
+Other users can change timestamp.
+.PP
+The default is set from `dmask' option. (If the directory is writable,
+.B utime(2)
+is also allowed. I.e. ~dmask & 022)
+
+Normally
+.B utime(2)
+checks current process is owner of the file, or it has
+CAP_FOWNER capability. But FAT filesystem doesn't have uid/gid on disk, so
+normal check is too unflexible. With this option you can relax it.
+.RE
+.TP
+.BI check= value
+Three different levels of pickyness can be chosen:
+.RS
+.TP
+.BR r [ elaxed ]
+Upper and lower case are accepted and equivalent, long name parts are
+truncated (e.g.
+.I verylongname.foobar
+becomes
+.IR verylong.foo ),
+leading and embedded spaces are accepted in each name part (name and extension).
+.TP
+.BR n [ ormal ]
+Like "relaxed", but many special characters (*, ?, <, spaces, etc.) are
+rejected. This is the default.
+.TP
+.BR s [ trict ]
+Like "normal", but names may not contain long parts and special characters
+that are sometimes used on Linux, but are not accepted by MS-DOS are
+rejected. (+, =, spaces, etc.)
+.RE
+.TP
+.BI codepage= value
+Sets the codepage for converting to shortname characters on FAT
+and VFAT filesystems. By default, codepage 437 is used.
+.TP
+.BR conv= {b [ inary ]| t [ ext ]| a [ uto ]}
+The
+.I fat
+filesystem can perform CRLF<-->NL (MS-DOS text format to UNIX text
+format) conversion in the kernel. The following conversion modes are
+available:
+.RS
+.TP
+.B binary
+no translation is performed. This is the default.
+.TP
+.B text
+CRLF<-->NL translation is performed on all files.
+.TP
+.B auto
+CRLF<-->NL translation is performed on all files that don't have a
+"well-known binary" extension. The list of known extensions can be found at
+the beginning of
+.I fs/fat/misc.c
+(as of 2.0, the list is: exe, com, bin, app, sys, drv, ovl, ovr, obj,
+lib, dll, pif, arc, zip, lha, lzh, zoo, tar, z, arj, tz, taz, tzp, tpz,
+gz, tgz, deb, gif, bmp, tif, gl, jpg, pcx, tfm, vf, gf, pk, pxl, dvi).
+.PP
+Programs that do computed lseeks won't like in-kernel text conversion.
+Several people have had their data ruined by this translation. Beware!
+
+For filesystems mounted in binary mode, a conversion tool
+(fromdos/todos) is available. This option is obsolete.
+.RE
+.TP
+.BI cvf_format= module
+Forces the driver to use the CVF (Compressed Volume File) module
+.RI cvf_ module
+instead of auto-detection. If the kernel supports kmod, the
+cvf_format=xxx option also controls on-demand CVF module loading.
+This option is obsolete.
+.TP
+.BI cvf_option= option
+Option passed to the CVF module. This option is obsolete.
+.TP
+.B debug
+Turn on the
+.I debug
+flag. A version string and a list of filesystem parameters will be
+printed (these data are also printed if the parameters appear to be
+inconsistent).
+.TP
+.B discard
+If set, causes discard/TRIM commands to be issued to the block device
+when blocks are freed. This is useful for SSD devices and
+sparse/thinly-provisoned LUNs.
+.TP
+.BR fat= {12 | 16 | 32 }
+Specify a 12, 16 or 32 bit fat. This overrides
+the automatic FAT type detection routine. Use with caution!
+.TP
+.BI iocharset= value
+Character set to use for converting between 8 bit characters
+and 16 bit Unicode characters. The default is iso8859-1.
+Long filenames are stored on disk in Unicode format.
+.TP
+.B nfs
+If set, enables in-memory indexing of directory inodes to reduce the
+frequency of ESTALE errors in NFS client operations. Useful only when
+the filesystem is exported via NFS.
+.TP
+.BI tz=UTC
+This option disables the conversion of timestamps
+between local time (as used by Windows on FAT) and UTC
+(which Linux uses internally). This is particularly
+useful when mounting devices (like digital cameras)
+that are set to UTC in order to avoid the pitfalls of
+local time.
+.TP
+.B quiet
+Turn on the
+.I quiet
+flag. Attempts to chown or chmod files do not return errors,
+although they fail. Use with caution!
+.TP
+.B showexec
+If set, the execute permission bits of the file will be allowed only if
+the extension part of the name is .EXE, .COM, or .BAT. Not set by default.
+.TP
+.B sys_immutable
+If set, ATTR_SYS attribute on FAT is handled as IMMUTABLE flag on Linux.
+Not set by default.
+.TP
+.B flush
+If set, the filesystem will try to flush to disk more early than normal.
+Not set by default.
+.TP
+.B usefree
+Use the "free clusters" value stored on FSINFO. It'll
+be used to determine number of free clusters without
+scanning disk. But it's not used by default, because
+recent Windows don't update it correctly in some
+case. If you are sure the "free clusters" on FSINFO is
+correct, by this option you can avoid scanning disk.
+.TP
+.BR dots ", " nodots ", " dotsOK= [ yes | no ]
+Various misguided attempts to force Unix or DOS conventions
+onto a FAT filesystem.
+
+.SH "Mount options for hfs"
+.TP
+.BI creator= cccc ", type=" cccc
+Set the creator/type values as shown by the MacOS finder
+used for creating new files. Default values: '????'.
+.TP
+.BI uid= n ", gid=" n
+Set the owner and group of all files.
+(Default: the uid and gid of the current process.)
+.TP
+.BI dir_umask= n ", file_umask=" n ", umask=" n
+Set the umask used for all directories, all regular files, or all
+files and directories. Defaults to the umask of the current process.
+.TP
+.BI session= n
+Select the CDROM session to mount.
+Defaults to leaving that decision to the CDROM driver.
+This option will fail with anything but a CDROM as underlying device.
+.TP
+.BI part= n
+Select partition number n from the device.
+Only makes sense for CDROMs.
+Defaults to not parsing the partition table at all.
+.TP
+.B quiet
+Don't complain about invalid mount options.
+
+.SH "Mount options for hpfs"
+.TP
+\fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP
+Set the owner and group of all files. (Default: the uid and gid
+of the current process.)
+.TP
+.BI umask= value
+Set the umask (the bitmask of the permissions that are
+.B not
+present). The default is the umask of the current process.
+The value is given in octal.
+.TP
+.BR case= { lower | asis }
+Convert all files names to lower case, or leave them.
+(Default:
+.BR case=lower .)
+.TP
+.BR conv= { binary | text | auto }
+For
+.BR conv=text ,
+delete some random CRs (in particular, all followed by NL)
+when reading a file.
+For
+.BR conv=auto ,
+choose more or less at random between
+.BR conv=binary " and " conv=text .
+For
+.BR conv=binary ,
+just read what is in the file. This is the default.
+.TP
+.B nocheck
+Do not abort mounting when certain consistency checks fail.
+
+.SH "Mount options for iso9660"
+ISO 9660 is a standard describing a filesystem structure to be used
+on CD-ROMs. (This filesystem type is also seen on some DVDs. See also the
+.I udf
+filesystem.)
+
+Normal
+.I iso9660
+filenames appear in a 8.3 format (i.e., DOS-like restrictions on filename
+length), and in addition all characters are in upper case. Also there is
+no field for file ownership, protection, number of links, provision for
+block/character devices, etc.
+
+Rock Ridge is an extension to iso9660 that provides all of these UNIX-like
+features. Basically there are extensions to each directory record that
+supply all of the additional information, and when Rock Ridge is in use,
+the filesystem is indistinguishable from a normal UNIX filesystem (except
+that it is read-only, of course).
+.TP
+.B norock
+Disable the use of Rock Ridge extensions, even if available. Cf.\&
+.BR map .
+.TP
+.B nojoliet
+Disable the use of Microsoft Joliet extensions, even if available. Cf.\&
+.BR map .
+.TP
+.BR check= { r [ elaxed ]| s [ trict ]}
+With
+.BR check=relaxed ,
+a filename is first converted to lower case before doing the lookup.
+This is probably only meaningful together with
+.B norock
+and
+.BR map=normal .
+(Default:
+.BR check=strict .)
+.TP
+\fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP
+Give all files in the filesystem the indicated user or group id,
+possibly overriding the information found in the Rock Ridge extensions.
+(Default:
+.BR uid=0,gid=0 .)
+.TP
+.BR map= { n [ ormal ]| o [ ff ]| a [ corn ]}
+For non-Rock Ridge volumes, normal name translation maps upper
+to lower case ASCII, drops a trailing `;1', and converts `;' to `.'.
+With
+.B map=off
+no name translation is done. See
+.BR norock .
+(Default:
+.BR map=normal .)
+.B map=acorn
+is like
+.BR map=normal
+but also apply Acorn extensions if present.
+.TP
+.BI mode= value
+For non-Rock Ridge volumes, give all files the indicated mode.
+(Default: read permission for everybody.)
+Since Linux 2.1.37 one no longer needs to specify the mode in
+decimal. (Octal is indicated by a leading 0.)
+.TP
+.B unhide
+Also show hidden and associated files.
+(If the ordinary files and the associated or hidden files have
+the same filenames, this may make the ordinary files inaccessible.)
+.TP
+.BR block= { 512 | 1024 | 2048 }
+Set the block size to the indicated value.
+(Default:
+.BR block=1024 .)
+.TP
+.BR conv= { a [ uto ]| b [ inary ]| m [ text ]| t [ ext ]}
+(Default:
+.BR conv=binary .)
+Since Linux 1.3.54 this option has no effect anymore.
+(And non-binary settings used to be very dangerous,
+possibly leading to silent data corruption.)
+.TP
+.B cruft
+If the high byte of the file length contains other garbage,
+set this mount option to ignore the high order bits of the file length.
+This implies that a file cannot be larger than 16MB.
+.TP
+.BI session= x
+Select number of session on multisession CD. (Since 2.3.4.)
+.TP
+.BI sbsector= xxx
+Session begins from sector xxx. (Since 2.3.4.)
+.LP
+The following options are the same as for vfat and specifying them only makes
+sense when using discs encoded using Microsoft's Joliet extensions.
+.TP
+.BI iocharset= value
+Character set to use for converting 16 bit Unicode characters on CD
+to 8 bit characters. The default is iso8859-1.
+.TP
+.B utf8
+Convert 16 bit Unicode characters on CD to UTF-8.
+
+.SH "Mount options for jfs"
+.TP
+.BI iocharset= name
+Character set to use for converting from Unicode to ASCII. The default is
+to do no conversion. Use
+.B iocharset=utf8
+for UTF8 translations. This requires CONFIG_NLS_UTF8 to be set in
+the kernel
+.I ".config"
+file.
+.TP
+.BI resize= value
+Resize the volume to
+.I value
+blocks. JFS only supports growing a volume, not shrinking it. This option
+is only valid during a remount, when the volume is mounted read-write. The
+.B resize
+keyword with no value will grow the volume to the full size of the partition.
+.TP
+.B nointegrity
+Do not write to the journal. The primary use of this option is to allow
+for higher performance when restoring a volume from backup media. The
+integrity of the volume is not guaranteed if the system abnormally abends.
+.TP
+.B integrity
+Default. Commit metadata changes to the journal. Use this option to remount
+a volume where the
+.B nointegrity
+option was previously specified in order to restore normal behavior.
+.TP
+.BR errors= { continue | remount-ro | panic }
+Define the behaviour when an error is encountered.
+(Either ignore errors and just mark the filesystem erroneous and continue,
+or remount the filesystem read-only, or panic and halt the system.)
+.TP
+.BR noquota | quota | usrquota | grpquota
+These options are accepted but ignored.
+
+.SH "Mount options for minix"
+None.
+
+.SH "Mount options for msdos"
+See mount options for fat.
+If the
+.I msdos
+filesystem detects an inconsistency, it reports an error and sets the file
+system read-only. The filesystem can be made writable again by remounting
+it.
+
+.SH "Mount options for ncpfs"
+Just like
+.IR nfs ", the " ncpfs
+implementation expects a binary argument (a
+.IR "struct ncp_mount_data" )
+to the mount system call. This argument is constructed by
+.BR ncpmount (8)
+and the current version of
+.B mount
+(2.12) does not know anything about ncpfs.
+
+.SH "Mount options for nfs and nfs4"
+See the options section of the
+.BR nfs (5)
+man page (nfs-utils package must be installed).
+
+The
+.IR nfs " and " nfs4
+implementation expects a binary argument (a
+.IR "struct nfs_mount_data" )
+to the mount system call. This argument is constructed by
+.BR mount.nfs (8)
+and the current version of
+.B mount
+(2.13) does not know anything about nfs and nfs4.
+
+.SH "Mount options for ntfs"
+.TP
+.BI iocharset= name
+Character set to use when returning file names.
+Unlike VFAT, NTFS suppresses names that contain
+nonconvertible characters. Deprecated.
+.\" since 2.5.11
+.TP
+.BI nls= name
+New name for the option earlier called
+.IR iocharset .
+.\" since 2.5.11
+.TP
+.BR utf8
+Use UTF-8 for converting file names.
+.TP
+.BR uni_xlate= { 0 | 1 | 2 }
+For 0 (or `no' or `false'), do not use escape sequences
+for unknown Unicode characters.
+For 1 (or `yes' or `true') or 2, use vfat-style 4-byte escape sequences
+starting with ":". Here 2 give a little-endian encoding
+and 1 a byteswapped bigendian encoding.
+.TP
+.B posix=[0|1]
+If enabled (posix=1), the filesystem distinguishes between
+upper and lower case. The 8.3 alias names are presented as
+hard links instead of being suppressed. This option is obsolete.
+.TP
+\fBuid=\fP\fIvalue\fP, \fBgid=\fP\fIvalue\fP and \fBumask=\fP\fIvalue\fP
+Set the file permission on the filesystem.
+The umask value is given in octal.
+By default, the files are owned by root and not readable by somebody else.
+
+.SH "Mount options for proc"
+.TP
+\fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP
+These options are recognized, but have no effect as far as I can see.
+
+.SH "Mount options for ramfs"
+Ramfs is a memory based filesystem. Mount it and you have it. Unmount it
+and it is gone. Present since Linux 2.3.99pre4.
+There are no mount options.
+
+.SH "Mount options for reiserfs"
+Reiserfs is a journaling filesystem.
+.TP
+.BR conv
+Instructs version 3.6 reiserfs software to mount a version 3.5 filesystem,
+using the 3.6 format for newly created objects. This filesystem will no
+longer be compatible with reiserfs 3.5 tools.
+.TP
+.BR hash= { rupasov | tea | r5 | detect }
+Choose which hash function reiserfs will use to find files within directories.
+.RS
+.TP
+.B rupasov
+A hash invented by Yury Yu. Rupasov. It is fast and preserves locality,
+mapping lexicographically close file names to close hash values.
+This option should not be used, as it causes a high probability of hash
+collisions.
+.TP
+.B tea
+A Davis-Meyer function implemented by Jeremy Fitzhardinge.
+It uses hash permuting bits in the name. It gets high randomness
+and, therefore, low probability of hash collisions at some CPU cost.
+This may be used if EHASHCOLLISION errors are experienced with the r5 hash.
+.TP
+.B r5
+A modified version of the rupasov hash. It is used by default and is
+the best choice unless the filesystem has huge directories and
+unusual file-name patterns.
+.TP
+.B detect
+Instructs
+.IR mount
+to detect which hash function is in use by examining
+the filesystem being mounted, and to write this information into
+the reiserfs superblock. This is only useful on the first mount of
+an old format filesystem.
+.RE
+.TP
+.BR hashed_relocation
+Tunes the block allocator. This may provide performance improvements
+in some situations.
+.TP
+.BR no_unhashed_relocation
+Tunes the block allocator. This may provide performance improvements
+in some situations.
+.TP
+.BR noborder
+Disable the border allocator algorithm invented by Yury Yu. Rupasov.
+This may provide performance improvements in some situations.
+.TP
+.BR nolog
+Disable journalling. This will provide slight performance improvements in
+some situations at the cost of losing reiserfs's fast recovery from crashes.
+Even with this option turned on, reiserfs still performs all journalling
+operations, save for actual writes into its journalling area. Implementation
+of
+.IR nolog
+is a work in progress.
+.TP
+.BR notail
+By default, reiserfs stores small files and `file tails' directly into its
+tree. This confuses some utilities such as
+.BR LILO (8).
+This option is used to disable packing of files into the tree.
+.TP
+.BR replayonly
+Replay the transactions which are in the journal, but do not actually
+mount the filesystem. Mainly used by
+.IR reiserfsck .
+.TP
+.BI resize= number
+A remount option which permits online expansion of reiserfs partitions.
+Instructs reiserfs to assume that the device has
+.I number
+blocks.
+This option is designed for use with devices which are under logical
+volume management (LVM).
+There is a special
+.I resizer
+utility which can be obtained from
+.IR ftp://ftp.namesys.com/pub/reiserfsprogs .
+.TP
+.BR user_xattr
+Enable Extended User Attributes. See the
+.BR attr (5)
+manual page.
+.TP
+.BR acl
+Enable POSIX Access Control Lists. See the
+.BR acl (5)
+manual page.
+.TP
+.BR barrier=none " / " barrier=flush "
+This enables/disables the use of write barriers in the journaling code.
+barrier=none disables it, barrier=flush enables it. Write barriers enforce
+proper on-disk ordering of journal commits, making volatile disk write caches
+safe to use, at some performance penalty. The reiserfs filesystem does not
+enable write barriers by default. Be sure to enable barriers unless your disks
+are battery-backed one way or another. Otherwise you risk filesystem
+corruption in case of power failure.
+
+.SH "Mount options for romfs"
+None.
+
+.SH "Mount options for squashfs"
+None.
+
+.SH "Mount options for smbfs"
+Just like
+.IR nfs ", the " smbfs
+implementation expects a binary argument (a
+.IR "struct smb_mount_data" )
+to the mount system call. This argument is constructed by
+.BR smbmount (8)
+and the current version of
+.B mount
+(2.12) does not know anything about smbfs.
+
+.SH "Mount options for sysv"
+None.
+
+.SH "Mount options for tmpfs"
+.TP
+.BI size= nbytes
+Override default maximum size of the filesystem.
+The size is given in bytes, and rounded up to entire pages.
+The default is half of the memory. The size parameter also accepts a suffix %
+to limit this tmpfs instance to that percentage of your physical RAM:
+the default, when neither size nor nr_blocks is specified, is size=50%
+.TP
+.B nr_blocks=
+The same as size, but in blocks of PAGE_CACHE_SIZE
+.TP
+.B nr_inodes=
+The maximum number of inodes for this instance. The default
+is half of the number of your physical RAM pages, or (on a
+machine with highmem) the number of lowmem RAM pages,
+whichever is the lower.
+.PP
+The tmpfs mount options for sizing (
+.BR size ,
+.BR nr_blocks ,
+and
+.BR nr_inodes )
+accept a suffix
+.BR k ,
+.B m
+or
+.B g
+for Ki, Mi, Gi (binary kilo, mega and giga) and can be changed on remount.
+
+.TP
+.B mode=
+Set initial permissions of the root directory.
+.TP
+.B uid=
+The user id.
+.TP
+.B gid=
+The group id.
+.TP
+.B mpol=[default|prefer:Node|bind:NodeList|interleave|interleave:NodeList]
+Set the NUMA memory allocation policy for all files in that
+instance (if the kernel CONFIG_NUMA is enabled) - which can be adjusted on the
+fly via 'mount -o remount ...'
+.RS
+.TP
+.B default
+prefers to allocate memory from the local node
+.TP
+.B prefer:Node
+prefers to allocate memory from the given Node
+.TP
+.B bind:NodeList
+allocates memory only from nodes in NodeList
+.TP
+.B interleave
+prefers to allocate from each node in turn
+.TP
+.B interleave:NodeList
+allocates from each node of NodeList in turn.
+.PP
+The NodeList format is a comma-separated list of decimal numbers and ranges, a
+range being two hyphen-separated decimal numbers, the smallest and largest node
+numbers in the range. For example, mpol=bind:0-3,5,7,9-15
+
+Note that trying to mount a tmpfs with an mpol option will fail if the
+running kernel does not support NUMA; and will fail if its nodelist
+specifies a node which is not online. If your system relies on that
+tmpfs being mounted, but from time to time runs a kernel built without
+NUMA capability (perhaps a safe recovery kernel), or with fewer nodes
+online, then it is advisable to omit the mpol option from automatic
+mount options. It can be added later, when the tmpfs is already mounted
+on MountPoint, by 'mount -o remount,mpol=Policy:NodeList MountPoint'.
+
+.SH "Mount options for ubifs"
+UBIFS is a flash file system which works on top of UBI volumes. Note that
+.B
+atime
+is not supported and is always turned off.
+.TP
+The device name may be specified as
+.RS
+.B ubiX_Y
+UBI device number
+.BR X ,
+volume number
+.B Y
+.TP
+.B ubiY
+UBI device number
+.BR 0 ,
+volume number
+.B Y
+.TP
+.B ubiX:NAME
+UBI device number
+.BR X ,
+volume with name
+.B NAME
+.TP
+.B ubi:NAME
+UBI device number
+.BR 0 ,
+volume with name
+.B NAME
+.RE
+Alternative
+.B !
+separator may be used instead of
+.BR : .
+.TP
+The following mount options are available:
+.TP
+.BR bulk_read
+Enable bulk-read. VFS read-ahead is disabled because it slows down the file
+system. Bulk-Read is an internal optimization. Some flashes may read faster if
+the data are read at one go, rather than at several read requests. For
+example, OneNAND can do "read-while-load" if it reads more than one NAND page.
+.TP
+.BR no_bulk_read
+Do not bulk-read. This is the default.
+.TP
+.BR chk_data_crc
+Check data CRC-32 checksums. This is the default.
+.TP
+.BR no_chk_data_crc.
+Do not check data CRC-32 checksums. With this option, the filesystem does not
+check CRC-32 checksum for data, but it does check it for the internal indexing
+information. This option only affects reading, not writing. CRC-32 is always
+calculated when writing the data.
+.TP
+.BR compr= { none | lzo | zlib }
+Select the default compressor which is used when new files are written. It is
+still possible to read compressed files if mounted with the
+.B none
+option.
+
+.SH "Mount options for udf"
+udf is the "Universal Disk Format" filesystem defined by the Optical
+Storage Technology Association, and is often used for DVD-ROM.
+See also
+.IR iso9660 .
+.TP
+.B gid=
+Set the default group.
+.TP
+.B umask=
+Set the default umask.
+The value is given in octal.
+.TP
+.B uid=
+Set the default user.
+.TP
+.B unhide
+Show otherwise hidden files.
+.TP
+.B undelete
+Show deleted files in lists.
+.TP
+.B nostrict
+Unset strict conformance.
+.\" .TP
+.\" .B utf8
+.\" (unused).
+.TP
+.B iocharset
+Set the NLS character set.
+.TP
+.B bs=
+Set the block size. (May not work unless 2048.)
+.TP
+.B novrs
+Skip volume sequence recognition.
+.TP
+.B session=
+Set the CDROM session counting from 0. Default: last session.
+.TP
+.B anchor=
+Override standard anchor location. Default: 256.
+.TP
+.B volume=
+Override the VolumeDesc location. (unused)
+.TP
+.B partition=
+Override the PartitionDesc location. (unused)
+.TP
+.B lastblock=
+Set the last block of the filesystem.
+.TP
+.B fileset=
+Override the fileset block location. (unused)
+.TP
+.B rootdir=
+Override the root directory location. (unused)
+
+.SH "Mount options for ufs"
+.TP
+.BI ufstype= value
+UFS is a filesystem widely used in different operating systems.
+The problem are differences among implementations. Features of some
+implementations are undocumented, so its hard to recognize the
+type of ufs automatically.
+That's why the user must specify the type of ufs by mount option.
+Possible values are:
+.RS
+.TP
+.B old
+Old format of ufs, this is the default, read only.
+(Don't forget to give the \-r option.)
+.TP
+.B 44bsd
+For filesystems created by a BSD-like system (NetBSD,FreeBSD,OpenBSD).
+.TP
+.B ufs2
+Used in FreeBSD 5.x supported as read-write.
+.TP
+.B 5xbsd
+Synonym for ufs2.
+.TP
+.B sun
+For filesystems created by SunOS or Solaris on Sparc.
+.TP
+.B sunx86
+For filesystems created by Solaris on x86.
+.TP
+.B hp
+For filesystems created by HP-UX, read-only.
+.TP
+.B nextstep
+For filesystems created by NeXTStep (on NeXT station) (currently read only).
+.TP
+.B nextstep-cd
+For NextStep CDROMs (block_size == 2048), read-only.
+.TP
+.B openstep
+For filesystems created by OpenStep (currently read only).
+The same filesystem type is also used by Mac OS X.
+.RE
+
+.TP
+.BI onerror= value
+Set behaviour on error:
+.RS
+.TP
+.B panic
+If an error is encountered, cause a kernel panic.
+.TP
+.RB [ lock | umount | repair ]
+These mount options don't do anything at present;
+when an error is encountered only a console message is printed.
+.RE
+
+.SH "Mount options for umsdos"
+See mount options for msdos.
+The
+.B dotsOK
+option is explicitly killed by
+.IR umsdos .
+
+.SH "Mount options for vfat"
+First of all, the mount options for
+.I fat
+are recognized.
+The
+.B dotsOK
+option is explicitly killed by
+.IR vfat .
+Furthermore, there are
+.TP
+.B uni_xlate
+Translate unhandled Unicode characters to special escaped sequences.
+This lets you backup and restore filenames that are created with any
+Unicode characters. Without this option, a '?' is used when no
+translation is possible. The escape character is ':' because it is
+otherwise illegal on the vfat filesystem. The escape sequence
+that gets used, where u is the unicode character,
+is: ':', (u & 0x3f), ((u>>6) & 0x3f), (u>>12).
+.TP
+.B posix
+Allow two files with names that only differ in case.
+This option is obsolete.
+.TP
+.B nonumtail
+First try to make a short name without sequence number,
+before trying
+.IR name~num.ext .
+.TP
+.B utf8
+UTF8 is the filesystem safe 8-bit encoding of Unicode that is used by the
+console. It can be enabled for the filesystem with this option or disabled
+with utf8=0, utf8=no or utf8=false. If `uni_xlate' gets set, UTF8 gets
+disabled.
+.TP
+.BR shortname= { lower | win95 | winnt | mixed }
+
+Defines the behaviour for creation and display of filenames which fit into
+8.3 characters. If a long name for a file exists, it will always be
+preferred display. There are four modes:
+:
+.RS
+.TP
+.I lower
+Force the short name to lower case upon display; store a long name when
+the short name is not all upper case.
+.TP
+.I win95
+Force the short name to upper case upon display; store a long name when
+the short name is not all upper case.
+.TP
+.I winnt
+Display the shortname as is; store a long name when the short name is
+not all lower case or all upper case.
+.TP
+.I mixed
+Display the short name as is; store a long name when the short name is not
+all upper case. This mode is the default since Linux 2.6.32.
+.RE
+
+
+.SH "Mount options for usbfs"
+.TP
+\fBdevuid=\fP\fIuid\fP and \fBdevgid=\fP\fIgid\fP and \fBdevmode=\fP\fImode\fP
+Set the owner and group and mode of the device files in the usbfs filesystem
+(default: uid=gid=0, mode=0644). The mode is given in octal.
+.TP
+\fBbusuid=\fP\fIuid\fP and \fBbusgid=\fP\fIgid\fP and \fBbusmode=\fP\fImode\fP
+Set the owner and group and mode of the bus directories in the usbfs
+filesystem (default: uid=gid=0, mode=0555). The mode is given in octal.
+.TP
+\fBlistuid=\fP\fIuid\fP and \fBlistgid=\fP\fIgid\fP and \fBlistmode=\fP\fImode\fP
+Set the owner and group and mode of the file
+.I devices
+(default: uid=gid=0, mode=0444). The mode is given in octal.
+
+.SH "Mount options for xenix"
+None.
+
+.SH "Mount options for xfs"
+.TP
+.BI allocsize= size
+Sets the buffered I/O end-of-file preallocation size when
+doing delayed allocation writeout (default size is 64KiB).
+Valid values for this option are page size (typically 4KiB)
+through to 1GiB, inclusive, in power-of-2 increments.
+.TP
+.BR attr2 | noattr2
+The options enable/disable (default is enabled) an "opportunistic"
+improvement to be made in the way inline extended attributes are
+stored on-disk.
+When the new form is used for the first time (by setting or
+removing extended attributes) the on-disk superblock feature
+bit field will be updated to reflect this format being in use.
+.TP
+.B barrier
+Enables the use of block layer write barriers for writes into
+the journal and unwritten extent conversion. This allows for
+drive level write caching to be enabled, for devices that
+support write barriers.
+.TP
+.B dmapi
+Enable the DMAPI (Data Management API) event callouts.
+Use with the
+.B mtpt
+option.
+.TP
+.BR grpid | bsdgroups " and " nogrpid | sysvgroups
+These options define what group ID a newly created file gets.
+When grpid is set, it takes the group ID of the directory in
+which it is created; otherwise (the default) it takes the fsgid
+of the current process, unless the directory has the setgid bit
+set, in which case it takes the gid from the parent directory,
+and also gets the setgid bit set if it is a directory itself.
+.TP
+.BI ihashsize= value
+Sets the number of hash buckets available for hashing the
+in-memory inodes of the specified mount point. If a value
+of zero is used, the value selected by the default algorithm
+will be displayed in
+.IR /proc/mounts .
+.TP
+.BR ikeep | noikeep
+When inode clusters are emptied of inodes, keep them around
+on the disk (ikeep) - this is the traditional XFS behaviour
+and is still the default for now. Using the noikeep option,
+inode clusters are returned to the free space pool.
+.TP
+.B inode64
+Indicates that XFS is allowed to create inodes at any location
+in the filesystem, including those which will result in inode
+numbers occupying more than 32 bits of significance. This is
+provided for backwards compatibility, but causes problems for
+backup applications that cannot handle large inode numbers.
+.TP
+.BR largeio | nolargeio
+If
+.B nolargeio
+is specified, the optimal I/O reported in
+st_blksize by
+.BR stat (2)
+will be as small as possible to allow user
+applications to avoid inefficient read/modify/write I/O.
+If
+.B largeio
+is specified, a filesystem that has a
+.B swidth
+specified
+will return the
+.B swidth
+value (in bytes) in st_blksize. If the
+filesystem does not have a
+.B swidth
+specified but does specify
+an
+.B allocsize
+then
+.B allocsize
+(in bytes) will be returned
+instead.
+If neither of these two options are specified, then filesystem
+will behave as if
+.B nolargeio
+was specified.
+.TP
+.BI logbufs= value
+Set the number of in-memory log buffers. Valid numbers range
+from 2-8 inclusive.
+The default value is 8 buffers for any recent kernel.
+.TP
+.BI logbsize= value
+Set the size of each in-memory log buffer.
+Size may be specified in bytes, or in kilobytes with a "k" suffix.
+Valid sizes for version 1 and version 2 logs are 16384 (16k) and
+32768 (32k). Valid sizes for version 2 logs also include
+65536 (64k), 131072 (128k) and 262144 (256k).
+The default value for any recent kernel is 32768.
+.TP
+\fBlogdev=\fP\fIdevice\fP and \fBrtdev=\fP\fIdevice\fP
+Use an external log (metadata journal) and/or real-time device.
+An XFS filesystem has up to three parts: a data section, a log section,
+and a real-time section.
+The real-time section is optional, and the log section can be separate
+from the data section or contained within it.
+Refer to
+.BR xfs (5).
+.TP
+.BI mtpt= mountpoint
+Use with the
+.B dmapi
+option. The value specified here will be
+included in the DMAPI mount event, and should be the path of
+the actual mountpoint that is used.
+.TP
+.B noalign
+Data allocations will not be aligned at stripe unit boundaries.
+.TP
+.B noatime
+Access timestamps are not updated when a file is read.
+.TP
+.B norecovery
+The filesystem will be mounted without running log recovery.
+If the filesystem was not cleanly unmounted, it is likely to
+be inconsistent when mounted in
+.B norecovery
+mode.
+Some files or directories may not be accessible because of this.
+Filesystems mounted
+.B norecovery
+must be mounted read-only or the mount will fail.
+.TP
+.B nouuid
+Don't check for double mounted filesystems using the filesystem uuid.
+This is useful to mount LVM snapshot volumes.
+.TP
+.B osyncisosync
+Make O_SYNC writes implement true O_SYNC. WITHOUT this option,
+Linux XFS behaves as if an
+.B osyncisdsync
+option is used,
+which will make writes to files opened with the O_SYNC flag set
+behave as if the O_DSYNC flag had been used instead.
+This can result in better performance without compromising
+data safety.
+However if this option is not in effect, timestamp updates from
+O_SYNC writes can be lost if the system crashes.
+If timestamp updates are critical, use the
+.B osyncisosync
+option.
+.TP
+.BR uquota | usrquota | uqnoenforce | quota
+User disk quota accounting enabled, and limits (optionally)
+enforced. Refer to
+.BR xfs_quota (8)
+for further details.
+.TP
+.BR gquota | grpquota | gqnoenforce
+Group disk quota accounting enabled and limits (optionally)
+enforced. Refer to
+.BR xfs_quota (8)
+for further details.
+.TP
+.BR pquota | prjquota | pqnoenforce
+Project disk quota accounting enabled and limits (optionally)
+enforced. Refer to
+.BR xfs_quota (8)
+for further details.
+.TP
+\fBsunit=\fP\fIvalue\fP and \fBswidth=\fP\fIvalue\fP
+Used to specify the stripe unit and width for a RAID device or a stripe
+volume.
+.I value
+must be specified in 512-byte block units.
+If this option is not specified and the filesystem was made on a stripe
+volume or the stripe width or unit were specified for the RAID device at
+mkfs time, then the mount system call will restore the value from the
+superblock.
+For filesystems that are made directly on RAID devices, these options can be
+used to override the information in the superblock if the underlying disk
+layout changes after the filesystem has been created.
+The
+.B swidth
+option is required if the
+.B sunit
+option has been specified,
+and must be a multiple of the
+.B sunit
+value.
+.TP
+.B swalloc
+Data allocations will be rounded up to stripe width boundaries
+when the current end of file is being extended and the file
+size is larger than the stripe width size.
+
+.SH "Mount options for xiafs"
+None. Although nothing is wrong with xiafs, it is not used much,
+and is not maintained. Probably one shouldn't use it.
+Since Linux version 2.1.21 xiafs is no longer part of the kernel source.
+
+.SH "THE LOOP DEVICE"
+One further possible type is a mount via the loop device. For example,
+the command
+.RS
+.sp
+.B "mount /tmp/disk.img /mnt -t vfat -o loop=/dev/loop"
+.sp
+.RE
+will set up the loop device
+.I /dev/loop3
+to correspond to the file
+.IR /tmp/disk.img ,
+and then mount this device on
+.IR /mnt .
+
+If no explicit loop device is mentioned
+(but just an option `\fB\-o loop\fP' is given), then
+.B mount
+will try to find some unused loop device and use that, for example
+.RS
+.sp
+.B "mount /tmp/disk.img /mnt -o loop"
+.sp
+.RE
+The mount command
+.B automatically
+creates a loop device from a regular file if a filesystem type is
+not specified or the filesystem is known for libblkid, for example:
+.RS
+.sp
+.B "mount /tmp/disk.img /mnt"
+.sp
+.B "mount -t ext3 /tmp/disk.img /mnt"
+.sp
+.RE
+This type of mount knows about four options, namely
+.BR loop ", " offset ", " sizelimit " and " encryption ,
+that are really options to
+.BR \%losetup (8).
+(These options can be used in addition to those specific
+to the filesystem type.)
+
+Since Linux 2.6.25 is supported auto-destruction of loop devices and
+then any loop device allocated by
+.B mount
+will be freed by
+.B umount
+independently on
+.IR /etc/mtab .
+
+You can also free a loop device by hand, using `losetup -d' or `umount -d`.
+
+.SH RETURN CODES
+.B mount
+has the following return codes (the bits can be ORed):
+.TP
+.BR 0
+success
+.TP
+.BR 1
+incorrect invocation or permissions
+.TP
+.BR 2
+system error (out of memory, cannot fork, no more loop devices)
+.TP
+.BR 4
+internal
+.B mount
+bug
+.TP
+.BR 8
+user interrupt
+.TP
+.BR 16
+problems writing or locking /etc/mtab
+.TP
+.BR 32
+mount failure
+.TP
+.BR 64
+some mount succeeded
+.RE
+
+The command mount -a returns 0 (all success), 32 (all failed) or 64 (some
+failed, some success).
+
+.SH NOTES
+The syntax of external mount helpers is:
+
+.RS
+.BI /sbin/mount. <suffix>
+.I spec dir
+.RB [ \-sfnv ]
+.RB [ \-o
+.IR options ]
+.RB [ \-t
+.IR type.subtype ]
+.RE
+
+where the <type> is filesystem type and \-sfnvo options have same meaning like
+standard mount options. The \-t option is used for filesystems with subtypes
+support (for example /sbin/mount.fuse -t fuse.sshfs).
+
+.SH FILES
+.TP 18n
+.I /etc/fstab
+filesystem table
+.TP
+.I /etc/mtab
+table of mounted filesystems
+.TP
+.I /etc/mtab~
+lock file
+.TP
+.I /etc/mtab.tmp
+temporary file
+.TP
+.I /etc/filesystems
+a list of filesystem types to try
+.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 "SEE ALSO"
+.BR mount (2),
+.BR umount (2),
+.BR fstab (5),
+.BR umount (8),
+.BR swapon (8),
+.BR findmnt (8),
+.BR nfs (5),
+.BR xfs (5),
+.BR e2label (8),
+.BR xfs_admin (8),
+.BR mountd (8),
+.BR nfsd (8),
+.BR mke2fs (8),
+.BR tune2fs (8),
+.BR losetup (8)
+.SH BUGS
+It is possible for a corrupted filesystem to cause a crash.
+.PP
+Some Linux filesystems don't support
+.B "\-o sync and \-o dirsync"
+(the ext2, ext3, fat and vfat filesystems
+.I do
+support synchronous updates (a la BSD) when mounted with the
+.B sync
+option).
+.PP
+The
+.B "\-o remount"
+may not be able to change mount parameters (all
+.IR ext2fs -specific
+parameters, except
+.BR sb ,
+are changeable with a remount, for example, but you can't change
+.B gid
+or
+.B umask
+for the
+.IR fatfs ).
+.PP
+It is possible that files
+.IR /etc/mtab
+and
+.IR /proc/mounts
+don't match. The first file is based only on the mount command options, but the
+content of the second file also depends on the kernel and others settings (e.g.
+remote NFS server. In particular case the mount command may reports unreliable
+information about a NFS mount point and the /proc/mounts file usually contains
+more reliable information.)
+.PP
+Checking files on NFS filesystem referenced by file descriptors (i.e. the
+.BR fcntl
+and
+.BR ioctl
+families of functions) may lead to inconsistent result due to the lack of
+consistency check in kernel even if noac is used.
+.SH HISTORY
+A
+.B mount
+command existed in Version 5 AT&T UNIX.
+.SH AUTHORS
+.nf
+Karel Zak <kzak@redhat.com>
+.fi
+.SH AVAILABILITY
+The mount command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+
diff --git a/sys-utils/mount.c b/sys-utils/mount.c
new file mode 100644
index 0000000..2d1d2cd
--- /dev/null
+++ b/sys-utils/mount.c
@@ -0,0 +1,995 @@
+/*
+ * mount(8) -- mount a filesystem
+ *
+ * Copyright (C) 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 <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <libmount.h>
+#include <ctype.h>
+
+#include "nls.h"
+#include "c.h"
+#include "env.h"
+#include "strutils.h"
+#include "xgetpass.h"
+#include "exitcodes.h"
+#include "xalloc.h"
+#include "closestream.h"
+
+#define OPTUTILS_EXIT_CODE MOUNT_EX_USAGE
+#include "optutils.h"
+
+/*** TODO: DOCS:
+ *
+ * --options-mode={ignore,append,prepend,replace} MNT_OMODE_{IGNORE, ...}
+ * --options-source={fstab,mtab,disable} MNT_OMODE_{FSTAB,MTAB,NOTAB}
+ * --options-source-force MNT_OMODE_FORCE
+ */
+
+static int passfd = -1;
+static int readwrite;
+
+static int mk_exit_code(struct libmnt_context *cxt, int rc);
+
+static void __attribute__((__noreturn__)) exit_non_root(const char *option)
+{
+ const uid_t ruid = getuid();
+ const uid_t euid = geteuid();
+
+ if (ruid == 0 && euid != 0) {
+ /* user is root, but setuid to non-root */
+ if (option)
+ errx(MOUNT_EX_USAGE, _("only root can use \"--%s\" option "
+ "(effective UID is %u)"),
+ option, euid);
+ errx(MOUNT_EX_USAGE, _("only root can do that "
+ "(effective UID is %u)"), euid);
+ }
+ if (option)
+ errx(MOUNT_EX_USAGE, _("only root can use \"--%s\" option"), option);
+ errx(MOUNT_EX_USAGE, _("only root can do that"));
+}
+
+static void __attribute__((__noreturn__)) print_version(void)
+{
+ const char *ver = NULL;
+ const char **features = NULL, **p;
+
+ mnt_get_library_version(&ver);
+ mnt_get_library_features(&features);
+
+ printf(_("%s from %s (libmount %s"),
+ program_invocation_short_name,
+ PACKAGE_STRING,
+ ver);
+ p = features;
+ while (p && *p) {
+ fputs(p == features ? ": " : ", ", stdout);
+ fputs(*p++, stdout);
+ }
+ fputs(")\n", stdout);
+ exit(MOUNT_EX_SUCCESS);
+}
+
+static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__)),
+ const char *filename, int line)
+{
+ if (filename)
+ warnx(_("%s: parse error: ignore entry at line %d."),
+ filename, line);
+ return 0;
+}
+
+static char *encrypt_pass_get(struct libmnt_context *cxt)
+{
+ if (!cxt)
+ return 0;
+
+#ifdef MCL_FUTURE
+ if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
+ warn(_("couldn't lock into memory"));
+ return NULL;
+ }
+#endif
+ return xgetpass(passfd, _("Password: "));
+}
+
+static void encrypt_pass_release(struct libmnt_context *cxt
+ __attribute__((__unused__)), char *pwd)
+{
+ char *p = pwd;
+
+ while (p && *p)
+ *p++ = '\0';
+
+ free(pwd);
+ munlockall();
+}
+
+/*
+ * Replace control chars with '?' to be compatible with coreutils. For more
+ * robust solution use findmnt(1) where we use \x?? hex encoding.
+ */
+static void safe_fputs(const char *data)
+{
+ const char *p;
+
+ for (p = data; p && *p; p++) {
+ if (iscntrl((unsigned char) *p))
+ fputc('?', stdout);
+ else
+ fputc(*p, stdout);
+ }
+}
+
+static void print_all(struct libmnt_context *cxt, char *pattern, int show_label)
+{
+ struct libmnt_table *tb;
+ struct libmnt_iter *itr = NULL;
+ struct libmnt_fs *fs;
+ struct libmnt_cache *cache = NULL;
+
+ if (mnt_context_get_mtab(cxt, &tb))
+ err(MOUNT_EX_SYSERR, _("failed to read mtab"));
+
+ itr = mnt_new_iter(MNT_ITER_FORWARD);
+ if (!itr)
+ err(MOUNT_EX_SYSERR, _("failed to initialize libmount iterator"));
+ if (show_label)
+ cache = mnt_new_cache();
+
+ while (mnt_table_next_fs(tb, itr, &fs) == 0) {
+ const char *type = mnt_fs_get_fstype(fs);
+ const char *src = mnt_fs_get_source(fs);
+ const char *optstr = mnt_fs_get_options(fs);
+ char *xsrc = NULL;
+
+ if (type && pattern && !mnt_match_fstype(type, pattern))
+ continue;
+
+ if (!mnt_fs_is_pseudofs(fs))
+ xsrc = mnt_pretty_path(src, cache);
+ printf ("%s on ", xsrc ? xsrc : src);
+ safe_fputs(mnt_fs_get_target(fs));
+
+ if (type)
+ printf (" type %s", type);
+ if (optstr)
+ printf (" (%s)", optstr);
+ if (show_label && src) {
+ char *lb = mnt_cache_find_tag_value(cache, src, "LABEL");
+ if (lb)
+ printf (" [%s]", lb);
+ }
+ fputc('\n', stdout);
+ free(xsrc);
+ }
+
+ mnt_free_cache(cache);
+ mnt_free_iter(itr);
+}
+
+/*
+ * mount -a [-F]
+ */
+static int mount_all(struct libmnt_context *cxt)
+{
+ struct libmnt_iter *itr;
+ struct libmnt_fs *fs;
+ int mntrc, ignored, rc = MOUNT_EX_SUCCESS;
+
+ int nsucc = 0, nerrs = 0;
+
+ itr = mnt_new_iter(MNT_ITER_FORWARD);
+ if (!itr) {
+ warn(_("failed to initialize libmount iterator"));
+ return MOUNT_EX_SYSERR;
+ }
+
+ while (mnt_context_next_mount(cxt, itr, &fs, &mntrc, &ignored) == 0) {
+
+ const char *tgt = mnt_fs_get_target(fs);
+
+ if (ignored) {
+ if (mnt_context_is_verbose(cxt))
+ printf(ignored == 1 ? _("%-25s: ignored\n") :
+ _("%-25s: already mounted\n"),
+ tgt);
+ } else if (mnt_context_is_fork(cxt)) {
+ if (mnt_context_is_verbose(cxt))
+ printf("%-25s: mount successfully forked\n", tgt);
+ } else {
+ mk_exit_code(cxt, mntrc); /* to print warnings */
+
+ if (mnt_context_get_status(cxt)) {
+ nsucc++;
+
+ if (mnt_context_is_verbose(cxt))
+ printf("%-25s: successfully mounted\n", tgt);
+ } else
+ nerrs++;
+ }
+ }
+
+ if (mnt_context_is_parent(cxt)) {
+ /* wait for mount --fork children */
+ int nchildren = 0;
+
+ nerrs = 0, nsucc = 0;
+
+ rc = mnt_context_wait_for_children(cxt, &nchildren, &nerrs);
+ if (!rc && nchildren)
+ nsucc = nchildren - nerrs;
+ }
+
+ if (nerrs == 0)
+ rc = MOUNT_EX_SUCCESS; /* all success */
+ else if (nsucc == 0)
+ rc = MOUNT_EX_FAIL; /* all failed */
+ else
+ rc = MOUNT_EX_SOMEOK; /* some success, some failed */
+
+ mnt_free_iter(itr);
+ return rc;
+}
+
+/*
+ * Handles generic errors like ENOMEM, ...
+ *
+ * rc = 0 success
+ * <0 error (usually -errno)
+ *
+ * Returns exit status (MOUNT_EX_*) and prints error message.
+ */
+static int handle_generic_errors(int rc, const char *msg, ...)
+{
+ va_list va;
+
+ va_start(va, msg);
+ errno = -rc;
+
+ switch(errno) {
+ case EINVAL:
+ case EPERM:
+ vwarn(msg, va);
+ rc = MOUNT_EX_USAGE;
+ break;
+ case ENOMEM:
+ vwarn(msg, va);
+ rc = MOUNT_EX_SYSERR;
+ break;
+ default:
+ vwarn(msg, va);
+ rc = MOUNT_EX_FAIL;
+ break;
+ }
+ va_end(va);
+ return rc;
+}
+
+#if defined(HAVE_LIBSELINUX) && defined(HAVE_SECURITY_GET_INITIAL_CONTEXT)
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+
+static void selinux_warning(struct libmnt_context *cxt, const char *tgt)
+{
+
+ if (tgt && mnt_context_is_verbose(cxt) && is_selinux_enabled() > 0) {
+ security_context_t raw = NULL, def = NULL;
+
+ if (getfilecon(tgt, &raw) > 0
+ && security_get_initial_context("file", &def) == 0) {
+
+ if (!selinux_file_context_cmp(raw, def))
+ printf(_(
+ "mount: %s does not contain SELinux labels.\n"
+ " You just mounted an file system that supports labels which does not\n"
+ " contain labels, onto an SELinux box. It is likely that confined\n"
+ " applications will generate AVC messages and not be allowed access to\n"
+ " this file system. For more details see restorecon(8) and mount(8).\n"),
+ tgt);
+ }
+ freecon(raw);
+ freecon(def);
+ }
+}
+#else
+# define selinux_warning(_x, _y)
+#endif
+
+
+/*
+ * rc = 0 success
+ * <0 error (usually -errno or -1)
+ *
+ * Returns exit status (MOUNT_EX_*) and/or prints error message.
+ */
+static int mk_exit_code(struct libmnt_context *cxt, int rc)
+{
+ int syserr;
+ struct stat st;
+ unsigned long uflags = 0, mflags = 0;
+
+ int restricted = mnt_context_is_restricted(cxt);
+ const char *tgt = mnt_context_get_target(cxt);
+ const char *src = mnt_context_get_source(cxt);
+
+try_readonly:
+ if (mnt_context_helper_executed(cxt))
+ /*
+ * /sbin/mount.<type> called, return status
+ */
+ return mnt_context_get_helper_status(cxt);
+
+ if (rc == 0 && mnt_context_get_status(cxt) == 1) {
+ /*
+ * Libmount success && syscall success.
+ */
+ selinux_warning(cxt, tgt);
+
+ return MOUNT_EX_SUCCESS; /* mount(2) success */
+ }
+
+ mnt_context_get_mflags(cxt, &mflags); /* mount(2) flags */
+ mnt_context_get_user_mflags(cxt, &uflags); /* userspace flags */
+
+ if (!mnt_context_syscall_called(cxt)) {
+ /*
+ * libmount errors (extra library checks)
+ */
+ switch (rc) {
+ case -EPERM:
+ warnx(_("only root can mount %s on %s"), src, tgt);
+ return MOUNT_EX_USAGE;
+ case -EBUSY:
+ warnx(_("%s is already mounted"), src);
+ return MOUNT_EX_USAGE;
+ case -MNT_ERR_NOFSTAB:
+ if (mnt_context_is_swapmatch(cxt)) {
+ warnx(_("can't find %s in %s"),
+ src ? src : tgt,
+ mnt_get_fstab_path());
+ return MOUNT_EX_USAGE;
+ }
+ /* source/target explicitly defined */
+ if (tgt)
+ warnx(_("can't find mountpoint %s in %s"),
+ tgt, mnt_get_fstab_path());
+ else
+ warnx(_("can't find mount source %s in %s"),
+ src, mnt_get_fstab_path());
+ return MOUNT_EX_USAGE;
+ case -MNT_ERR_NOFSTYPE:
+ if (restricted)
+ warnx(_("I could not determine the filesystem type, "
+ "and none was specified"));
+ else
+ warnx(_("you must specify the filesystem type"));
+ return MOUNT_EX_USAGE;
+ case -MNT_ERR_NOSOURCE:
+ if (src)
+ warnx(_("can't find %s"), src);
+ else
+ warnx(_("mount source not defined"));
+ return MOUNT_EX_USAGE;
+ case -MNT_ERR_MOUNTOPT:
+ if (errno)
+ warn(_("failed to parse mount options"));
+ else
+ warnx(_("failed to parse mount options"));
+ return MOUNT_EX_USAGE;
+ case -MNT_ERR_LOOPDEV:
+ if (errno == ENOENT
+ && (uflags & MNT_MS_ENCRYPTION)
+ && src && stat(src, &st) == 0)
+ warnx(_("%s: failed to setup loop device "
+ "(probably unknown encryption type)"), src);
+ else
+ warn(_("%s: failed to setup loop device"), src);
+ return MOUNT_EX_FAIL;
+ default:
+ return handle_generic_errors(rc, _("%s: mount failed"),
+ tgt ? tgt : src);
+ }
+ } else if (mnt_context_get_syscall_errno(cxt) == 0) {
+ /*
+ * mount(2) syscall success, but something else failed
+ * (probably error in mtab processing).
+ */
+ if (rc < 0)
+ return handle_generic_errors(rc,
+ _("%s: filesystem mounted, but mount(8) failed"),
+ tgt ? tgt : src);
+
+ return MOUNT_EX_SOFTWARE; /* internal error */
+
+ }
+
+ /*
+ * mount(2) errors
+ */
+ syserr = mnt_context_get_syscall_errno(cxt);
+
+
+ switch(syserr) {
+ case EPERM:
+ if (geteuid() == 0) {
+ if (stat(tgt, &st) || !S_ISDIR(st.st_mode))
+ warnx(_("mount point %s is not a directory"), tgt);
+ else
+ warnx(_("permission denied"));
+ } else
+ warnx(_("must be superuser to use mount"));
+ break;
+
+ case EBUSY:
+ {
+ struct libmnt_table *tb;
+
+ if (mflags & MS_REMOUNT) {
+ warnx(_("%s is busy"), tgt);
+ break;
+ }
+
+ warnx(_("%s is already mounted or %s busy"), src, tgt);
+
+ if (src && mnt_context_get_mtab(cxt, &tb) == 0) {
+ struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_FORWARD);
+ struct libmnt_fs *fs;
+
+ while(mnt_table_next_fs(tb, itr, &fs) == 0) {
+ const char *s = mnt_fs_get_srcpath(fs),
+ *t = mnt_fs_get_target(fs);
+
+ if (t && s && mnt_fs_streq_srcpath(fs, src))
+ fprintf(stderr, _(
+ " %s is already mounted on %s\n"), s, t);
+ }
+ mnt_free_iter(itr);
+ }
+ break;
+ }
+ case ENOENT:
+ if (lstat(tgt, &st))
+ warnx(_("mount point %s does not exist"), tgt);
+ else if (stat(tgt, &st))
+ warnx(_("mount point %s is a symbolic link to nowhere"), tgt);
+ else if (stat(src, &st)) {
+ if (uflags & MNT_MS_NOFAIL)
+ return MOUNT_EX_SUCCESS;
+
+ warnx(_("special device %s does not exist"), src);
+ } else {
+ errno = syserr;
+ warn(_("mount(2) failed")); /* print errno */
+ }
+ break;
+
+ case ENOTDIR:
+ if (stat(tgt, &st) || ! S_ISDIR(st.st_mode))
+ warnx(_("mount point %s is not a directory"), tgt);
+ else if (stat(src, &st) && errno == ENOTDIR) {
+ if (uflags & MNT_MS_NOFAIL)
+ return MOUNT_EX_SUCCESS;
+
+ warnx(_("special device %s does not exist "
+ "(a path prefix is not a directory)"), src);
+ } else {
+ errno = syserr;
+ warn(_("mount(2) failed")); /* print errno */
+ }
+ break;
+
+ case EINVAL:
+ if (mflags & MS_REMOUNT)
+ warnx(_("%s not mounted or bad option"), tgt);
+ else if (mflags & MS_PROPAGATION)
+ warnx(_("%s is not mountpoint or bad option"), tgt);
+ else
+ warnx(_("wrong fs type, bad option, bad superblock on %s,\n"
+ " missing codepage or helper program, or other error"),
+ src);
+
+ if (mnt_fs_is_netfs(mnt_context_get_fs(cxt)))
+ fprintf(stderr, _(
+ " (for several filesystems (e.g. nfs, cifs) you might\n"
+ " need a /sbin/mount.<type> helper program)\n"));
+
+ fprintf(stderr, _(
+ " In some cases useful info is found in syslog - try\n"
+ " dmesg | tail or so\n"));
+ break;
+
+ case EMFILE:
+ warnx(_("mount table full"));
+ break;
+
+ case EIO:
+ warnx(_("%s: can't read superblock"), src);
+ break;
+
+ case ENODEV:
+ warnx(_("unknown filesystem type '%s'"), mnt_context_get_fstype(cxt));
+ break;
+
+ case ENOTBLK:
+ if (uflags & MNT_MS_NOFAIL)
+ return MOUNT_EX_SUCCESS;
+
+ if (stat(src, &st))
+ warnx(_("%s is not a block device, and stat(2) fails?"), src);
+ else if (S_ISBLK(st.st_mode))
+ warnx(_("the kernel does not recognize %s as a block device\n"
+ " (maybe `modprobe driver'?)"), src);
+ else if (S_ISREG(st.st_mode))
+ warnx(_("%s is not a block device (maybe try `-o loop'?)"), src);
+ else
+ warnx(_(" %s is not a block device"), src);
+ break;
+
+ case ENXIO:
+ if (uflags & MNT_MS_NOFAIL)
+ return MOUNT_EX_SUCCESS;
+
+ warnx(_("%s is not a valid block device"), src);
+ break;
+
+ case EACCES:
+ case EROFS:
+ if (mflags & MS_RDONLY)
+ warnx(_("cannot mount %s read-only"), src);
+
+ else if (readwrite)
+ warnx(_("%s is write-protected but explicit `-w' flag given"), src);
+
+ else if (mflags & MS_REMOUNT)
+ warnx(_("cannot remount %s read-write, is write-protected"), src);
+
+ else {
+ warnx(_("%s is write-protected, mounting read-only"), src);
+
+ mnt_context_reset_status(cxt);
+ mnt_context_set_mflags(cxt, mflags | MS_RDONLY);
+ rc = mnt_context_do_mount(cxt);
+ if (!rc)
+ rc = mnt_context_finalize_mount(cxt);
+
+ goto try_readonly;
+ }
+ break;
+
+ case ENOMEDIUM:
+ warnx(_("no medium found on %s"), src);
+ break;
+
+ default:
+ warn(_("mount %s on %s failed"), src, tgt);
+ break;
+ }
+
+ return MOUNT_EX_FAIL;
+}
+
+static struct libmnt_table *append_fstab(struct libmnt_context *cxt,
+ struct libmnt_table *fstab,
+ const char *path)
+{
+
+ if (!fstab) {
+ fstab = mnt_new_table();
+ if (!fstab)
+ err(MOUNT_EX_SYSERR, _("failed to initialize libmount table"));
+
+ mnt_table_set_parser_errcb(fstab, table_parser_errcb);
+ mnt_context_set_fstab(cxt, fstab);
+ }
+
+ if (mnt_table_parse_fstab(fstab, path))
+ errx(MOUNT_EX_USAGE,_("%s: failed to parse"), path);
+
+ return fstab;
+}
+
+static void __attribute__((__noreturn__)) usage(FILE *out)
+{
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(
+ " %1$s [-lhV]\n"
+ " %1$s -a [options]\n"
+ " %1$s [options] [--source] <source> | [--target] <directory>\n"
+ " %1$s [options] <source> <directory>\n"
+ " %1$s <operation> <mountpoint> [<target>]\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_OPTIONS, out);
+ fprintf(out, _(
+ " -a, --all mount all filesystems mentioned in fstab\n"
+ " -c, --no-canonicalize don't canonicalize paths\n"
+ " -f, --fake dry run; skip the mount(2) syscall\n"
+ " -F, --fork fork off for each device (use with -a)\n"
+ " -T, --fstab <path> alternative file to /etc/fstab\n"));
+ fprintf(out, _(
+ " -h, --help display this help text and exit\n"
+ " -i, --internal-only don't call the mount.<type> helpers\n"
+ " -l, --show-labels lists all mounts with LABELs\n"
+ " -n, --no-mtab don't write to /etc/mtab\n"));
+ fprintf(out, _(
+ " -o, --options <list> comma-separated list of mount options\n"
+ " -O, --test-opts <list> limit the set of filesystems (use with -a)\n"
+ " -p, --pass-fd <num> read the passphrase from file descriptor\n"
+ " -r, --read-only mount the filesystem read-only (same as -o ro)\n"
+ " -t, --types <list> limit the set of filesystem types\n"));
+ fprintf(out, _(
+ " --source <src> explicitly specifies source (path, label, uuid)\n"
+ " --target <target> explicitly specifies mountpoint\n"));
+ fprintf(out, _(
+ " -v, --verbose say what is being done\n"
+ " -V, --version display version information and exit\n"
+ " -w, --read-write mount the filesystem read-write (default)\n"));
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+
+ fprintf(out, _(
+ "\nSource:\n"
+ " -L, --label <label> synonym for LABEL=<label>\n"
+ " -U, --uuid <uuid> synonym for UUID=<uuid>\n"
+ " LABEL=<label> specifies device by filesystem label\n"
+ " UUID=<uuid> specifies device by filesystem UUID\n"
+ " PARTLABEL=<label> specifies device by partition label\n"
+ " PARTUUID=<uuid> specifies device by partition UUID\n"));
+
+ fprintf(out, _(
+ " <device> specifies device by path\n"
+ " <directory> mountpoint for bind mounts (see --bind/rbind)\n"
+ " <file> regular file for loopdev setup\n"));
+
+ fprintf(out, _(
+ "\nOperations:\n"
+ " -B, --bind mount a subtree somewhere else (same as -o bind)\n"
+ " -M, --move move a subtree to some other place\n"
+ " -R, --rbind mount a subtree and all submounts somewhere else\n"));
+ fprintf(out, _(
+ " --make-shared mark a subtree as shared\n"
+ " --make-slave mark a subtree as slave\n"
+ " --make-private mark a subtree as private\n"
+ " --make-unbindable mark a subtree as unbindable\n"));
+ fprintf(out, _(
+ " --make-rshared recursively mark a whole subtree as shared\n"
+ " --make-rslave recursively mark a whole subtree as slave\n"
+ " --make-rprivate recursively mark a whole subtree as private\n"
+ " --make-runbindable recursively mark a whole subtree as unbindable\n"));
+
+ fprintf(out, USAGE_MAN_TAIL("mount(8)"));
+
+ exit(out == stderr ? MOUNT_EX_USAGE : MOUNT_EX_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ int c, rc = MOUNT_EX_SUCCESS, all = 0, show_labels = 0;
+ struct libmnt_context *cxt;
+ struct libmnt_table *fstab = NULL;
+ char *srcbuf = NULL;
+ char *types = NULL;
+ unsigned long oper = 0;
+
+ enum {
+ MOUNT_OPT_SHARED = CHAR_MAX + 1,
+ MOUNT_OPT_SLAVE,
+ MOUNT_OPT_PRIVATE,
+ MOUNT_OPT_UNBINDABLE,
+ MOUNT_OPT_RSHARED,
+ MOUNT_OPT_RSLAVE,
+ MOUNT_OPT_RPRIVATE,
+ MOUNT_OPT_RUNBINDABLE,
+ MOUNT_OPT_TARGET,
+ MOUNT_OPT_SOURCE
+ };
+
+ static const struct option longopts[] = {
+ { "all", 0, 0, 'a' },
+ { "fake", 0, 0, 'f' },
+ { "fstab", 1, 0, 'T' },
+ { "fork", 0, 0, 'F' },
+ { "help", 0, 0, 'h' },
+ { "no-mtab", 0, 0, 'n' },
+ { "read-only", 0, 0, 'r' },
+ { "ro", 0, 0, 'r' },
+ { "verbose", 0, 0, 'v' },
+ { "version", 0, 0, 'V' },
+ { "read-write", 0, 0, 'w' },
+ { "rw", 0, 0, 'w' },
+ { "options", 1, 0, 'o' },
+ { "test-opts", 1, 0, 'O' },
+ { "pass-fd", 1, 0, 'p' },
+ { "types", 1, 0, 't' },
+ { "uuid", 1, 0, 'U' },
+ { "label", 1, 0, 'L'},
+ { "bind", 0, 0, 'B' },
+ { "move", 0, 0, 'M' },
+ { "rbind", 0, 0, 'R' },
+ { "make-shared", 0, 0, MOUNT_OPT_SHARED },
+ { "make-slave", 0, 0, MOUNT_OPT_SLAVE },
+ { "make-private", 0, 0, MOUNT_OPT_PRIVATE },
+ { "make-unbindable", 0, 0, MOUNT_OPT_UNBINDABLE },
+ { "make-rshared", 0, 0, MOUNT_OPT_RSHARED },
+ { "make-rslave", 0, 0, MOUNT_OPT_RSLAVE },
+ { "make-rprivate", 0, 0, MOUNT_OPT_RPRIVATE },
+ { "make-runbindable", 0, 0, MOUNT_OPT_RUNBINDABLE },
+ { "no-canonicalize", 0, 0, 'c' },
+ { "internal-only", 0, 0, 'i' },
+ { "show-labels", 0, 0, 'l' },
+ { "target", 1, 0, MOUNT_OPT_TARGET },
+ { "source", 1, 0, MOUNT_OPT_SOURCE },
+ { NULL, 0, 0, 0 }
+ };
+
+ static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */
+ { 'B','M','R', /* bind,move,rbind */
+ MOUNT_OPT_SHARED, MOUNT_OPT_SLAVE,
+ MOUNT_OPT_PRIVATE, MOUNT_OPT_UNBINDABLE,
+ MOUNT_OPT_RSHARED, MOUNT_OPT_RSLAVE,
+ MOUNT_OPT_RPRIVATE, MOUNT_OPT_RUNBINDABLE },
+
+ { 'L','U', MOUNT_OPT_SOURCE }, /* label,uuid,source */
+ { 0 }
+ };
+ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+
+ sanitize_env();
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ mnt_init_debug(0);
+ cxt = mnt_new_context();
+ if (!cxt)
+ err(MOUNT_EX_SYSERR, _("libmount context allocation failed"));
+
+ mnt_context_set_tables_errcb(cxt, table_parser_errcb);
+
+ while ((c = getopt_long(argc, argv, "aBcfFhilL:Mno:O:p:rRsU:vVwt:T:",
+ longopts, NULL)) != -1) {
+
+ /* only few options are allowed for non-root users */
+ if (mnt_context_is_restricted(cxt) &&
+ !strchr("hlLUVvpris", c) &&
+ c != MOUNT_OPT_TARGET &&
+ c != MOUNT_OPT_SOURCE)
+ exit_non_root(option_to_longopt(c, longopts));
+
+ err_exclusive_options(c, longopts, excl, excl_st);
+
+ switch(c) {
+ case 'a':
+ all = 1;
+ break;
+ case 'c':
+ mnt_context_disable_canonicalize(cxt, TRUE);
+ break;
+ case 'f':
+ mnt_context_enable_fake(cxt, TRUE);
+ break;
+ case 'F':
+ mnt_context_enable_fork(cxt, TRUE);
+ break;
+ case 'h':
+ usage(stdout);
+ break;
+ case 'i':
+ mnt_context_disable_helpers(cxt, TRUE);
+ break;
+ case 'n':
+ mnt_context_disable_mtab(cxt, TRUE);
+ break;
+ case 'r':
+ if (mnt_context_append_options(cxt, "ro"))
+ err(MOUNT_EX_SYSERR, _("failed to append options"));
+ readwrite = 0;
+ break;
+ case 'v':
+ mnt_context_enable_verbose(cxt, TRUE);
+ break;
+ case 'V':
+ print_version();
+ break;
+ case 'w':
+ if (mnt_context_append_options(cxt, "rw"))
+ err(MOUNT_EX_SYSERR, _("failed to append options"));
+ readwrite = 1;
+ break;
+ case 'o':
+ if (mnt_context_append_options(cxt, optarg))
+ err(MOUNT_EX_SYSERR, _("failed to append options"));
+ break;
+ case 'O':
+ if (mnt_context_set_options_pattern(cxt, optarg))
+ err(MOUNT_EX_SYSERR, _("failed to set options pattern"));
+ break;
+ case 'p':
+ passfd = strtou32_or_err(optarg,
+ _("invalid passphrase file descriptor"));
+ break;
+ case 'L':
+ xasprintf(&srcbuf, "LABEL=\"%s\"", optarg);
+ mnt_context_disable_swapmatch(cxt, 1);
+ mnt_context_set_source(cxt, srcbuf);
+ free(srcbuf);
+ break;
+ case 'U':
+ xasprintf(&srcbuf, "UUID=\"%s\"", optarg);
+ mnt_context_disable_swapmatch(cxt, 1);
+ mnt_context_set_source(cxt, srcbuf);
+ free(srcbuf);
+ break;
+ case 'l':
+ show_labels = 1;
+ break;
+ case 't':
+ types = optarg;
+ break;
+ case 'T':
+ fstab = append_fstab(cxt, fstab, optarg);
+ break;
+ case 's':
+ mnt_context_enable_sloppy(cxt, TRUE);
+ break;
+ case 'B':
+ oper |= MS_BIND;
+ break;
+ case 'M':
+ oper |= MS_MOVE;
+ break;
+ case 'R':
+ oper |= (MS_BIND | MS_REC);
+ break;
+ case MOUNT_OPT_SHARED:
+ oper |= MS_SHARED;
+ break;
+ case MOUNT_OPT_SLAVE:
+ oper |= MS_SLAVE;
+ break;
+ case MOUNT_OPT_PRIVATE:
+ oper |= MS_PRIVATE;
+ break;
+ case MOUNT_OPT_UNBINDABLE:
+ oper |= MS_UNBINDABLE;
+ break;
+ case MOUNT_OPT_RSHARED:
+ oper |= (MS_SHARED | MS_REC);
+ break;
+ case MOUNT_OPT_RSLAVE:
+ oper |= (MS_SLAVE | MS_REC);
+ break;
+ case MOUNT_OPT_RPRIVATE:
+ oper |= (MS_PRIVATE | MS_REC);
+ break;
+ case MOUNT_OPT_RUNBINDABLE:
+ oper |= (MS_UNBINDABLE | MS_REC);
+ break;
+ case MOUNT_OPT_TARGET:
+ mnt_context_disable_swapmatch(cxt, 1);
+ mnt_context_set_target(cxt, optarg);
+ break;
+ case MOUNT_OPT_SOURCE:
+ mnt_context_disable_swapmatch(cxt, 1);
+ mnt_context_set_source(cxt, optarg);
+ break;
+ default:
+ usage(stderr);
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (!mnt_context_get_source(cxt) &&
+ !mnt_context_get_target(cxt) &&
+ !argc &&
+ !all) {
+ if (oper)
+ usage(stderr);
+ print_all(cxt, types, show_labels);
+ goto done;
+ }
+
+ if (oper && (types || all || mnt_context_get_source(cxt)))
+ usage(stderr);
+
+ if (types && (all || strchr(types, ',') ||
+ strncmp(types, "no", 2) == 0))
+ mnt_context_set_fstype_pattern(cxt, types);
+ else if (types)
+ mnt_context_set_fstype(cxt, types);
+
+ mnt_context_set_passwd_cb(cxt, encrypt_pass_get, encrypt_pass_release);
+
+ if (all) {
+ /*
+ * A) Mount all
+ */
+ rc = mount_all(cxt);
+ goto done;
+
+ } else if (argc == 0 && (mnt_context_get_source(cxt) ||
+ mnt_context_get_target(cxt))) {
+ /*
+ * B) mount -L|-U|--source|--target
+ */
+ if (mnt_context_is_restricted(cxt) &&
+ mnt_context_get_source(cxt) &&
+ mnt_context_get_target(cxt))
+ exit_non_root(NULL);
+
+ } else if (argc == 1) {
+ /*
+ * C) mount [-L|-U|--source] <target>
+ * mount <source|target>
+ *
+ * non-root may specify source *or* target, but not both
+ */
+ if (mnt_context_is_restricted(cxt) &&
+ mnt_context_get_source(cxt))
+ exit_non_root(NULL);
+
+ mnt_context_set_target(cxt, argv[0]);
+
+ } else if (argc == 2 && !mnt_context_get_source(cxt)
+ && !mnt_context_get_target(cxt)) {
+ /*
+ * D) mount <source> <target>
+ */
+ if (mnt_context_is_restricted(cxt))
+ exit_non_root(NULL);
+ mnt_context_set_source(cxt, argv[0]);
+ mnt_context_set_target(cxt, argv[1]);
+
+ } else
+ usage(stderr);
+
+ if (oper) {
+ /* MS_PROPAGATION operations, let's set the mount flags */
+ mnt_context_set_mflags(cxt, oper);
+
+ /* For -make* or --bind is fstab unnecessary */
+ mnt_context_set_optsmode(cxt, MNT_OMODE_NOTAB);
+ }
+
+ rc = mnt_context_mount(cxt);
+ rc = mk_exit_code(cxt, rc);
+
+done:
+ mnt_free_context(cxt);
+ mnt_free_table(fstab);
+ return rc;
+}
+
diff --git a/sys-utils/mountpoint.1 b/sys-utils/mountpoint.1
new file mode 100644
index 0000000..3c7d084
--- /dev/null
+++ b/sys-utils/mountpoint.1
@@ -0,0 +1,43 @@
+.\" -*- nroff -*-
+.TH MOUNTPOINT 1 "June 2011" "util-linux" "User Commands"
+.SH NAME
+mountpoint \- see if a directory is a mountpoint
+.SH SYNOPSIS
+.B mountpoint
+.RB [ \-q ]
+.RB [ \-d ]
+.I directory
+
+.B mountpoint
+.RB \-x
+.I device
+
+.SH DESCRIPTION
+.B mountpoint
+checks if the directory is mentioned in the /proc/self/mountinfo file.
+.SH OPTIONS
+.IP "\fB\-h, \-\-help\fP"
+Print help and exit.
+.IP "\fB\-q, \-\-quiet\fP"
+Be quiet - don't print anything.
+.IP "\fB\-d, \-\-fs\-devno\fP"
+Print major/minor device number of the filesystem on stdout.
+.IP "\fB\-x, \-\-devno\fP"
+Print major/minor device number of the blockdevice on stdout.
+.SH EXIT STATUS
+Zero if the directory is a mountpoint, non-zero if not.
+.SH AUTHOR
+.PP
+Karel Zak <kzak@redhat.com>
+.SH NOTES
+.PP
+The util-linux
+.B mountpoint
+implementation was written from scratch for libmount. The original version
+for sysvinit suite was written by Miquel van Smoorenburg.
+.SH SEE ALSO
+.BR mount (8)
+.SH AVAILABILITY
+The mountpoint command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+
diff --git a/sys-utils/mountpoint.c b/sys-utils/mountpoint.c
new file mode 100644
index 0000000..6ab813d
--- /dev/null
+++ b/sys-utils/mountpoint.c
@@ -0,0 +1,212 @@
+/*
+ * mountpoint(1) - see if a directory is a mountpoint
+ *
+ * This is libmount based reimplementation of the mountpoit(1)
+ * from sysvinit project.
+ *
+ *
+ * Copyright (C) 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 <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <libmount.h>
+
+#include "nls.h"
+#include "xalloc.h"
+#include "c.h"
+#include "closestream.h"
+
+static int quiet;
+
+static int dir_to_device(const char *spec, dev_t *dev)
+{
+ struct libmnt_table *tb = mnt_new_table_from_file("/proc/self/mountinfo");
+ struct libmnt_fs *fs;
+ struct libmnt_cache *cache;
+ int rc = -1;
+
+ if (!tb) {
+ /*
+ * Fallback. Traditional way to detect mountpoints. This way
+ * is independent on /proc, but not able to detect bind mounts.
+ */
+ struct stat pst, st;
+ char buf[PATH_MAX], *cn;
+ int len;
+
+ if (stat(spec, &st) != 0)
+ return -1;
+
+ cn = mnt_resolve_path(spec, NULL); /* canonicalize */
+
+ len = snprintf(buf, sizeof(buf), "%s/..", cn ? cn : spec);
+ free(cn);
+
+ if (len < 0 || (size_t) len + 1 > sizeof(buf))
+ return -1;
+ if (stat(buf, &pst) !=0)
+ return -1;
+
+ if ((st.st_dev != pst.st_dev) ||
+ (st.st_dev == pst.st_dev && st.st_ino == pst.st_ino)) {
+ *dev = st.st_dev;
+ return 0;
+ }
+
+ return -1;
+ }
+
+ /* to canonicalize all necessary paths */
+ cache = mnt_new_cache();
+ mnt_table_set_cache(tb, cache);
+
+ fs = mnt_table_find_target(tb, spec, MNT_ITER_BACKWARD);
+ if (fs && mnt_fs_get_target(fs)) {
+ *dev = mnt_fs_get_devno(fs);
+ rc = 0;
+ }
+
+ mnt_free_table(tb);
+ mnt_free_cache(cache);
+ return rc;
+}
+
+static int print_devno(const char *devname, struct stat *st)
+{
+ struct stat stbuf;
+
+ if (!st && stat(devname, &stbuf) == 0)
+ st = &stbuf;
+ if (!st)
+ return -1;
+ if (!S_ISBLK(st->st_mode)) {
+ if (!quiet)
+ warnx(_("%s: not a block device"), devname);
+ return -1;
+ }
+ printf("%u:%u\n", major(st->st_rdev), minor(st->st_rdev));
+ return 0;
+}
+
+static void __attribute__((__noreturn__)) usage(FILE *out)
+{
+ fputs(USAGE_HEADER, out);
+ fprintf(out,
+ _(" %1$s [-qd] /path/to/directory\n"
+ " %1$s -x /dev/device\n"), program_invocation_short_name);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -q, --quiet quiet mode - don't print anything\n"
+ " -d, --fs-devno print maj:min device number of the filesystem\n"
+ " -x, --devno print maj:min device number of the block device\n"), out);
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ fprintf(out, USAGE_MAN_TAIL("mountpoint(1)"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ int c, fs_devno = 0, dev_devno = 0, rc = 0;
+ char *spec;
+ struct stat st;
+
+ static const struct option longopts[] = {
+ { "quiet", 0, 0, 'q' },
+ { "fs-devno", 0, 0, 'd' },
+ { "devno", 0, 0, 'x' },
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'V' },
+ { NULL, 0, 0, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ mnt_init_debug(0);
+
+ while ((c = getopt_long(argc, argv, "qdxhV", longopts, NULL)) != -1) {
+
+ switch(c) {
+ case 'q':
+ quiet = 1;
+ break;
+ case 'd':
+ fs_devno = 1;
+ break;
+ case 'x':
+ dev_devno = 1;
+ break;
+ case 'h':
+ usage(stdout);
+ break;
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ default:
+ usage(stderr);
+ break;
+ }
+ }
+
+ if (optind + 1 != argc)
+ usage(stderr);
+
+ spec = argv[optind++];
+
+ if (stat(spec, &st)) {
+ if (!quiet)
+ err(EXIT_FAILURE, "%s", spec);
+ return EXIT_FAILURE;
+ }
+ if (dev_devno)
+ rc = print_devno(spec, &st);
+ else {
+ dev_t src;
+
+ if (!S_ISDIR(st.st_mode)) {
+ if (!quiet)
+ errx(EXIT_FAILURE, _("%s: not a directory"), spec);
+ return EXIT_FAILURE;
+ }
+
+ if ( dir_to_device(spec, &src)) {
+ if (!quiet)
+ printf(_("%s is not a mountpoint\n"), spec);
+ return EXIT_FAILURE;
+ }
+ if (fs_devno)
+ printf("%u:%u\n", major(src), minor(src));
+ else if (!quiet)
+ printf(_("%s is a mountpoint\n"), spec);
+ }
+
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/sys-utils/pivot_root.8 b/sys-utils/pivot_root.8
new file mode 100644
index 0000000..e260972
--- /dev/null
+++ b/sys-utils/pivot_root.8
@@ -0,0 +1,75 @@
+.TH PIVOT_ROOT 8 "August 2011" "util-linux" "System Administration"
+.SH NAME
+pivot_root \- change the root filesystem
+.SH SYNOPSIS
+.B pivot_root
+.I new_root put_old
+.SH DESCRIPTION
+\fBpivot_root\fP moves the root file system of the current process to the
+directory \fIput_old\fP and makes \fInew_root\fP the new root file system.
+Since \fBpivot_root\fP(8) simply calls \fBpivot_root\fP(2), we refer to
+the man page of the latter for further details.
+
+Note that, depending on the implementation of \fBpivot_root\fP, root and
+cwd of the caller may or may not change. The following is a sequence for
+invoking \fBpivot_root\fP that works in either case, assuming that
+\fBpivot_root\fP and \fBchroot\fP are in the current \fBPATH\fP:
+.sp
+cd \fInew_root\fP
+.br
+pivot_root . \fIput_old\fP
+.br
+exec chroot . \fIcommand\fP
+.sp
+Note that \fBchroot\fP must be available under the old root and under the new
+root, because \fBpivot_root\fP may or may not have implicitly changed the
+root directory of the shell.
+
+Note that \fBexec chroot\fP changes the running executable, which is
+necessary if the old root directory should be unmounted afterwards.
+Also note that standard input, output, and error may still point to a
+device on the old root file system, keeping it busy. They can easily be
+changed when invoking \fBchroot\fP (see below; note the absence of
+leading slashes to make it work whether \fBpivot_root\fP has changed the
+shell's root or not).
+.SH OPTIONS
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Output version information and exit.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display help and exit.
+.SH EXAMPLES
+Change the root file system to /dev/hda1 from an interactive shell:
+.sp
+.nf
+mount /dev/hda1 /new-root
+cd /new-root
+pivot_root . old-root
+exec chroot . sh <dev/console >dev/console 2>&1
+umount /old-root
+.fi
+.sp
+Mount the new root file system over NFS from 10.0.0.1:/my_root and run
+\fBinit\fP:
+.sp
+.nf
+ifconfig lo 127.0.0.1 up # for portmap
+# configure Ethernet or such
+portmap # for lockd (implicitly started by mount)
+mount -o ro 10.0.0.1:/my_root /mnt
+killall portmap # portmap keeps old root busy
+cd /mnt
+pivot_root . old_root
+exec chroot . sh -c 'umount /old_root; exec /sbin/init' \\
+ <dev/console >dev/console 2>&1
+.fi
+.SH "SEE ALSO"
+.BR chroot (1),
+.BR mount (8),
+.BR pivot_root (2),
+.BR switch_root (8),
+.BR umount (8)
+.SH AVAILABILITY
+The pivot_root command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/sys-utils/pivot_root.c b/sys-utils/pivot_root.c
new file mode 100644
index 0000000..31ceabb
--- /dev/null
+++ b/sys-utils/pivot_root.c
@@ -0,0 +1,75 @@
+/*
+ * pivot_root.c - Change the root file system
+ *
+ * Copyright (C) 2000 Werner Almesberger
+ *
+ * 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.
+ */
+#include <err.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "nls.h"
+#include "closestream.h"
+
+#define pivot_root(new_root,put_old) syscall(SYS_pivot_root,new_root,put_old)
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fprintf(out, USAGE_HEADER);
+ fprintf(out, _(" %s [options] new_root put_old\n"),
+ program_invocation_short_name);
+ fprintf(out, USAGE_OPTIONS);
+ fprintf(out, USAGE_HELP);
+ fprintf(out, USAGE_VERSION);
+ fprintf(out, USAGE_MAN_TAIL("pivot_root(8)"));
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ int ch;
+ static const struct option longopts[] = {
+ {"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 ((ch = getopt_long(argc, argv, "Vh", longopts, NULL)) != -1)
+ switch (ch) {
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+
+ if (argc != 3)
+ usage(stderr);
+
+ if (pivot_root(argv[1], argv[2]) < 0)
+ err(EXIT_FAILURE, _("failed to change root from `%s' to `%s'"),
+ argv[1], argv[2]);
+
+ return EXIT_SUCCESS;
+}
diff --git a/sys-utils/prlimit.1 b/sys-utils/prlimit.1
new file mode 100644
index 0000000..2623199
--- /dev/null
+++ b/sys-utils/prlimit.1
@@ -0,0 +1,124 @@
+.\" prlimit.1 --
+.\" Copyright 2011 Davidlohr Bueso <dave@gnu.org>
+.\" May be distributed under the GNU General Public License
+
+.TH PRLIMIT 1 "October 2011" "util-linux" "User Commands"
+.SH NAME
+prlimit \-
+get and set a process resource limits.
+.SH SYNOPSIS
+.B prlimit
+.RB [options]
+.RB [ \-\-{resource_name}[=limits]
+.RB [ \-\-pid\ PID]
+
+.B prlimit
+.RB [options]
+.RB [ \-\-{resource_name}[=limits]]
+.RB "command " [ argument ...]
+
+.SH DESCRIPTION
+Given a process id and one or more resources, \fBprlimit\fP tries to retrieve
+and/or modify the limits.
+
+When \fIcommand\fR is given,
+.B prlimit
+will run this command with the given arguments.
+
+The \fIlimits\fP format is composed by a soft and a hard (ceiling) value, separated
+by a semicolon (:), in order to modify the existing value(s). If no limits are
+used, \fBprlimit\fP will only display the current values. If one of the values
+is not used, then the existing one will be used. To specify the unlimited or
+infinity limit (RLIM_INFINITY), the -1 or 'unlimited' string can be passed.
+
+Because of the nature of limits, the soft value must be lower or equal to the
+high limit. To see all the available resource limits, refer to the RESOURCE
+OPTIONS section.
+
+.IP "\fB<soft>:<hard>\fP Specify both limits"
+.IP "\fB<soft>:\fP Specify only the soft limit"
+.IP "\fB:<hard>\fP Specify only the hard limit"
+.IP "\fB<value>\fP Specify both soft and hard limits to the same value"
+
+.SH GENERAL OPTIONS
+.IP "\fB\-p, \-\-pid\fP"
+Specify the process id, if none is given, it will use the running process.
+.IP "\fB\-o, \-\-output \fIlist\fP"
+Define the output columns to use. If no output arrangement is specified, then a default set is used.
+Use \fB\-\-help\fP to get list of all supported columns.
+.IP "\fB\-V, \-\-version\fP"
+Output version information and exit.
+.IP "\fB\-\-verbose\fP"
+Verbose mode.
+.IP "\fB\-\-raw\fP"
+Use the raw output format.
+.IP "\fB\-\-noheadings\fP"
+Do not print a header line.
+.IP "\fB\-h, \-\-help\fP"
+Print a help text and exit.
+
+.SH RESOURCE OPTIONS
+.IP "\fB\-c, \-\-core\fP[=limits]"
+Maximum size of a core file.
+.IP "\fB\-d, \-\-data\fP[=limits]"
+Maximum data size.
+.IP "\fB\-e, \-\-nice\fP[=limits]"
+Maximum nice priority allowed to raise.
+.IP "\fB\-f, \-\-fsize\fP[=limits]"
+Maximum file size.
+.IP "\fB\-i, \-\-sigpending\fP[=limits]"
+Maximum number of pending signals.
+.IP "\fB\-l, \-\-memlock\fP[=limits]"
+Maximum locked-in-memory address space.
+.IP "\fB\-m, \-\-rss\fP[=limits]"
+Maximum Resident Set Size (RSS).
+.IP "\fB\-n, \-\-nofile\fP[=limits]"
+Maximum number of open files.
+.IP "\fB\-q, \-\-msgqueue\fP[=limits]"
+Maximum number of bytes in POSIX message queues.
+.IP "\fB\-r, \-\-rtprio\fP[=limits]"
+Maximum real-time priority.
+.IP "\fB\-s, \-\-stack\fP[=limits]"
+Maximum size of the stack.
+.IP "\fB\-t, \-\-cpu\fP[=limits]"
+CPU time, in seconds.
+.IP "\fB\-u, \-\-nproc\fP[=limits]"
+Maximum number of processes.
+.IP "\fB\-v, \-\-as\fP[=limits]"
+Address space limit.
+.IP "\fB\-x, \-\-locks\fP[=limits]"
+Maximum number of file locks held.
+.IP "\fB\-y, \-\-rttime\fP[=limits]"
+Timeout for real-time tasks.
+
+.RE
+.SH EXAMPLES
+.IP "\fBprlimit \-\-pid 13134\fP"
+Display limit values for all current resources.
+.IP "\fBprlimit \-\-pid 13134 \--rss --nofile=1024:4095\fP"
+Display the limits of the RSS and set the soft and hard limits for the number
+of open files to 1024 and 4095, respectively.
+.IP "\fBprlimit \-\-pid 13134 --nproc=512:\fP"
+Modify only the soft limit for the number of processes.
+.IP "\fBprlimit \-\-pid $$ --nproc=unlimited\fP"
+Set the number of processes for both soft and ceiling values to unlimited.
+.IP "\fBprlimit --cpu=10 sort -u hugefile\fP"
+Set the soft and hard CPU time limit and run 'sort'.
+
+.SH "SEE ALSO"
+.BR prlimit (2),
+.BR ulimit (1)
+
+.SH NOTES
+.nf
+The prlimit system call is supported since Linux 2.6.36, previous kernels will
+break this program.
+.fi
+
+.SH AUTHORS
+.nf
+Davidlohr Bueso <dave@gnu.org> - In memory of Dennis M. Ritchie.
+.fi
+.SH AVAILABILITY
+The prlimit command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/sys-utils/prlimit.c b/sys-utils/prlimit.c
new file mode 100644
index 0000000..2055adf
--- /dev/null
+++ b/sys-utils/prlimit.c
@@ -0,0 +1,643 @@
+/*
+ * prlimit - get/set process resource limits.
+ *
+ * Copyright (C) 2011 Davidlohr Bueso <dave@gnu.org>
+ *
+ * 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.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/resource.h>
+
+#include "c.h"
+#include "nls.h"
+#include "tt.h"
+#include "xalloc.h"
+#include "strutils.h"
+#include "list.h"
+#include "closestream.h"
+
+#ifndef RLIMIT_RTTIME
+# define RLIMIT_RTTIME 15
+#endif
+
+enum {
+ AS,
+ CORE,
+ CPU,
+ DATA,
+ FSIZE,
+ LOCKS,
+ MEMLOCK,
+ MSGQUEUE,
+ NICE,
+ NOFILE,
+ NPROC,
+ RSS,
+ RTPRIO,
+ RTTIME,
+ SIGPENDING,
+ STACK
+};
+
+struct prlimit_desc {
+ const char *name;
+ const char *help;
+ const char *unit;
+ int resource;
+};
+
+static struct prlimit_desc prlimit_desc[] =
+{
+ [AS] = { "AS", N_("address space limit"), N_("bytes"), RLIMIT_AS },
+ [CORE] = { "CORE", N_("max core file size"), N_("blocks"), RLIMIT_CORE },
+ [CPU] = { "CPU", N_("CPU time"), N_("seconds"), RLIMIT_CPU },
+ [DATA] = { "DATA", N_("max data size"), N_("bytes"), RLIMIT_DATA },
+ [FSIZE] = { "FSIZE", N_("max file size"), N_("blocks"), RLIMIT_FSIZE },
+ [LOCKS] = { "LOCKS", N_("max number of file locks held"), NULL, RLIMIT_LOCKS },
+ [MEMLOCK] = { "MEMLOCK", N_("max locked-in-memory address space"), N_("bytes"), RLIMIT_MEMLOCK },
+ [MSGQUEUE] = { "MSGQUEUE", N_("max bytes in POSIX mqueues"), N_("bytes"), RLIMIT_MSGQUEUE },
+ [NICE] = { "NICE", N_("max nice prio allowed to raise"), NULL, RLIMIT_NICE },
+ [NOFILE] = { "NOFILE", N_("max number of open files"), NULL, RLIMIT_NOFILE },
+ [NPROC] = { "NPROC", N_("max number of processes"), NULL, RLIMIT_NPROC },
+ [RSS] = { "RSS", N_("max resident set size"), N_("pages"), RLIMIT_RSS },
+ [RTPRIO] = { "RTPRIO", N_("max real-time priority"), NULL, RLIMIT_RTPRIO },
+ [RTTIME] = { "RTTIME", N_("timeout for real-time tasks"), N_("microsecs"), RLIMIT_RTTIME },
+ [SIGPENDING] = { "SIGPENDING", N_("max number of pending signals"), NULL, RLIMIT_SIGPENDING },
+ [STACK] = { "STACK", N_("max stack size"), N_("bytes"), RLIMIT_STACK }
+};
+
+struct prlimit {
+ struct list_head lims;
+
+ struct rlimit rlim;
+ struct prlimit_desc *desc;
+ int modify; /* PRLIMIT_{SOFT,HARD} mask */
+};
+
+#define PRLIMIT_EMPTY_LIMIT {{ 0, 0, }, NULL, 0 }
+
+enum {
+ COL_HELP,
+ COL_RES,
+ COL_SOFT,
+ COL_HARD,
+ COL_UNITS,
+};
+
+/* 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 */
+struct colinfo infos[] = {
+ [COL_RES] = { "RESOURCE", 0.25, TT_FL_TRUNC, N_("resource name") },
+ [COL_HELP] = { "DESCRIPTION", 0.1, TT_FL_TRUNC, N_("resource description")},
+ [COL_SOFT] = { "SOFT", 0.1, TT_FL_RIGHT, N_("soft limit")},
+ [COL_HARD] = { "HARD", 1, TT_FL_RIGHT, N_("hard limit (ceiling)")},
+ [COL_UNITS] = { "UNITS", 0.1, TT_FL_TRUNC, N_("units")},
+};
+
+#define NCOLS ARRAY_SIZE(infos)
+#define MAX_RESOURCES ARRAY_SIZE(prlimit_desc)
+
+#define INFINITY_STR "unlimited"
+#define INFINITY_STRLEN (sizeof(INFINITY_STR) - 1)
+
+#define PRLIMIT_SOFT (1 << 1)
+#define PRLIMIT_HARD (1 << 2)
+
+/* array with IDs of enabled columns */
+static int columns[NCOLS], ncolumns;
+static pid_t pid; /* calling process (default) */
+static int verbose;
+
+#ifndef HAVE_PRLIMIT
+# include <sys/syscall.h>
+static int prlimit(pid_t p, int resource,
+ const struct rlimit *new_limit,
+ struct rlimit *old_limit)
+{
+ return syscall(SYS_prlimit64, p, resource, new_limit, old_limit);
+}
+#endif
+
+static void rem_prlim(struct prlimit *lim);
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ size_t i;
+
+ fputs(USAGE_HEADER, out);
+
+ fprintf(out,
+ _(" %s [options] [-p PID]\n"), program_invocation_short_name);
+ fprintf(out,
+ _(" %s [options] COMMAND\n"), program_invocation_short_name);
+
+ fputs(_("\nGeneral Options:\n"), out);
+ fputs(_(" -p, --pid <pid> process id\n"
+ " -o, --output <list> define which output columns to use\n"
+ " --noheadings don't print headings\n"
+ " --raw use the raw output format\n"
+ " --verbose verbose output\n"
+ " -h, --help display this help and exit\n"
+ " -V, --version output version information and exit\n"), out);
+
+ fputs(_("\nResources Options:\n"), out);
+ fputs(_(" -c, --core maximum size of core files created\n"
+ " -d, --data maximum size of a process's data segment\n"
+ " -e, --nice maximum nice priority allowed to raise\n"
+ " -f, --fsize maximum size of files written by the process\n"
+ " -i, --sigpending maximum number of pending signals\n"
+ " -l, --memlock maximum size a process may lock into memory\n"
+ " -m, --rss maximum resident set size\n"
+ " -n, --nofile maximum number of open files\n"
+ " -q, --msgqueue maximum bytes in POSIX message queues\n"
+ " -r, --rtprio maximum real-time scheduling priority\n"
+ " -s, --stack maximum stack size\n"
+ " -t, --cpu maximum amount of CPU time in seconds\n"
+ " -u, --nproc maximum number of user processes\n"
+ " -v, --as size of virtual memory\n"
+ " -x, --locks maximum number of file locks\n"
+ " -y, --rttime CPU time in microseconds a process scheduled\n"
+ " under real-time scheduling\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("prlimit(1)"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+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 add_tt_line(struct tt *tt, struct prlimit *l)
+{
+ int i;
+ struct tt_line *line;
+
+ assert(tt);
+ assert(l);
+
+ 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_RES:
+ rc = xasprintf(&str, "%s", l->desc->name);
+ break;
+ case COL_HELP:
+ rc = xasprintf(&str, "%s", l->desc->help);
+ break;
+ case COL_SOFT:
+ rc = l->rlim.rlim_cur == RLIM_INFINITY ?
+ xasprintf(&str, "%s", "unlimited") :
+ xasprintf(&str, "%llu", (unsigned long long) l->rlim.rlim_cur);
+ break;
+ case COL_HARD:
+ rc = l->rlim.rlim_max == RLIM_INFINITY ?
+ xasprintf(&str, "%s", "unlimited") :
+ xasprintf(&str, "%llu", (unsigned long long) l->rlim.rlim_max);
+ break;
+ case COL_UNITS:
+ str = l->desc->unit ? xstrdup(_(l->desc->unit)) : NULL;
+ break;
+ default:
+ break;
+ }
+
+ if (rc || str)
+ tt_line_set_data(line, i, str);
+ }
+}
+
+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 int show_limits(struct list_head *lims, int tt_flags)
+{
+ int i;
+ 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"));
+ goto done;
+ }
+ }
+
+
+ list_for_each_safe(p, pnext, lims) {
+ struct prlimit *lim = list_entry(p, struct prlimit, lims);
+
+ add_tt_line(tt, lim);
+ rem_prlim(lim);
+ }
+
+ tt_print_table(tt);
+done:
+ tt_free_table(tt);
+ return 0;
+}
+
+/*
+ * If one of the limits is unknown (default value for not being passed), we
+ * need to get the current limit and use it. I see no other way other than
+ * using prlimit(2).
+ */
+static void get_unknown_hardsoft(struct prlimit *lim)
+{
+ struct rlimit old;
+
+ if (prlimit(pid, lim->desc->resource, NULL, &old) == -1)
+ err(EXIT_FAILURE, _("failed to get old %s limit"),
+ lim->desc->name);
+
+ if (!(lim->modify & PRLIMIT_SOFT))
+ lim->rlim.rlim_cur = old.rlim_cur;
+ else if (!(lim->modify & PRLIMIT_HARD))
+ lim->rlim.rlim_max = old.rlim_max;
+}
+
+static void do_prlimit(struct list_head *lims)
+{
+ struct list_head *p, *pnext;
+
+ list_for_each_safe(p, pnext, lims) {
+ struct rlimit *new = NULL, *old = NULL;
+ struct prlimit *lim = list_entry(p, struct prlimit, lims);
+
+ if (lim->modify) {
+ if (lim->modify != (PRLIMIT_HARD | PRLIMIT_SOFT))
+ get_unknown_hardsoft(lim);
+
+ if ((lim->rlim.rlim_cur > lim->rlim.rlim_max) &&
+ (lim->rlim.rlim_cur != RLIM_INFINITY ||
+ lim->rlim.rlim_max != RLIM_INFINITY))
+ errx(EXIT_FAILURE, _("the soft limit %s cannot exceed the hard limit"),
+ lim->desc->name);
+ new = &lim->rlim;
+ } else
+ old = &lim->rlim;
+
+ if (verbose && new) {
+ printf(_("New %s limit: "), lim->desc->name);
+ if (new->rlim_cur == RLIM_INFINITY)
+ printf("<%s", _("unlimited"));
+ else
+ printf("<%ju", new->rlim_cur);
+
+ if (new->rlim_max == RLIM_INFINITY)
+ printf(":%s>\n", _("unlimited"));
+ else
+ printf(":%ju>\n", new->rlim_max);
+ }
+
+ if (prlimit(pid, lim->desc->resource, new, old) == -1)
+ err(EXIT_FAILURE, lim->modify ?
+ _("failed to set the %s resource limit") :
+ _("failed to get the %s resource limit"),
+ lim->desc->name);
+
+ if (lim->modify)
+ rem_prlim(lim); /* modify only; don't show */
+ }
+}
+
+
+
+static int get_range(char *str, rlim_t *soft, rlim_t *hard, int *found)
+{
+ char *end = NULL;
+
+ if (!str)
+ return 0;
+
+ *found = errno = 0;
+ *soft = *hard = RLIM_INFINITY;
+
+ if (!strcmp(str, INFINITY_STR)) { /* <unlimited> */
+ *found |= PRLIMIT_SOFT | PRLIMIT_HARD;
+ return 0;
+
+ } else if (*str == ':') { /* <:hard> */
+ str++;
+
+ if (strcmp(str, INFINITY_STR) != 0) {
+ *hard = strtoull(str, &end, 10);
+
+ if (errno || !end || *end || end == str)
+ return -1;
+ }
+ *found |= PRLIMIT_HARD;
+ return 0;
+
+ }
+
+ if (strncmp(str, INFINITY_STR, INFINITY_STRLEN) == 0) {
+ /* <unlimited> or <unlimited:> */
+ end = str + INFINITY_STRLEN;
+ } else {
+ /* <value> or <soft:> */
+ *hard = *soft = strtoull(str, &end, 10);
+ if (errno || !end || end == str)
+ return -1;
+ }
+
+ if (*end == ':' && !*(end + 1)) /* <soft:> */
+ *found |= PRLIMIT_SOFT;
+
+ else if (*end == ':') { /* <soft:hard> */
+ str = end + 1;
+
+ if (!strcmp(str, INFINITY_STR))
+ *hard = RLIM_INFINITY;
+ else {
+ end = NULL;
+ errno = 0;
+ *hard = strtoull(str, &end, 10);
+
+ if (errno || !end || *end || end == str)
+ return -1;
+ }
+ *found |= PRLIMIT_SOFT | PRLIMIT_HARD;
+
+ } else /* <value> */
+ *found |= PRLIMIT_SOFT | PRLIMIT_HARD;
+
+ return 0;
+}
+
+
+static int parse_prlim(struct rlimit *lim, char *ops, size_t id)
+{
+ rlim_t soft, hard;
+ int found = 0;
+
+ if (get_range(ops, &soft, &hard, &found))
+ errx(EXIT_FAILURE, _("failed to parse %s limit"),
+ prlimit_desc[id].name);
+
+ lim->rlim_cur = soft;
+ lim->rlim_max = hard;
+
+ return found;
+}
+
+static int add_prlim(char *ops, struct list_head *lims, size_t id)
+{
+ struct prlimit *lim = xcalloc(1, sizeof(*lim));
+
+ INIT_LIST_HEAD(&lim->lims);
+ lim->desc = &prlimit_desc[id];
+
+ if (ops)
+ lim->modify = parse_prlim(&lim->rlim, ops, id);
+
+ list_add_tail(&lim->lims, lims);
+ return 0;
+}
+
+static void rem_prlim(struct prlimit *lim)
+{
+ if (!lim)
+ return;
+ list_del(&lim->lims);
+ free(lim);
+}
+
+int main(int argc, char **argv)
+{
+ int opt, tt_flags = 0;
+ struct list_head lims;
+
+ enum {
+ VERBOSE_OPTION = CHAR_MAX + 1,
+ RAW_OPTION,
+ NOHEADINGS_OPTION
+ };
+
+ static const struct option longopts[] = {
+ { "pid", required_argument, NULL, 'p' },
+ { "output", required_argument, NULL, 'o' },
+ { "as", optional_argument, NULL, 'v' },
+ { "core", optional_argument, NULL, 'c' },
+ { "cpu", optional_argument, NULL, 't' },
+ { "data", optional_argument, NULL, 'd' },
+ { "fsize", optional_argument, NULL, 'f' },
+ { "locks", optional_argument, NULL, 'x' },
+ { "memlock", optional_argument, NULL, 'l' },
+ { "msgqueue", optional_argument, NULL, 'q' },
+ { "nice", optional_argument, NULL, 'e' },
+ { "nofile", optional_argument, NULL, 'n' },
+ { "nproc", optional_argument, NULL, 'u' },
+ { "rss", optional_argument, NULL, 'm' },
+ { "rtprio", optional_argument, NULL, 'r' },
+ { "rttime", optional_argument, NULL, 'y' },
+ { "sigpending", optional_argument, NULL, 'i' },
+ { "stack", optional_argument, NULL, 's' },
+ { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'h' },
+ { "noheadings", no_argument, NULL, NOHEADINGS_OPTION },
+ { "raw", no_argument, NULL, RAW_OPTION },
+ { "verbose", no_argument, NULL, VERBOSE_OPTION },
+ { NULL, 0, NULL, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ INIT_LIST_HEAD(&lims);
+
+ /*
+ * Something is very wrong if this doesn't succeed,
+ * assuming STACK is the last resource, of course.
+ */
+ assert(MAX_RESOURCES == STACK + 1);
+
+ while((opt = getopt_long(argc, argv,
+ "+c::d::e::f::i::l::m::n::q::r::s::t::u::v::x::y::p:o:vVh",
+ longopts, NULL)) != -1) {
+ switch(opt) {
+ case 'c':
+ add_prlim(optarg, &lims, CORE);
+ break;
+ case 'd':
+ add_prlim(optarg, &lims, DATA);
+ break;
+ case 'e':
+ add_prlim(optarg, &lims, NICE);
+ break;
+ case 'f':
+ add_prlim(optarg, &lims, FSIZE);
+ break;
+ case 'i':
+ add_prlim(optarg, &lims, SIGPENDING);
+ break;
+ case 'l':
+ add_prlim(optarg, &lims, MEMLOCK);
+ break;
+ case 'm':
+ add_prlim(optarg, &lims, RSS);
+ break;
+ case 'n':
+ add_prlim(optarg, &lims, NOFILE);
+ break;
+ case 'q':
+ add_prlim(optarg, &lims, MSGQUEUE);
+ break;
+ case 'r':
+ add_prlim(optarg, &lims, RTPRIO);
+ break;
+ case 's':
+ add_prlim(optarg, &lims, STACK);
+ break;
+ case 't':
+ add_prlim(optarg, &lims, CPU);
+ break;
+ case 'u':
+ add_prlim(optarg, &lims, NPROC);
+ break;
+ case 'v':
+ add_prlim(optarg, &lims, AS);
+ break;
+ case 'x':
+ add_prlim(optarg, &lims, LOCKS);
+ break;
+ case 'y':
+ add_prlim(optarg, &lims, RTTIME);
+ break;
+
+ case 'p':
+ if (pid)
+ errx(EXIT_FAILURE, _("option --pid may be specified only once"));
+ pid = strtos32_or_err(optarg, _("invalid PID argument"));
+ break;
+ case 'h':
+ usage(stdout);
+ 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 NOHEADINGS_OPTION:
+ tt_flags |= TT_FL_NOHEADINGS;
+ break;
+ case VERBOSE_OPTION:
+ verbose++;
+ break;
+ case RAW_OPTION:
+ tt_flags |= TT_FL_RAW;
+ break;
+
+ default:
+ usage(stderr);
+ }
+ }
+ if (argc > optind && pid)
+ errx(EXIT_FAILURE, _("options --pid and COMMAND are mutually exclusive"));
+ if (!ncolumns) {
+ /* default columns */
+ columns[ncolumns++] = COL_RES;
+ columns[ncolumns++] = COL_HELP;
+ columns[ncolumns++] = COL_SOFT;
+ columns[ncolumns++] = COL_HARD;
+ columns[ncolumns++] = COL_UNITS;
+ }
+
+ if (list_empty(&lims)) {
+ /* default is to print all resources */
+ size_t n;
+
+ for (n = 0; n < MAX_RESOURCES; n++)
+ add_prlim(NULL, &lims, n);
+ }
+
+ do_prlimit(&lims);
+
+ if (!list_empty(&lims))
+ show_limits(&lims, tt_flags);
+
+ if (argc > optind) {
+ /* prlimit [options] COMMAND */
+ execvp(argv[optind], &argv[optind]);
+ err(EXIT_FAILURE, _("executing %s failed"), argv[optind]);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/sys-utils/readprofile.8 b/sys-utils/readprofile.8
new file mode 100644
index 0000000..308dba4
--- /dev/null
+++ b/sys-utils/readprofile.8
@@ -0,0 +1,152 @@
+.TH READPROFILE "8" "October 2011" "util-linux" "System Administration"
+.SH NAME
+readprofile - read kernel profiling information
+.SH SYNOPSIS
+.B readprofile
+.RI [ options ]
+.SH VERSION
+This manpage documents version 2.0 of the program.
+.SH DESCRIPTION
+.LP
+The
+.B readprofile
+command uses the
+.B /proc/profile
+information to print ascii data on standard output. The output is
+organized in three columns: the first is the number of clock ticks,
+the second is the name of the C function in the kernel where those
+many ticks occurred, and the third is the normalized `load' of the
+procedure, calculated as a ratio between the number of ticks and the
+length of the procedure. The output is filled with blanks to ease
+readability.
+.SH OPTIONS
+.TP
+\fB\-m\fR, \fB\-\-mapfile\fR \fImapfile\fR
+Specify a mapfile, which by default is
+.BR /usr/src/linux/System.map .
+You should specify the map file on cmdline if your current kernel
+isn't the last one you compiled, or if you keep System.map elsewhere.
+If the name of the map file ends with `.gz' it is decompressed on the
+fly.
+.TP
+\fB\-p\fR, \fB\-\-profile\fR \fIpro-file\fR
+Specify a different profiling buffer, which by default is
+.B /proc/profile.
+Using a different pro-file is useful if you want to `freeze' the
+kernel profiling at some time and read it later. The
+.B /proc/profile
+file can be copied using `cat' or `cp'. There is no more support for
+compressed profile buffers, like in
+.B readprofile-1.1,
+because the program needs to know the size of the buffer in advance.
+.TP
+\fB\-i\fR, \fB\-\-info\fR
+Info. This makes
+.B readprofile
+only print the profiling step used by the kernel. The profiling step
+is the resolution of the profiling buffer, and is chosen during
+kernel configuration (through `make config'), or in the kernel's
+command line. If the
+.B \-t
+(terse) switch is used together with
+.B \-i
+only the decimal number is printed.
+.TP
+\fB\-a\fR, \fB\-\-all\fR
+Print all symbols in the mapfile. By default the procedures with
+reported ticks are not printed.
+.TP
+\fB\-b\fR, \fB\-\-histbin\fR
+Print individual histogram-bin counts.
+.TP
+\fB\-r\fR, \fB\-\-reset\fR
+Reset the profiling buffer. This can only be invoked by root,
+because
+.B /proc/profile
+is readable by everybody but writable only by the superuser.
+However, you can make
+.B readprofile
+setuid 0, in order to reset the buffer without gaining privileges.
+.TP
+\fB\-M\fR, \fB\-\-multiplier\fR \fImultiplier\fR
+On some architectures it is possible to alter the frequency at which
+the kernel delivers profiling interrupts to each CPU. This option
+allows you to set the frequency, as a multiplier of the system clock
+frequency, HZ. This is supported on i386-SMP (2.2 and 2.4 kernel)
+and also on sparc-SMP and sparc64-SMP (2.4 kernel). This option also
+resets the profiling buffer, and requires superuser privileges.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+Verbose. The output is organized in four columns and filled with
+blanks. The first column is the RAM address of a kernel function,
+the second is the name of the function, the third is the number of
+clock ticks and the last is the normalized load.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+This makes
+.B readprofile
+print its version number and exit.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display help and exit.
+.SH EXAMPLES
+Browse the profiling buffer ordering by clock ticks:
+.nf
+ readprofile | sort -nr | less
+
+.fi
+Print the 20 most loaded procedures:
+.nf
+ readprofile | sort -nr +2 | head -20
+
+.fi
+Print only filesystem profile:
+.nf
+ readprofile | grep _ext2
+
+.fi
+Look at all the kernel information, with ram addresses:
+.nf
+ readprofile -av | less
+
+.fi
+Browse a `freezed' profile buffer for a non current kernel:
+.nf
+ readprofile -p ~/profile.freeze -m /zImage.map.gz
+
+.fi
+Request profiling at 2kHz per CPU, and reset the profiling buffer:
+.nf
+ sudo readprofile -M 20
+.fi
+.SH BUGS
+.LP
+.B readprofile
+only works with an 1.3.x or newer kernel, because
+.B /proc/profile
+changed in the step from 1.2 to 1.3
+.LP
+This program only works with ELF kernels. The change for a.out
+kernels is trivial, and left as an exercise to the a.out user.
+.LP
+To enable profiling, the kernel must be rebooted, because no
+profiling module is available, and it wouldn't be easy to build. To
+enable profiling, you can specify "profile=2" (or another number) on
+the kernel commandline. The number you specify is the two-exponent
+used as profiling step.
+.LP
+Profiling is disabled when interrupts are inhibited. This means that
+many profiling ticks happen when interrupts are re-enabled. Watch
+out for misleading information.
+.SH FILES
+.nf
+/proc/profile A binary snapshot of the profiling buffer.
+/usr/src/linux/System.map The symbol table for the kernel.
+/usr/src/linux/* The program being profiled :-)
+.fi
+.SH AVAILABILITY
+The readprofile 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/sys-utils/readprofile.c b/sys-utils/readprofile.c
new file mode 100644
index 0000000..04d9c73
--- /dev/null
+++ b/sys-utils/readprofile.c
@@ -0,0 +1,405 @@
+/*
+ * readprofile.c - used to read /proc/profile
+ *
+ * Copyright (C) 1994,1996 Alessandro Rubini (rubini@ipvvis.unipv.it)
+ *
+ * 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.
+ */
+
+/*
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ * 1999-09-01 Stephane Eranian <eranian@cello.hpl.hp.com>
+ * - 64bit clean patch
+ * 3Feb2001 Andrew Morton <andrewm@uow.edu.au>
+ * - -M option to write profile multiplier.
+ * 2001-11-07 Werner Almesberger <wa@almesberger.net>
+ * - byte order auto-detection and -n option
+ * 2001-11-09 Werner Almesberger <wa@almesberger.net>
+ * - skip step size (index 0)
+ * 2002-03-09 John Levon <moz@compsoc.man.ac.uk>
+ * - make maplineno do something
+ * 2002-11-28 Mads Martin Joergensen +
+ * - also try /boot/System.map-`uname -r`
+ * 2003-04-09 Werner Almesberger <wa@almesberger.net>
+ * - fixed off-by eight error and improved heuristics in byte order detection
+ * 2003-08-12 Nikita Danilov <Nikita@Namesys.COM>
+ * - added -s option; example of use:
+ * "readprofile -s -m /boot/System.map-test | grep __d_lookup | sort -n -k3"
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include "nls.h"
+#include "xalloc.h"
+#include "closestream.h"
+
+#define S_LEN 128
+
+/* These are the defaults */
+static char defaultmap[]="/boot/System.map";
+static char defaultpro[]="/proc/profile";
+
+static FILE *myopen(char *name, char *mode, int *flag)
+{
+ int len = strlen(name);
+
+ if (!strcmp(name + len - 3, ".gz")) {
+ FILE *res;
+ char *cmdline = xmalloc(len + 6);
+ sprintf(cmdline, "zcat %s", name);
+ res = popen(cmdline, mode);
+ free(cmdline);
+ *flag = 1;
+ return res;
+ }
+ *flag = 0;
+ return fopen(name, mode);
+}
+
+#ifndef BOOT_SYSTEM_MAP
+#define BOOT_SYSTEM_MAP "/boot/System.map-"
+#endif
+
+static char *boot_uname_r_str(void)
+{
+ struct utsname uname_info;
+ char *s;
+ size_t len;
+
+ if (uname(&uname_info))
+ return "";
+ len = strlen(BOOT_SYSTEM_MAP) + strlen(uname_info.release) + 1;
+ s = xmalloc(len);
+ strcpy(s, BOOT_SYSTEM_MAP);
+ strcat(s, uname_info.release);
+ return s;
+}
+
+static void __attribute__ ((__noreturn__))
+ usage(FILE * out)
+{
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
+ fputs(USAGE_OPTIONS, out);
+
+ fprintf(out,
+ _(" -m, --mapfile <mapfile> (defaults: \"%s\" and\n"), defaultmap);
+ fprintf(out,
+ _(" \"%s\")\n"), boot_uname_r_str());
+ fprintf(out,
+ _(" -p, --profile <pro-file> (default: \"%s\")\n"), defaultpro);
+ fputs(_(" -M, --multiplier <mult> set the profiling multiplier to <mult>\n"), out);
+ fputs(_(" -i, --info print only info about the sampling step\n"), out);
+ fputs(_(" -v, --verbose print verbose data\n"), out);
+ fputs(_(" -a, --all print all symbols, even if count is 0\n"), out);
+ fputs(_(" -b, --histbin print individual histogram-bin counts\n"), out);
+ fputs(_(" -s, --counters print individual counters within functions\n"), out);
+ fputs(_(" -r, --reset reset all the counters (root only)\n"), out);
+ fputs(_(" -n, --no-auto disable byte order auto-detection\n"), out);
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ fprintf(out, USAGE_MAN_TAIL("readprofile(8)"));
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ FILE *map;
+ int proFd;
+ char *mapFile, *proFile, *mult = 0;
+ size_t len = 0, indx = 1;
+ unsigned long long add0 = 0;
+ unsigned int step;
+ unsigned int *buf, total, fn_len;
+ unsigned long long fn_add, next_add; /* current and next address */
+ char fn_name[S_LEN], next_name[S_LEN]; /* current and next name */
+ char mode[8];
+ int c;
+ ssize_t rc;
+ int optAll = 0, optInfo = 0, optReset = 0, optVerbose = 0, optNative = 0;
+ int optBins = 0, optSub = 0;
+ char mapline[S_LEN];
+ int maplineno = 1;
+ int popenMap; /* flag to tell if popen() has been used */
+ int header_printed;
+
+ static const struct option longopts[] = {
+ {"mapfile", required_argument, NULL, 'm'},
+ {"profile", required_argument, NULL, 'p'},
+ {"multiplier", required_argument, NULL, 'M'},
+ {"info", no_argument, NULL, 'i'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"all", no_argument, NULL, 'a'},
+ {"histbin", no_argument, NULL, 'b'},
+ {"counters", no_argument, NULL, 's'},
+ {"reest", no_argument, NULL, 'r'},
+ {"no-auto", no_argument, NULL, 'n'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, 0, 0}
+ };
+
+#define next (current^1)
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ proFile = defaultpro;
+ mapFile = defaultmap;
+
+ while ((c = getopt_long(argc, argv, "m:p:M:ivabsrnVh", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'm':
+ mapFile = optarg;
+ break;
+ case 'n':
+ optNative++;
+ break;
+ case 'p':
+ proFile = optarg;
+ break;
+ case 'a':
+ optAll++;
+ break;
+ case 'b':
+ optBins++;
+ break;
+ case 's':
+ optSub++;
+ break;
+ case 'i':
+ optInfo++;
+ break;
+ case 'M':
+ mult = optarg;
+ break;
+ case 'r':
+ optReset++;
+ break;
+ case 'v':
+ optVerbose++;
+ break;
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+ }
+
+ if (optReset || mult) {
+ int multiplier, fd, to_write;
+
+ /* When writing the multiplier, if the length of the
+ * write is not sizeof(int), the multiplier is not
+ * changed. */
+ if (mult) {
+ multiplier = strtoul(mult, 0, 10);
+ to_write = sizeof(int);
+ } else {
+ multiplier = 0;
+ /* sth different from sizeof(int) */
+ to_write = 1;
+ }
+ /* try to become root, just in case */
+ setuid(0);
+ fd = open(defaultpro, O_WRONLY);
+ if (fd < 0)
+ err(EXIT_FAILURE, "%s", defaultpro);
+ if (write(fd, &multiplier, to_write) != to_write)
+ err(EXIT_FAILURE, _("error writing %s"), defaultpro);
+ close(fd);
+ exit(EXIT_SUCCESS);
+ }
+
+ /* Use an fd for the profiling buffer, to skip stdio overhead */
+ if (((proFd = open(proFile, O_RDONLY)) < 0)
+ || ((int)(len = lseek(proFd, 0, SEEK_END)) < 0)
+ || (lseek(proFd, 0, SEEK_SET) < 0))
+ err(EXIT_FAILURE, "%s", proFile);
+
+ buf = xmalloc(len);
+
+ rc = read(proFd, buf, len);
+ if (rc < 0 || (size_t) rc != len)
+ err(EXIT_FAILURE, "%s", proFile);
+ close(proFd);
+
+ if (!optNative) {
+ int entries = len / sizeof(*buf);
+ int big = 0, small = 0;
+ unsigned *p;
+ size_t i;
+
+ for (p = buf + 1; p < buf + entries; p++) {
+ if (*p & ~0U << (sizeof(*buf) * 4))
+ big++;
+ if (*p & ((1 << (sizeof(*buf) * 4)) - 1))
+ small++;
+ }
+ if (big > small) {
+ warnx(_("Assuming reversed byte order. "
+ "Use -n to force native byte order."));
+ for (p = buf; p < buf + entries; p++)
+ for (i = 0; i < sizeof(*buf) / 2; i++) {
+ unsigned char *b = (unsigned char *)p;
+ unsigned char tmp;
+ tmp = b[i];
+ b[i] = b[sizeof(*buf) - i - 1];
+ b[sizeof(*buf) - i - 1] = tmp;
+ }
+ }
+ }
+
+ step = buf[0];
+ if (optInfo) {
+ printf(_("Sampling_step: %i\n"), step);
+ exit(EXIT_SUCCESS);
+ }
+
+ total = 0;
+
+ map = myopen(mapFile, "r", &popenMap);
+ if (map == NULL && mapFile == defaultmap) {
+ mapFile = boot_uname_r_str();
+ map = myopen(mapFile, "r", &popenMap);
+ }
+ if (map == NULL)
+ err(EXIT_FAILURE, "%s", mapFile);
+
+ while (fgets(mapline, S_LEN, map)) {
+ if (sscanf(mapline, "%llx %s %s", &fn_add, mode, fn_name) != 3)
+ errx(EXIT_FAILURE, _("%s(%i): wrong map line"), mapFile,
+ maplineno);
+ /* only elf works like this */
+ if (!strcmp(fn_name, "_stext") || !strcmp(fn_name, "__stext")) {
+ add0 = fn_add;
+ break;
+ }
+ maplineno++;
+ }
+
+ if (!add0)
+ errx(EXIT_FAILURE, _("can't find \"_stext\" in %s"), mapFile);
+
+ /*
+ * Main loop.
+ */
+ while (fgets(mapline, S_LEN, map)) {
+ unsigned int this = 0;
+ int done = 0;
+
+ if (sscanf(mapline, "%llx %s %s", &next_add, mode, next_name) != 3)
+ errx(EXIT_FAILURE, _("%s(%i): wrong map line"), mapFile,
+ maplineno);
+ header_printed = 0;
+
+ /* the kernel only profiles up to _etext */
+ if (!strcmp(next_name, "_etext") ||
+ !strcmp(next_name, "__etext"))
+ done = 1;
+ else {
+ /* ignore any LEADING (before a '[tT]' symbol
+ * is found) Absolute symbols and __init_end
+ * because some architectures place it before
+ * .text section */
+ if ((*mode == 'A' || *mode == '?')
+ && (total == 0 || !strcmp(next_name, "__init_end")))
+ continue;
+ if (*mode != 'T' && *mode != 't' &&
+ *mode != 'W' && *mode != 'w')
+ break; /* only text is profiled */
+ }
+
+ if (indx >= len / sizeof(*buf))
+ errx(EXIT_FAILURE,
+ _("profile address out of range. Wrong map file?"));
+
+ while (indx < (next_add - add0) / step) {
+ if (optBins && (buf[indx] || optAll)) {
+ if (!header_printed) {
+ printf("%s:\n", fn_name);
+ header_printed = 1;
+ }
+ printf("\t%llx\t%u\n", (indx - 1) * step + add0,
+ buf[indx]);
+ }
+ this += buf[indx++];
+ }
+ total += this;
+
+ if (optBins) {
+ if (optVerbose || this > 0)
+ printf(" total\t\t\t\t%u\n", this);
+ } else if ((this || optAll) &&
+ (fn_len = next_add - fn_add) != 0) {
+ if (optVerbose)
+ printf("%016llx %-40s %6i %8.4f\n", fn_add,
+ fn_name, this, this / (double)fn_len);
+ else
+ printf("%6i %-40s %8.4f\n",
+ this, fn_name, this / (double)fn_len);
+ if (optSub) {
+ unsigned long long scan;
+
+ for (scan = (fn_add - add0) / step + 1;
+ scan < (next_add - add0) / step;
+ scan++) {
+ unsigned long long addr;
+ addr = (scan - 1) * step + add0;
+ printf("\t%#llx\t%s+%#llx\t%u\n",
+ addr, fn_name, addr - fn_add,
+ buf[scan]);
+ }
+ }
+ }
+
+ fn_add = next_add;
+ strcpy(fn_name, next_name);
+
+ maplineno++;
+ if (done)
+ break;
+ }
+
+ /* clock ticks, out of kernel text - probably modules */
+ printf("%6i %s\n", buf[len / sizeof(*buf) - 1], "*unknown*");
+
+ /* trailer */
+ if (optVerbose)
+ printf("%016x %-40s %6i %8.4f\n",
+ 0, "total", total, total / (double)(fn_add - add0));
+ else
+ printf("%6i %-40s %8.4f\n",
+ total, _("total"), total / (double)(fn_add - add0));
+
+ popenMap ? pclose(map) : fclose(map);
+ exit(EXIT_SUCCESS);
+}
diff --git a/sys-utils/renice.1 b/sys-utils/renice.1
new file mode 100644
index 0000000..afffd9e
--- /dev/null
+++ b/sys-utils/renice.1
@@ -0,0 +1,128 @@
+.\" Copyright (c) 1983, 1991, 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.
+.\"
+.\" @(#)renice.8 8.1 (Berkeley) 6/9/93
+.\"
+.TH RENICE "1" "September 2011" "util-linux" "User Commands"
+.SH NAME
+renice \- alter priority of running processes
+.SH SYNOPSIS
+.B renice
+-n priority [options] <pid> [...]
+.SH DESCRIPTION
+.B Renice
+alters the scheduling priority of one or more running processes. The
+following
+.I who
+parameters are interpreted as process ID's, process group ID's, or user
+names.
+.BR Renice 'ing
+a process group causes all processes in the process group to have their
+scheduling priority altered.
+.BR Renice 'ing
+a user causes all processes owned by the user to have their scheduling
+priority altered. By default, the processes to be affected are specified by
+their process ID's.
+.PP
+.SH OPTIONS
+.TP
+\fB\-n\fR, \fB\-\-priority\fR \fIpriority\fR
+The scheduling
+.I priority
+of the process, process group, or user. Use of
+.BR \-n " or " \-\-priority
+can only exist as the first argument of
+.B renice
+command.
+.TP
+\fB\-g\fR, \fB\-\-pgrp\fR \fIpgid\fR
+Force
+.I who
+parameters to be interpreted as \fIprocess group ID\fR's.
+.TP
+\fB\-u\fR, \fB\-\-user\fR \fIname\fR or \fIuid\fR
+Force the
+.I who
+parameters to be interpreted as \fIuser name\fR or \fIid\fR.
+.TP
+\fB\-p\fR, \fB\-\-pid\fR \fIprocess id\fR
+Resets the
+.I who
+interpretation to be (the default) \fIprocess ID\fR's.
+.TP
+\fB\-v\fR, \fB\-\-version\fR
+Print version.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Print help.
+.SH EXAMPLES
+.TP
+.B renice +1 987 -u daemon root -p 32
+would change the priority of process ID's 987 and 32, and all processes owned
+by users daemon and root.
+.SH NOTES
+Users other than the super-user may only alter the priority of processes they
+own, and can only monotonically increase their ``nice value'' (for security
+reasons) within the range 0 to
+.BR PRIO_MAX \ (20),
+unless a nice resource limit is set (Linux 2.6.12 and higher). The
+super-user may alter the priority of any process and set the priority to any
+value in the range
+.BR PRIO_MIN \ (\-20)
+to
+.BR PRIO_MAX .
+Useful priorities are: 20 (the affected processes will run only when nothing
+else in the system wants to), 0 (the ``base'' scheduling priority), anything
+negative (to make things go very fast).
+.SH FILES
+.TP
+.B /etc/passwd
+to map user names to user ID's
+.SH SEE ALSO
+.BR getpriority (2),
+.BR setpriority (2)
+.SH BUGS
+Non super-users can not increase scheduling priorities of their own processes,
+even if they were the ones that decreased the priorities in the first place.
+.PP
+The Linux kernel (at least version 2.0.0) and linux libc (at least version
+5.2.18) does not agree entirely on what the specifics of the systemcall
+interface to set nice values is. Thus causes renice to report bogus previous
+nice values.
+.SH HISTORY
+The
+.B renice
+command appeared in 4.0BSD.
+.SH AVAILABILITY
+The renice 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/sys-utils/renice.c b/sys-utils/renice.c
new file mode 100644
index 0000000..f5d51b6
--- /dev/null
+++ b/sys-utils/renice.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 1983, 1989, 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
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <stdio.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "nls.h"
+#include "c.h"
+#include "closestream.h"
+
+static int donice(int,int,int);
+
+static void __attribute__((__noreturn__)) usage(FILE *out)
+{
+ fputs(_("\nUsage:\n"), out);
+ fprintf(out,
+ _(" %1$s [-n] <priority> [-p] <pid> [<pid> ...]\n"
+ " %1$s [-n] <priority> -g <pgrp> [<pgrp> ...]\n"
+ " %1$s [-n] <priority> -u <user> [<user> ...]\n"),
+ program_invocation_short_name);
+
+ fputs(_("\nOptions:\n"), out);
+ fputs(_(" -g, --pgrp <id> interpret as process group ID\n"
+ " -h, --help print help\n"
+ " -n, --priority <num> set the nice increment value\n"
+ " -p, --pid <id> force to be interpreted as process ID\n"
+ " -u, --user <name|id> interpret as username or user ID\n"
+ " -v, --version print version\n"), out);
+
+ fputs(_("\nFor more information see renice(1).\n"), out);
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+/*
+ * Change the priority (nice) of processes
+ * or groups of processes which are already
+ * running.
+ */
+int
+main(int argc, char **argv)
+{
+ int which = PRIO_PROCESS;
+ int who = 0, prio, errs = 0;
+ char *endptr = NULL;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ argc--;
+ argv++;
+
+ if (argc == 1) {
+ if (strcmp(*argv, "-h") == 0 ||
+ strcmp(*argv, "--help") == 0)
+ usage(stdout);
+
+ if (strcmp(*argv, "-v") == 0 ||
+ strcmp(*argv, "--version") == 0) {
+ printf(_("renice from %s\n"), PACKAGE_STRING);
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ if (argc < 2)
+ usage(stderr);
+
+ if (strcmp(*argv, "-n") == 0 || strcmp(*argv, "--priority") == 0) {
+ argc--;
+ argv++;
+ }
+
+ prio = strtol(*argv, &endptr, 10);
+ if (*endptr)
+ usage(stderr);
+
+ argc--;
+ argv++;
+
+ for (; argc > 0; argc--, argv++) {
+ if (strcmp(*argv, "-g") == 0 || strcmp(*argv, "--pgrp") == 0) {
+ which = PRIO_PGRP;
+ continue;
+ }
+ if (strcmp(*argv, "-u") == 0 || strcmp(*argv, "--user") == 0) {
+ which = PRIO_USER;
+ continue;
+ }
+ if (strcmp(*argv, "-p") == 0 || strcmp(*argv, "--pid") == 0) {
+ which = PRIO_PROCESS;
+ continue;
+ }
+ if (which == PRIO_USER) {
+ register struct passwd *pwd = getpwnam(*argv);
+
+ if (pwd == NULL) {
+ warnx(_("unknown user %s"), *argv);
+ continue;
+ }
+ who = pwd->pw_uid;
+ } else {
+ who = strtol(*argv, &endptr, 10);
+ if (who < 0 || *endptr) {
+ warnx(_("bad value %s"), *argv);
+ continue;
+ }
+ }
+ errs += donice(which, who, prio);
+ }
+ return errs != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+static int
+donice(int which, int who, int prio) {
+ int oldprio, newprio;
+ const char *idtype = _("process ID");
+
+ if (which == PRIO_USER)
+ idtype = _("user ID");
+ else if (which == PRIO_PGRP)
+ idtype = _("process group ID");
+
+ errno = 0;
+ oldprio = getpriority(which, who);
+ if (oldprio == -1 && errno) {
+ warn(_("failed to get priority for %d (%s)"), who, idtype);
+ return 1;
+ }
+ if (setpriority(which, who, prio) < 0) {
+ warn(_("failed to set priority for %d (%s)"), who, idtype);
+ return 1;
+ }
+ errno = 0;
+ newprio = getpriority(which, who);
+ if (newprio == -1 && errno) {
+ warn(_("failed to get priority for %d (%s)"), who, idtype);
+ return 1;
+ }
+
+ printf(_("%d (%s) old priority %d, new priority %d\n"),
+ who, idtype, oldprio, newprio);
+ return 0;
+}
diff --git a/sys-utils/rtcwake.8 b/sys-utils/rtcwake.8
new file mode 100644
index 0000000..a536f32
--- /dev/null
+++ b/sys-utils/rtcwake.8
@@ -0,0 +1,146 @@
+.\" Copyright (c) 2007, SUSE LINUX Products GmbH
+.\" Bernhard Walle <bwalle@suse.de>
+.\"
+.\" 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.
+.\"
+.TH RTCWAKE 8 "July 2007" "util-linux" "System Administration"
+.SH NAME
+rtcwake - enter a system sleep state until specified wakeup time
+.SH SYNOPSIS
+.B rtcwake
+.RB [ \-hvVluan ]
+.RB [ \-d
+.IR device ]
+.RB [ \-m
+.IR standby_mode ]
+.RB { "\-t \fItime_t\fP" | "\-s \fIseconds\fP" }
+.SH DESCRIPTION
+This program is used to enter a system sleep state until specified wakeup time.
+.PP
+This uses cross-platform Linux interfaces to enter a system sleep state, and
+leave it no later than a specified time. It uses any RTC framework driver that
+supports standard driver model wakeup flags.
+.PP
+This is normally used like the old \fBapmsleep\fP utility, to wake from a suspend
+state like ACPI S1 (standby) or S3 (suspend-to-RAM). Most platforms can
+implement those without analogues of BIOS, APM, or ACPI.
+.P
+On some systems, this can also be used like \fBnvram-wakeup\fP, waking from states
+like ACPI S4 (suspend to disk). Not all systems have persistent media that are
+appropriate for such suspend modes.
+.SS Options
+.TP
+\fB-v\fP | \fB--verbose\fP
+Be verbose.
+.TP
+\fB-h\fP | \fB--help\fP
+Display a short help message that shows how to use the program.
+.TP
+\fB-V\fP | \fB--version\fP
+Displays version information and exists.
+.TP
+\fB-n\fP | \fB--dry-run\fP
+This option does everything but actually setup alarm, suspend system or wait
+for the alarm.
+.TP
+\fB-a\fP | \fB--auto\fP
+Reads the clock mode (whether the hardware clock is set to UTC or local time)
+from \fI/etc/adjtime\fP. That's the location where the
+.BR hwclock (8)
+stores that information. This is the default.
+.TP
+\fB-l\fP | \fB--local\fP
+Assumes that the hardware clock is set to local time, regardless of the
+contents of \fI/etc/adjtime\fP.
+.TP
+\fB-u\fP | \fB--utc\fP
+Assumes that the hardware clock is set to UTC (Universal Time Coordinated),
+regardless of the contents of \fI/etc/adjtime\fP.
+.TP
+\fB-d\fP \fIdevice\fP | \fB--device\fP \fIdevice\fP
+Uses \fIdevice\fP instead of \fIrtc0\fP as realtime clock. This option
+is only relevant if your system has more than one RTC. You may specify
+\fIrtc1\fP, \fIrtc2\fP, ... here.
+.TP
+\fB-s\fP \fIseconds\fP | \fB--seconds\fP \fIseconds\fP
+Sets the wakeup time to \fIseconds\fP in future from now.
+.TP
+\fB-t\fP \fItime_t\fP | \fB--time\fP \fItime_t\fP
+Sets the wakeup time to the absolute time \fItime_t\fP. \fItime_t\fP
+is the time in seconds since 1970-01-01, 00:00 UTC. Use the
+.BR date (1)
+tool to convert between human-readable time and \fItime_t\fP.
+.TP
+\fB-m\fP \fImode\fP | \fB--mode\fP \fImode\fP
+Use standby state \fImode\fP. Valid values are:
+.RS
+.TP
+.B standby
+ACPI state S1. This state offers minimal, though real, power savings, while
+providing a very low-latency transition back to a working system. This is the
+default mode.
+.TP
+.B mem
+ACPI state S3 (Suspend-to-RAM). This state offers significant power savings as
+everything in the system is put into a low-power state, except for memory,
+which is placed in self-refresh mode to retain its contents.
+.TP
+.B disk
+ACPI state S4 (Suspend-to-disk). This state offers the greatest power savings,
+and can be used even in the absence of low-level platform support for power
+management. This state operates similarly to Suspend-to-RAM, but includes a
+final step of writing memory contents to disk.
+.TP
+.B off
+ACPI state S5 (Poweroff). This is done by calling '/sbin/shutdown'.
+Not officially supported by ACPI, but usually working.
+.TP
+.B no
+Don't suspend. The rtcwake command sets RTC wakeup time only.
+.TP
+.B on
+Don't suspend, but read RTC device until alarm time appears. This mode is
+useful for debugging.
+.TP
+.B disable
+Disable previously set alarm.
+.TP
+.B show
+Print alarm information in format: "alarm: off|on <time>".
+The time is in ctime() output format, e.g. "alarm: on Tue Nov 16 04:48:45 2010".
+.RE
+.PP
+.SH NOTES
+Some PC systems can't currently exit sleep states such as \fImem\fP
+using only the kernel code accessed by this driver.
+They need help from userspace code to make the framebuffer work again.
+.SH HISTORY
+The program was posted several times on LKML and other lists
+before appearing in kernel commit message for Linux 2.6 in the GIT
+commit 87ac84f42a7a580d0dd72ae31d6a5eb4bfe04c6d.
+.SH AVAILABILITY
+The rtcwake command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.SH AUTHOR
+The program was written by David Brownell <dbrownell@users.sourceforge.net>
+and improved by Bernhard Walle <bwalle@suse.de>.
+.SH COPYRIGHT
+This is free software. You may redistribute copies of it under the terms
+of the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.
+There is NO WARRANTY, to the extent permitted by law.
+.SH "SEE ALSO"
+.BR hwclock (8),
+.BR date (1)
diff --git a/sys-utils/rtcwake.c b/sys-utils/rtcwake.c
new file mode 100644
index 0000000..0e16bd3
--- /dev/null
+++ b/sys-utils/rtcwake.c
@@ -0,0 +1,624 @@
+/*
+ * rtcwake -- enter a system sleep state until specified wakeup time.
+ *
+ * This uses cross-platform Linux interfaces to enter a system sleep state,
+ * and leave it no later than a specified time. It uses any RTC framework
+ * driver that supports standard driver model wakeup flags.
+ *
+ * This is normally used like the old "apmsleep" utility, to wake from a
+ * suspend state like ACPI S1 (standby) or S3 (suspend-to-RAM). Most
+ * platforms can implement those without analogues of BIOS, APM, or ACPI.
+ *
+ * On some systems, this can also be used like "nvram-wakeup", waking
+ * from states like ACPI S4 (suspend to disk). Not all systems have
+ * persistent media that are appropriate for such suspend modes.
+ *
+ * The best way to set the system's RTC is so that it holds the current
+ * time in UTC. Use the "-l" flag to tell this program that the system
+ * RTC uses a local timezone instead (maybe you dual-boot MS-Windows).
+ * That flag should not be needed on systems with adjtime support.
+ */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <linux/rtc.h>
+
+#include "nls.h"
+#include "xalloc.h"
+#include "pathnames.h"
+#include "usleep.h"
+#include "strutils.h"
+#include "c.h"
+#include "closestream.h"
+
+/* constants from legacy PC/AT hardware */
+#define RTC_PF 0x40
+#define RTC_AF 0x20
+#define RTC_UF 0x10
+
+#define MAX_LINE 1024
+
+#define RTC_PATH "/sys/class/rtc/%s/device/power/wakeup"
+#define SYS_POWER_STATE_PATH "/sys/power/state"
+#define ADJTIME_PATH "/etc/adjtime"
+#define DEFAULT_DEVICE "/dev/rtc0"
+#define DEFAULT_MODE "standby"
+
+enum ClockMode {
+ CM_AUTO,
+ CM_UTC,
+ CM_LOCAL
+};
+
+static unsigned verbose;
+static unsigned dryrun;
+static unsigned ioctl_aie_on; /* ioctl(AIE_ON) succeeded */
+enum ClockMode clock_mode = CM_AUTO;
+
+static struct option long_options[] = {
+ {"auto", no_argument, 0, 'a'},
+ {"dry-run", no_argument, 0, 'n'},
+ {"local", no_argument, 0, 'l'},
+ {"utc", no_argument, 0, 'u'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {"help", no_argument, 0, 'h'},
+ {"mode", required_argument, 0, 'm'},
+ {"device", required_argument, 0, 'd'},
+ {"seconds", required_argument, 0, 's'},
+ {"time", required_argument, 0, 't'},
+ {0, 0, 0, 0 }
+};
+
+static void __attribute__((__noreturn__)) usage(FILE *out)
+{
+ fputs(USAGE_HEADER, out);
+ fprintf(out,
+ _(" %s [options]\n"), program_invocation_short_name);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -d, --device <device> select rtc device (rtc0|rtc1|...)\n"
+ " -n, --dry-run does everything, but suspend\n"
+ " -l, --local RTC uses local timezone\n"
+ " -m, --mode <mode> standby|mem|... sleep mode\n"
+ " -s, --seconds <seconds> seconds to sleep\n"
+ " -t, --time <time_t> time to wake\n"
+ " -u, --utc RTC uses UTC\n"
+ " -v, --verbose verbose messages\n"), out);
+
+ printf(USAGE_SEPARATOR);
+ printf(USAGE_HELP);
+ printf(USAGE_VERSION);
+
+ printf(USAGE_MAN_TAIL("rtcwake(8)"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static int is_wakeup_enabled(const char *devname)
+{
+ char buf[128], *s;
+ FILE *f;
+
+ /* strip the '/dev/' from the devname here */
+ snprintf(buf, sizeof buf, RTC_PATH, devname + strlen("/dev/"));
+ f = fopen(buf, "r");
+ if (!f) {
+ warn(_("cannot open %s"), buf);
+ return 0;
+ }
+ s = fgets(buf, sizeof buf, f);
+ fclose(f);
+ if (!s)
+ return 0;
+
+ s = strchr(buf, '\n');
+ if (!s)
+ return 0;
+ *s = 0;
+
+ /* wakeup events could be disabled or not supported */
+ return strcmp(buf, "enabled") == 0;
+}
+
+/* all times should be in UTC */
+static time_t sys_time;
+static time_t rtc_time;
+
+static int get_basetimes(int fd)
+{
+ struct tm tm;
+ struct rtc_time rtc;
+
+ /* this process works in RTC time, except when working
+ * with the system clock (which always uses UTC).
+ */
+ if (clock_mode == CM_UTC)
+ setenv("TZ", "UTC", 1);
+ tzset();
+
+ /* read rtc and system clocks "at the same time", or as
+ * precisely (+/- a second) as we can read them.
+ */
+ if (ioctl(fd, RTC_RD_TIME, &rtc) < 0) {
+ warn(_("read rtc time failed"));
+ return -1;
+ }
+ sys_time = time(0);
+ if (sys_time == (time_t)-1) {
+ warn(_("read system time failed"));
+ return -1;
+ }
+
+ /* convert rtc_time to normal arithmetic-friendly form,
+ * updating tm.tm_wday as used by asctime().
+ */
+ memset(&tm, 0, sizeof tm);
+ tm.tm_sec = rtc.tm_sec;
+ tm.tm_min = rtc.tm_min;
+ tm.tm_hour = rtc.tm_hour;
+ tm.tm_mday = rtc.tm_mday;
+ tm.tm_mon = rtc.tm_mon;
+ tm.tm_year = rtc.tm_year;
+ tm.tm_isdst = -1; /* assume the system knows better than the RTC */
+ rtc_time = mktime(&tm);
+
+ if (rtc_time == (time_t)-1) {
+ warn(_("convert rtc time failed"));
+ return -1;
+ }
+
+ if (verbose) {
+ /* Unless the system uses UTC, either delta or tzone
+ * reflects a seconds offset from UTC. The value can
+ * help sort out problems like bugs in your C library.
+ */
+ printf("\tdelta = %ld\n", sys_time - rtc_time);
+ printf("\ttzone = %ld\n", timezone);
+
+ printf("\ttzname = %s\n", tzname[daylight]);
+ gmtime_r(&rtc_time, &tm);
+ printf("\tsystime = %ld, (UTC) %s",
+ (long) sys_time, asctime(gmtime(&sys_time)));
+ printf("\trtctime = %ld, (UTC) %s",
+ (long) rtc_time, asctime(&tm));
+ }
+
+ return 0;
+}
+
+static int setup_alarm(int fd, time_t *wakeup)
+{
+ struct tm *tm;
+ struct rtc_wkalrm wake;
+
+ /* The wakeup time is in POSIX time (more or less UTC).
+ * Ideally RTCs use that same time; but PCs can't do that
+ * if they need to boot MS-Windows. Messy...
+ *
+ * When clock_mode == CM_UTC this process's timezone is UTC,
+ * so we'll pass a UTC date to the RTC.
+ *
+ * Else clock_mode == CM_LOCAL so the time given to the RTC
+ * will instead use the local time zone.
+ */
+ tm = localtime(wakeup);
+
+ wake.time.tm_sec = tm->tm_sec;
+ wake.time.tm_min = tm->tm_min;
+ wake.time.tm_hour = tm->tm_hour;
+ wake.time.tm_mday = tm->tm_mday;
+ wake.time.tm_mon = tm->tm_mon;
+ wake.time.tm_year = tm->tm_year;
+ /* wday, yday, and isdst fields are unused by Linux */
+ wake.time.tm_wday = -1;
+ wake.time.tm_yday = -1;
+ wake.time.tm_isdst = -1;
+
+ wake.enabled = 1;
+
+ /* First try the preferred RTC_WKALM_SET */
+ if (!dryrun && ioctl(fd, RTC_WKALM_SET, &wake) < 0) {
+ wake.enabled = 0;
+ /* Fall back on the non-preferred way of setting wakeups; only
+ * works for alarms < 24 hours from now */
+ if ((rtc_time + (24 * 60 * 60)) > *wakeup) {
+ if (ioctl(fd, RTC_ALM_SET, &wake.time) < 0) {
+ warn(_("set rtc alarm failed"));
+ return -1;
+ }
+ if (ioctl(fd, RTC_AIE_ON, 0) < 0) {
+ warn(_("enable rtc alarm failed"));
+ return -1;
+ }
+ ioctl_aie_on = 1;
+ } else {
+ warn(_("set rtc wake alarm failed"));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int is_suspend_available(const char *suspend)
+{
+ int rc;
+ char buf[32];
+ FILE *f = fopen(SYS_POWER_STATE_PATH, "r");
+
+ if (!f)
+ return -1;
+
+ if (fgets(buf, sizeof buf, f) == NULL)
+ rc = -1;
+ else
+ rc = strstr(buf, suspend) != NULL;
+
+ fclose(f);
+ return rc;
+}
+
+static void suspend_system(const char *suspend)
+{
+ FILE *f = fopen(SYS_POWER_STATE_PATH, "w");
+
+ if (!f) {
+ warn(_("cannot open %s"), SYS_POWER_STATE_PATH);
+ return;
+ }
+
+ if (!dryrun) {
+ fprintf(f, "%s\n", suspend);
+ fflush(f);
+ }
+
+ /* this executes after wake from suspend */
+ if (close_stream(f))
+ errx(EXIT_FAILURE, _("write error"));
+}
+
+
+static int read_clock_mode(void)
+{
+ FILE *fp;
+ char linebuf[MAX_LINE];
+
+ fp = fopen(ADJTIME_PATH, "r");
+ if (!fp)
+ return -1;
+
+ /* skip first line */
+ if (!fgets(linebuf, MAX_LINE, fp)) {
+ fclose(fp);
+ return -1;
+ }
+
+ /* skip second line */
+ if (!fgets(linebuf, MAX_LINE, fp)) {
+ fclose(fp);
+ return -1;
+ }
+
+ /* read third line */
+ if (!fgets(linebuf, MAX_LINE, fp)) {
+ fclose(fp);
+ return -1;
+ }
+
+ if (strncmp(linebuf, "UTC", 3) == 0)
+ clock_mode = CM_UTC;
+ else if (strncmp(linebuf, "LOCAL", 5) == 0)
+ clock_mode = CM_LOCAL;
+
+ fclose(fp);
+
+ return 0;
+}
+
+/**
+ * print basic alarm settings
+ */
+static int print_alarm(int fd)
+{
+ struct rtc_wkalrm wake;
+ struct rtc_time rtc;
+ struct tm tm;
+ time_t alarm;
+
+ /* First try the preferred RTC_WKALM_RD */
+ if (ioctl(fd, RTC_WKALM_RD, &wake) < 0) {
+ /* Fall back on the non-preferred way of reading wakeups; only
+ * works for alarms < 24 hours from now
+ *
+ * set wake.enabled to 1 and determine from value of the year-1
+ * means disabled
+ */
+ wake.enabled = 1;
+ if (ioctl(fd, RTC_ALM_READ, &wake.time) < 0) {
+ warn(_("read rtc alarm failed"));
+ return -1;
+ }
+ }
+
+ if (wake.enabled != 1 || wake.time.tm_year == -1) {
+ printf(_("alarm: off\n"));
+ return 0;
+ }
+
+ rtc = wake.time;
+
+ memset(&tm, 0, sizeof tm);
+ tm.tm_sec = rtc.tm_sec;
+ tm.tm_min = rtc.tm_min;
+ tm.tm_hour = rtc.tm_hour;
+ tm.tm_mday = rtc.tm_mday;
+ tm.tm_mon = rtc.tm_mon;
+ tm.tm_year = rtc.tm_year;
+ tm.tm_isdst = -1; /* assume the system knows better than the RTC */
+
+ alarm = mktime(&tm);
+ if (alarm == (time_t)-1) {
+ warn(_("convert time failed"));
+ return -1;
+ }
+
+ /* 0 if both UTC, or expresses diff if RTC in local time */
+ alarm += sys_time - rtc_time;
+
+ printf(_("alarm: on %s"), ctime(&alarm));
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ char *devname = DEFAULT_DEVICE;
+ unsigned seconds = 0;
+ char *suspend = DEFAULT_MODE;
+
+ int rc = EXIT_SUCCESS;
+ int t;
+ int fd;
+ time_t alarm = 0;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while ((t = getopt_long(argc, argv, "ahd:lm:ns:t:uVv",
+ long_options, NULL)) != EOF) {
+ switch (t) {
+ case 'a':
+ /* CM_AUTO is default */
+ break;
+
+ case 'd':
+ devname = optarg;
+ break;
+
+ case 'l':
+ clock_mode = CM_LOCAL;
+ break;
+
+ /* what system power mode to use? for now handle only
+ * standardized mode names; eventually when systems
+ * define their own state names, parse
+ * /sys/power/state.
+ *
+ * "on" is used just to test the RTC alarm mechanism,
+ * bypassing all the wakeup-from-sleep infrastructure.
+ */
+ case 'm':
+ if (strcmp(optarg, "standby") == 0
+ || strcmp(optarg, "mem") == 0
+ || strcmp(optarg, "disk") == 0
+ || strcmp(optarg, "on") == 0
+ || strcmp(optarg, "no") == 0
+ || strcmp(optarg, "off") == 0
+ || strcmp(optarg, "disable") == 0
+ || strcmp(optarg, "show") == 0
+ ) {
+ suspend = optarg;
+ break;
+ }
+
+ errx(EXIT_FAILURE, _("unrecognized suspend state '%s'"),
+ optarg);
+ break;
+
+ case 'n':
+ dryrun = 1;
+ break;
+
+ /* alarm time, seconds-to-sleep (relative) */
+ case 's':
+ seconds = strtou32_or_err(optarg, _("invalid seconds argument"));
+ break;
+
+ /* alarm time, time_t (absolute, seconds since
+ * 1/1 1970 UTC)
+ */
+ case 't':
+ alarm = strtou32_or_err(optarg, _("invalid time argument"));
+ break;
+
+ case 'u':
+ clock_mode = CM_UTC;
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ exit(EXIT_SUCCESS);
+
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+ }
+
+ if (clock_mode == CM_AUTO) {
+ if (read_clock_mode() < 0) {
+ printf(_("%s: assuming RTC uses UTC ...\n"),
+ program_invocation_short_name);
+ clock_mode = CM_UTC;
+ }
+ }
+ if (verbose)
+ printf(clock_mode == CM_UTC ? _("Using UTC time.\n") :
+ _("Using local time.\n"));
+
+ if (!alarm && !seconds && strcmp(suspend,"disable") &&
+ strcmp(suspend,"show")) {
+
+ warnx(_("must provide wake time (see -t and -s options)"));
+ usage(stderr);
+ }
+
+ /* when devname doesn't start with /dev, append it */
+ if (strncmp(devname, "/dev/", strlen("/dev/")) != 0) {
+ char *new_devname;
+
+ new_devname = xmalloc(strlen(devname) + strlen("/dev/") + 1);
+
+ strcpy(new_devname, "/dev/");
+ strcat(new_devname, devname);
+ devname = new_devname;
+ }
+
+ if (strcmp(suspend, "on") != 0 && strcmp(suspend, "no") != 0
+ && !is_wakeup_enabled(devname))
+ errx(EXIT_FAILURE, _("%s not enabled for wakeup events"), devname);
+
+ /* this RTC must exist and (if we'll sleep) be wakeup-enabled */
+#ifdef O_CLOEXEC
+ fd = open(devname, O_RDONLY | O_CLOEXEC);
+#else
+ fd = open(devname, O_RDONLY);
+#endif
+ if (fd < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), devname);
+
+ /* relative or absolute alarm time, normalized to time_t */
+ if (get_basetimes(fd) < 0)
+ exit(EXIT_FAILURE);
+ if (verbose)
+ printf(_("alarm %ld, sys_time %ld, rtc_time %ld, seconds %u\n"),
+ alarm, sys_time, rtc_time, seconds);
+
+ if (strcmp(suspend, "show") && strcmp(suspend, "disable")) {
+ if (strcmp(suspend, "no") && strcmp(suspend, "on") &&
+ strcmp(suspend, "off") && is_suspend_available(suspend) <= 0) {
+ errx(EXIT_FAILURE, _("suspend to \"%s\" unavailable"), suspend);
+ }
+
+ /* care about alarm setup only if the show|disable
+ * modes are not set
+ */
+ if (alarm) {
+ if (alarm < sys_time)
+ errx(EXIT_FAILURE, _("time doesn't go backward to %s"),
+ ctime(&alarm));
+ alarm += sys_time - rtc_time;
+ } else
+ alarm = rtc_time + seconds + 1;
+
+ if (setup_alarm(fd, &alarm) < 0)
+ exit(EXIT_FAILURE);
+
+ if (strcmp(suspend, "no") == 0 || strcmp(suspend, "on") == 0)
+ printf(_("%s: wakeup using %s at %s"),
+ program_invocation_short_name, devname,
+ ctime(&alarm));
+ else
+ printf(_("%s: wakeup from \"%s\" using %s at %s"),
+ program_invocation_short_name, suspend, devname,
+ ctime(&alarm));
+ fflush(stdout);
+ usleep(10 * 1000);
+ }
+
+ if (strcmp(suspend, "no") == 0) {
+ if (verbose)
+ printf(_("suspend mode: no; leaving\n"));
+ dryrun = 1; /* to skip disabling alarm at the end */
+
+ } else if (strcmp(suspend, "off") == 0) {
+ char *arg[4];
+ int i = 0;
+
+ if (verbose)
+ printf(_("suspend mode: off; executing %s\n"),
+ _PATH_SHUTDOWN);
+ arg[i++] = _PATH_SHUTDOWN;
+ arg[i++] = "-P";
+ arg[i++] = "now";
+ arg[i] = NULL;
+
+ if (!dryrun) {
+ execv(arg[0], arg);
+
+ warn(_("unable to execute %s"), _PATH_SHUTDOWN);
+ rc = EXIT_FAILURE;
+ }
+
+ } else if (strcmp(suspend, "on") == 0) {
+ unsigned long data;
+
+ if (verbose)
+ printf(_("suspend mode: on; reading rtc\n"));
+
+ if (!dryrun) {
+ do {
+ t = read(fd, &data, sizeof data);
+ if (t < 0) {
+ warn(_("rtc read failed"));
+ break;
+ }
+ if (verbose)
+ printf("... %s: %03lx\n", devname, data);
+ } while (!(data & RTC_AF));
+ }
+
+ } else if (strcmp(suspend, "disable") == 0) {
+ /* just break, alarm gets disabled in the end */
+ if (verbose)
+ printf(_("suspend mode: disable; disabling alarm\n"));
+
+ } else if(strcmp(suspend,"show") == 0) {
+ if (verbose)
+ printf(_("suspend mode: show; printing alarm info\n"));
+ if (print_alarm(fd))
+ rc = EXIT_FAILURE;
+ dryrun = 1; /* don't really disable alarm in the end, just show */
+
+ } else {
+ if (verbose)
+ printf(_("suspend mode: %s; suspending system\n"), suspend);
+ sync();
+ suspend_system(suspend);
+ }
+
+ if (!dryrun && ioctl_aie_on && ioctl(fd, RTC_AIE_OFF, 0) < 0)
+ warn(_("disable rtc alarm interrupt failed"));
+
+ close(fd);
+ return rc;
+}
diff --git a/sys-utils/setarch.8 b/sys-utils/setarch.8
new file mode 100644
index 0000000..d206f2c
--- /dev/null
+++ b/sys-utils/setarch.8
@@ -0,0 +1,85 @@
+.TH SETARCH 8 "September 2011" "util-linux" "System Administration"
+.SH NAME
+setarch \- change reported architecture in new program environment and set personality flags
+.SH SYNOPSIS
+.B setarch
+.I <arch>
+.RI [ options ]
+.RI [ program
+.RI [ arguments ]]
+.br
+.B arch
+.RI [ options ]
+.RI [ program
+.RI [ arguments ]]
+.SH DESCRIPTION
+.B setarch
+This utility currently only affects the output of uname -m. For example, on an AMD64 system, running 'setarch i386 program' will cause 'program' to see
+.IR i686
+(or other relevant arch) instead of
+.IR x86_64
+as machine type. It also allows to set various personality options. The default
+.B program
+is /bin/sh.
+.SH OPTIONS
+.TP
+.I "\-v," "\-\-verbose"
+Be verbose.
+.TP
+.I "\-h," "\-\-help"
+Display help (it is also displayed when setarch takes no arguments).
+.TP
+.I "\-V," "\-\-version"
+Output version information and exit.
+.TP
+.I "\-\-uname\-2.6"
+Causes the program to see a kernel version number beginning with 2.6.
+.TP
+.I "\-3," "\-\-3gb"
+Specifies that processes should use a maximum of 3GB of address space on systems where it is supported (ADDR_LIMIT_3GB).
+.TP
+.I "\-B," "\-\-32bit"
+Turns on ADDR_LIMIT_32BIT.
+.TP
+.I "\-F," "\-\-fdpic-funcptrs"
+Userspace function pointers point to descriptors (turns on FDPIC_FUNCPTRS).
+.TP
+.I "\-I," "\-\-short-inode"
+Turns on SHORT_INODE.
+.TP
+.I "\-L," "\-\-addr-compat-layout"
+Changes the way virtual memory is allocated (turns on the ADDR_COMPAT_LAYOUT).
+.TP
+.I "\-R," "\-\-addr-no-randomize"
+Disables randomization of the virtual address space (turns on ADDR_NO_RANDOMIZE).
+.TP
+.I "\-S," "\-\-whole-seconds"
+Turns on WHOLE_SECONDS.
+.TP
+.I "\-T," "\-\-sticky-timeouts"
+Turns on STICKY_TIMEOUTS.
+.TP
+.I "\-X" "\-\-read-implies-exec"
+Turns on READ_IMPLIES_EXEC.
+.TP
+.I "\-Z," "mmap-page-zero"
+Turns on MMAP_PAGE_ZERO.
+.SH EXAMPLES
+setarch ppc32 rpmbuild --target=ppc --rebuild foo.src.rpm
+.br
+setarch ppc32 -v -vL3 rpmbuild --target=ppc --rebuild bar.src.rpm
+.br
+setarch ppc32 --32bit rpmbuild --target=ppc --rebuild foo.src.rpm
+.SH AUTHOR
+.MT sopwith@redhat.com
+Elliot Lee
+.ME
+.br
+.MT jnovy@redhat.com
+Jindrich Novy
+.ME
+.SH AVAILABILITY
+The setarch 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/sys-utils/setarch.c b/sys-utils/setarch.c
new file mode 100644
index 0000000..d13cdd6
--- /dev/null
+++ b/sys-utils/setarch.c
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2003-2007 Red Hat, Inc.
+ *
+ * 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.
+ *
+ *
+ * Written by Elliot Lee <sopwith@redhat.com>
+ * New personality options & code added by Jindrich Novy <jnovy@redhat.com>
+ * ADD_NO_RANDOMIZE flag added by Arjan van de Ven <arjanv@redhat.com>
+ * Help and MIPS support from Mike Frysinger (vapier@gentoo.org)
+ * Better error handling from Dmitry V. Levin (ldv@altlinux.org)
+ *
+ * based on ideas from the ppc32 util by Guy Streeter (2002-01), based on the
+ * sparc32 util by Jakub Jelinek (1998, 1999)
+ */
+
+#include <syscall.h>
+#include <linux/personality.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <limits.h>
+#include <sys/utsname.h>
+#include "nls.h"
+#include "c.h"
+#include "closestream.h"
+
+#define set_pers(pers) ((long)syscall(SYS_personality, pers))
+
+/* Options without equivalent short options */
+enum {
+ OPT_4GB = CHAR_MAX + 1,
+ OPT_UNAME26
+};
+
+#define turn_on(_flag, _opts) \
+ do { \
+ (_opts) |= _flag; \
+ if (verbose) \
+ printf(_("Switching on %s.\n"), #_flag); \
+ } while(0)
+
+
+#if !HAVE_DECL_UNAME26
+# define UNAME26 0x0020000
+#endif
+#if !HAVE_DECL_ADDR_NO_RANDOMIZE
+# define ADDR_NO_RANDOMIZE 0x0040000
+#endif
+#if !HAVE_DECL_FDPIC_FUNCPTRS
+# define FDPIC_FUNCPTRS 0x0080000
+#endif
+#if !HAVE_DECL_MMAP_PAGE_ZERO
+# define MMAP_PAGE_ZERO 0x0100000
+#endif
+#if !HAVE_DECL_ADDR_COMPAT_LAYOUT
+# define ADDR_COMPAT_LAYOUT 0x0200000
+#endif
+#if !HAVE_DECL_READ_IMPLIES_EXEC
+# define READ_IMPLIES_EXEC 0x0400000
+#endif
+#if !HAVE_DECL_ADDR_LIMIT_32BIT
+# define ADDR_LIMIT_32BIT 0x0800000
+#endif
+#if !HAVE_DECL_SHORT_INODE
+# define SHORT_INODE 0x1000000
+#endif
+#if !HAVE_DECL_WHOLE_SECONDS
+# define WHOLE_SECONDS 0x2000000
+#endif
+#if !HAVE_DECL_STICKY_TIMEOUTS
+# define STICKY_TIMEOUTS 0x4000000
+#endif
+#if !HAVE_DECL_ADDR_LIMIT_3GB
+# define ADDR_LIMIT_3GB 0x8000000
+#endif
+
+static void __attribute__((__noreturn__))
+show_help(void)
+{
+ printf(USAGE_HEADER);
+ printf(_(" %s%s [options] [program [program arguments]]\n"),
+ program_invocation_short_name,
+ !strcmp(program_invocation_short_name, "setarch") ? " <arch>" : "");
+
+ printf(USAGE_OPTIONS);
+ printf(_(
+ " -v, --verbose says what options are being switched on\n"
+ " -R, --addr-no-randomize disables randomization of the virtual address space\n"
+ " -F, --fdpic-funcptrs makes function pointers point to descriptors\n"
+ " -Z, --mmap-page-zero turns on MMAP_PAGE_ZERO\n"
+ " -L, --addr-compat-layout changes the way virtual memory is allocated\n"
+ " -X, --read-implies-exec turns on READ_IMPLIES_EXEC\n"
+ " -B, --32bit turns on ADDR_LIMIT_32BIT\n"
+ " -I, --short-inode turns on SHORT_INODE\n"
+ " -S, --whole-seconds turns on WHOLE_SECONDS\n"
+ " -T, --sticky-timeouts turns on STICKY_TIMEOUTS\n"
+ " -3, --3gb limits the used address space to a maximum of 3 GB\n"
+ " --4gb ignored (for backward compatibility only)\n"
+ " --uname-2.6 turns on UNAME26\n"));
+
+ printf(USAGE_SEPARATOR);
+ printf(USAGE_HELP);
+ printf(USAGE_VERSION);
+ printf(USAGE_MAN_TAIL("setarch(8)"));
+
+ exit(EXIT_SUCCESS);
+}
+
+static void __attribute__((__noreturn__))
+show_usage(const char *s)
+{
+ if (s)
+ errx(EXIT_FAILURE, _("%s\nTry `%s --help' for more information."), s, program_invocation_short_name);
+ else
+ errx(EXIT_FAILURE, _("Try `%s --help' for more information."), program_invocation_short_name);
+}
+
+static void __attribute__((__noreturn__))
+show_version(void)
+{
+ printf(UTIL_LINUX_VERSION);
+ exit(EXIT_SUCCESS);
+}
+
+static int
+set_arch(const char *pers, unsigned long options)
+{
+ struct utsname un;
+ int i;
+ unsigned long pers_value;
+
+ struct {
+ int perval;
+ const char *target_arch, *result_arch;
+ } transitions[] = {
+ {PER_LINUX32, "linux32", NULL},
+ {PER_LINUX, "linux64", NULL},
+#if defined(__powerpc__) || defined(__powerpc64__)
+ {PER_LINUX32, "ppc32", "ppc"},
+ {PER_LINUX32, "ppc", "ppc"},
+ {PER_LINUX, "ppc64", "ppc64"},
+ {PER_LINUX, "ppc64pseries", "ppc64"},
+ {PER_LINUX, "ppc64iseries", "ppc64"},
+#endif
+#if defined(__x86_64__) || defined(__i386__) || defined(__ia64__)
+ {PER_LINUX32, "i386", "i386"},
+ {PER_LINUX32, "i486", "i386"},
+ {PER_LINUX32, "i586", "i386"},
+ {PER_LINUX32, "i686", "i386"},
+ {PER_LINUX32, "athlon", "i386"},
+#endif
+#if defined(__x86_64__) || defined(__i386__)
+ {PER_LINUX, "x86_64", "x86_64"},
+#endif
+#if defined(__ia64__) || defined(__i386__)
+ {PER_LINUX, "ia64", "ia64"},
+#endif
+#if defined(__hppa__)
+ {PER_LINUX32, "parisc32", "parisc"},
+ {PER_LINUX32, "parisc", "parisc"},
+ {PER_LINUX, "parisc64", "parisc64"},
+#endif
+#if defined(__s390x__) || defined(__s390__)
+ {PER_LINUX32, "s390", "s390"},
+ {PER_LINUX, "s390x", "s390x"},
+#endif
+#if defined(__sparc64__) || defined(__sparc__)
+ {PER_LINUX32, "sparc", "sparc"},
+ {PER_LINUX32, "sparc32bash", "sparc"},
+ {PER_LINUX32, "sparc32", "sparc"},
+ {PER_LINUX, "sparc64", "sparc64"},
+#endif
+#if defined(__mips64__) || defined(__mips__)
+ {PER_LINUX32, "mips32", "mips"},
+ {PER_LINUX32, "mips", "mips"},
+ {PER_LINUX, "mips64", "mips64"},
+#endif
+#if defined(__alpha__)
+ {PER_LINUX, "alpha", "alpha"},
+ {PER_LINUX, "alphaev5", "alpha"},
+ {PER_LINUX, "alphaev56", "alpha"},
+ {PER_LINUX, "alphaev6", "alpha"},
+ {PER_LINUX, "alphaev67", "alpha"},
+#endif
+ {-1, NULL, NULL}
+ };
+
+ for(i = 0; transitions[i].perval >= 0; i++)
+ if(!strcmp(pers, transitions[i].target_arch))
+ break;
+
+ if(transitions[i].perval < 0)
+ errx(EXIT_FAILURE, _("%s: Unrecognized architecture"), pers);
+
+ pers_value = transitions[i].perval | options;
+ if (set_pers(pers_value) == -EINVAL)
+ return 1;
+
+ uname(&un);
+ if(transitions[i].result_arch &&
+ strcmp(un.machine, transitions[i].result_arch))
+ {
+ if(strcmp(transitions[i].result_arch, "i386")
+ || (strcmp(un.machine, "i486")
+ && strcmp(un.machine, "i586")
+ && strcmp(un.machine, "i686")
+ && strcmp(un.machine, "athlon")))
+ errx(EXIT_FAILURE, _("%s: Unrecognized architecture"), pers);
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ const char *p;
+ unsigned long options = 0;
+ int verbose = 0;
+ int c;
+
+ /* Options --3gb and --4gb are for compatibitity with an old Debian setarch
+ implementation. */
+ static const struct option longopts[] =
+ {
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'V' },
+ { "verbose", 0, 0, 'v' },
+ { "addr-no-randomize", 0, 0, 'R' },
+ { "fdpic-funcptrs", 0, 0, 'F' },
+ { "mmap-page-zero", 0, 0, 'Z' },
+ { "addr-compat-layout", 0, 0, 'L' },
+ { "read-implies-exec", 0, 0, 'X' },
+ { "32bit", 0, 0, 'B' },
+ { "short-inode", 0, 0, 'I' },
+ { "whole-seconds", 0, 0, 'S' },
+ { "sticky-timeouts", 0, 0, 'T' },
+ { "3gb", 0, 0, '3' },
+ { "4gb", 0, 0, OPT_4GB },
+ { "uname-2.6", 0, 0, OPT_UNAME26 },
+ { NULL, 0, 0, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ if (argc < 1)
+ show_usage(_("Not enough arguments"));
+
+ p = program_invocation_short_name;
+ if (!strcmp(p, "setarch")) {
+ argc--;
+ if (argc < 1)
+ show_usage(_("Not enough arguments"));
+ p = argv[1];
+ argv[1] = argv[0]; /* for getopt_long() to get the program name */
+ argv++;
+ if (!strcmp(p, "-h") || !strcmp(p, "--help"))
+ show_help();
+ else if (!strcmp(p, "-V") || !strcmp(p, "--version"))
+ show_version();
+ }
+ #if defined(__sparc64__) || defined(__sparc__)
+ if (!strcmp(p, "sparc32bash")) {
+ if (set_arch(p, 0L))
+ err(EXIT_FAILURE, _("Failed to set personality to %s"), p);
+ execl("/bin/bash", NULL);
+ err(EXIT_FAILURE, "/bin/bash");
+ }
+ #endif
+
+ while ((c = getopt_long(argc, argv, "+hVv3BFILRSTXZ", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'h':
+ show_help();
+ break;
+ case 'V':
+ show_version();
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'R':
+ turn_on(ADDR_NO_RANDOMIZE, options);
+ break;
+ case 'F':
+ turn_on(FDPIC_FUNCPTRS, options);
+ break;
+ case 'Z':
+ turn_on(MMAP_PAGE_ZERO, options);
+ break;
+ case 'L':
+ turn_on(ADDR_COMPAT_LAYOUT, options);
+ break;
+ case 'X':
+ turn_on(READ_IMPLIES_EXEC, options);
+ break;
+ case 'B':
+ turn_on(ADDR_LIMIT_32BIT, options);
+ break;
+ case 'I':
+ turn_on(SHORT_INODE, options);
+ break;
+ case 'S':
+ turn_on(WHOLE_SECONDS, options);
+ break;
+ case 'T':
+ turn_on(STICKY_TIMEOUTS, options);
+ break;
+ case '3':
+ turn_on(ADDR_LIMIT_3GB, options);
+ break;
+ case OPT_4GB: /* just ignore this one */
+ break;
+ case OPT_UNAME26:
+ turn_on(UNAME26, options);
+ break;
+ default:
+ show_usage(NULL);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (set_arch(p, options))
+ err(EXIT_FAILURE, _("Failed to set personality to %s"), p);
+
+ if (!argc) {
+ execl("/bin/sh", "-sh", NULL);
+ err(EXIT_FAILURE, "/bin/sh");
+ }
+
+ execvp(argv[0], argv);
+ err(EXIT_FAILURE, "%s", argv[0]);
+ return EXIT_FAILURE;
+}
diff --git a/sys-utils/setsid.1 b/sys-utils/setsid.1
new file mode 100644
index 0000000..eff7948
--- /dev/null
+++ b/sys-utils/setsid.1
@@ -0,0 +1,24 @@
+.\" Rick Sladkey <jrs@world.std.com>
+.\" In the public domain.
+.\" Path modifications by faith@cs.unc.edu
+.TH SETSID 1 "November 1993" "util-linux" "User Commands"
+.SH NAME
+setsid \- run a program in a new session
+.SH SYNOPSIS
+.B setsid
+.I program
+.RI [ arg ...]
+.SH DESCRIPTION
+.B setsid
+runs a program in a new session.
+.SH OPTIONS
+.TP
+\fB\-c\fP, \fB\-\-ctty\fP
+Set the controlling terminal to the current one.
+.SH "SEE ALSO"
+.BR setsid (2)
+.SH AUTHOR
+Rick Sladkey <jrs@world.std.com>
+.SH AVAILABILITY
+The setsid command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/sys-utils/setsid.c b/sys-utils/setsid.c
new file mode 100644
index 0000000..071ccc4
--- /dev/null
+++ b/sys-utils/setsid.c
@@ -0,0 +1,98 @@
+/*
+ * setsid.c -- execute a command in a new session
+ * Rick Sladkey <jrs@world.std.com>
+ * In the public domain.
+ *
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ *
+ * 2001-01-18 John Fremlin <vii@penguinpowered.com>
+ * - fork in case we are process group leader
+ *
+ */
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include "c.h"
+#include "nls.h"
+#include "closestream.h"
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(
+ " %s [options] <program> [arguments ...]\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -c, --ctty set the controlling terminal to the current one\n"),
+ out);
+
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+
+ fprintf(out, USAGE_MAN_TAIL("setsid(1)"));
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ int ch;
+ int ctty = 0;
+
+ static const struct option longopts[] = {
+ {"ctty", no_argument, NULL, 'c'},
+ {"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 ((ch = getopt_long(argc, argv, "+Vhc", longopts, NULL)) != -1)
+ switch (ch) {
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'c':
+ ctty=1;
+ break;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+
+ if (argc < 2)
+ usage(stderr);
+
+ if (getpgrp() == getpid()) {
+ switch (fork()) {
+ case -1:
+ err(EXIT_FAILURE, _("fork"));
+ case 0:
+ /* child */
+ break;
+ default:
+ /* parent */
+ return 0;
+ }
+ }
+ if (setsid() < 0)
+ /* cannot happen */
+ err(EXIT_FAILURE, _("setsid failed"));
+
+ if (ctty) {
+ if (ioctl(STDIN_FILENO, TIOCSCTTY, 1))
+ warn(_("failed to set the controlling terminal"));
+ }
+ execvp(argv[optind], argv + optind);
+ err(EXIT_FAILURE, _("execvp failed"));
+}
diff --git a/sys-utils/swapoff.8 b/sys-utils/swapoff.8
new file mode 100644
index 0000000..1a06b7e
--- /dev/null
+++ b/sys-utils/swapoff.8
@@ -0,0 +1 @@
+.so man8/swapon.8
diff --git a/sys-utils/swapoff.c b/sys-utils/swapoff.c
new file mode 100644
index 0000000..0bd85ac
--- /dev/null
+++ b/sys-utils/swapoff.c
@@ -0,0 +1,196 @@
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+
+#ifdef HAVE_SYS_SWAP_H
+# include <sys/swap.h>
+#endif
+
+#include "nls.h"
+#include "c.h"
+#include "closestream.h"
+
+#include "swapon-common.h"
+
+#ifndef SWAPON_HAS_TWO_ARGS
+/* libc is insane, let's call the kernel */
+# include <sys/syscall.h>
+# define swapoff(path) syscall(SYS_swapoff, path)
+#endif
+
+static int verbose;
+static int all;
+
+#define QUIET 1
+#define CANONIC 1
+
+static int do_swapoff(const char *orig_special, int quiet, int canonic)
+{
+ const char *special = orig_special;
+
+ if (verbose)
+ printf(_("swapoff %s\n"), orig_special);
+
+ if (!canonic) {
+ special = mnt_resolve_spec(orig_special, mntcache);
+ if (!special)
+ return cannot_find(orig_special);
+ }
+
+ if (swapoff(special) == 0)
+ return 0; /* success */
+
+ if (errno == EPERM)
+ errx(EXIT_FAILURE, _("Not superuser."));
+
+ if (!quiet || errno == ENOMEM)
+ warn(_("%s: swapoff failed"), orig_special);
+
+ return -1;
+}
+
+static int swapoff_by_label(const char *label, int quiet)
+{
+ const char *special = mnt_resolve_tag("LABEL", label, mntcache);
+ return special ? do_swapoff(special, quiet, CANONIC) : cannot_find(label);
+}
+
+static int swapoff_by_uuid(const char *uuid, int quiet)
+{
+ const char *special = mnt_resolve_tag("UUID", uuid, mntcache);
+ return special ? do_swapoff(special, quiet, CANONIC) : cannot_find(uuid);
+}
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fputs(USAGE_HEADER, out);
+
+ fprintf(out, _(" %s [options] [<spec>]\n"), program_invocation_short_name);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -a, --all disable all swaps from /proc/swaps\n"
+ " -v, --verbose verbose mode\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+
+ fputs(_("\nThe <spec> parameter:\n" \
+ " -L <label> LABEL of device to be used\n" \
+ " -U <uuid> UUID of device to be used\n" \
+ " LABEL=<label> LABEL of device to be used\n" \
+ " UUID=<uuid> UUID of device to be used\n" \
+ " <device> name of device to be used\n" \
+ " <file> name of file to be used\n"), out);
+
+ fprintf(out, USAGE_MAN_TAIL("swapoff(8)"));
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static int swapoff_all(void)
+{
+ int status = 0;
+ struct libmnt_table *tb;
+ struct libmnt_fs *fs;
+ struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_BACKWARD);
+
+ if (!itr)
+ err(EXIT_FAILURE, _("failed to initialize libmount iterator"));
+
+ /*
+ * In case /proc/swaps exists, unswap stuff listed there. We are quiet
+ * but report errors in status. Errors might mean that /proc/swaps
+ * exists as ordinary file, not in procfs. do_swapoff() exits
+ * immediately on EPERM.
+ */
+ tb = get_swaps();
+
+ while (tb && mnt_table_find_next_fs(tb, itr, match_swap, NULL, &fs) == 0)
+ status |= do_swapoff(mnt_fs_get_source(fs), QUIET, CANONIC);
+
+ /*
+ * Unswap stuff mentioned in /etc/fstab. Probably it was unmounted
+ * already, so errors are not bad. Doing swapoff -a twice should not
+ * give error messages.
+ */
+ tb = get_fstab();
+ mnt_reset_iter(itr, MNT_ITER_FORWARD);
+
+ while (tb && mnt_table_find_next_fs(tb, itr, match_swap, NULL, &fs) == 0) {
+ if (!is_active_swap(mnt_fs_get_source(fs)))
+ do_swapoff(mnt_fs_get_source(fs), QUIET, !CANONIC);
+ }
+
+ mnt_free_iter(itr);
+ return status;
+}
+
+int main(int argc, char *argv[])
+{
+ int status = 0, c;
+ size_t i;
+
+ static const struct option long_opts[] = {
+ { "all", 0, 0, 'a' },
+ { "help", 0, 0, 'h' },
+ { "verbose", 0, 0, 'v' },
+ { "version", 0, 0, 'V' },
+ { NULL, 0, 0, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while ((c = getopt_long(argc, argv, "ahvVL:U:",
+ long_opts, NULL)) != -1) {
+ switch (c) {
+ case 'a': /* all */
+ ++all;
+ break;
+ case 'h': /* help */
+ usage(stdout);
+ break;
+ case 'v': /* be chatty */
+ ++verbose;
+ break;
+ case 'V': /* version */
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'L':
+ add_label(optarg);
+ break;
+ case 'U':
+ add_uuid(optarg);
+ break;
+ case '?':
+ default:
+ usage(stderr);
+ }
+ }
+ argv += optind;
+
+ if (!all && !numof_labels() && !numof_uuids() && *argv == NULL)
+ usage(stderr);
+
+ mnt_init_debug(0);
+ mntcache = mnt_new_cache();
+
+ for (i = 0; i < numof_labels(); i++)
+ status |= swapoff_by_label(get_label(i), !QUIET);
+
+ for (i = 0; i < numof_uuids(); i++)
+ status |= swapoff_by_uuid(get_uuid(i), !QUIET);
+
+ while (*argv != NULL)
+ status |= do_swapoff(*argv++, !QUIET, !CANONIC);
+
+ if (all)
+ status |= swapoff_all();
+
+ free_tables();
+ mnt_free_cache(mntcache);
+
+ return status;
+}
diff --git a/sys-utils/swapon-common.c b/sys-utils/swapon-common.c
new file mode 100644
index 0000000..5c95ef3
--- /dev/null
+++ b/sys-utils/swapon-common.c
@@ -0,0 +1,106 @@
+
+#include "c.h"
+#include "nls.h"
+#include "swapon-common.h"
+#include "xalloc.h"
+
+/*
+ * content of /proc/swaps and /etc/fstab
+ */
+static struct libmnt_table *swaps, *fstab;
+
+struct libmnt_cache *mntcache;
+
+struct libmnt_table *get_fstab(void)
+{
+ if (!fstab) {
+ fstab = mnt_new_table();
+ if (!fstab)
+ return NULL;
+ mnt_table_set_cache(fstab, mntcache);
+ if (mnt_table_parse_fstab(fstab, NULL) != 0)
+ return NULL;
+ }
+
+ return fstab;
+}
+
+struct libmnt_table *get_swaps(void)
+{
+ if (!swaps) {
+ swaps = mnt_new_table();
+ if (!swaps)
+ return NULL;
+ mnt_table_set_cache(swaps, mntcache);
+ if (mnt_table_parse_swaps(swaps, NULL) != 0)
+ return NULL;
+ }
+
+ return swaps;
+}
+
+void free_tables(void)
+{
+ mnt_free_table(swaps);
+ mnt_free_table(fstab);
+}
+
+int match_swap(struct libmnt_fs *fs, void *data __attribute__((unused)))
+{
+ return fs && mnt_fs_is_swaparea(fs);
+}
+
+int is_active_swap(const char *filename)
+{
+ struct libmnt_table *st = get_swaps();
+ return st && mnt_table_find_source(st, filename, MNT_ITER_BACKWARD);
+}
+
+
+int cannot_find(const char *special)
+{
+ warnx(_("cannot find the device for %s"), special);
+ return -1;
+}
+
+/*
+ * Lists with -L and -U option
+ */
+static const char **llist;
+static size_t llct;
+static const char **ulist;
+static size_t ulct;
+
+
+void add_label(const char *label)
+{
+ llist = (const char **) xrealloc(llist, (++llct) * sizeof(char *));
+ llist[llct - 1] = label;
+}
+
+const char *get_label(size_t i)
+{
+ return i < llct ? llist[i] : NULL;
+}
+
+size_t numof_labels(void)
+{
+ return llct;
+}
+
+void add_uuid(const char *uuid)
+{
+ ulist = (const char **) xrealloc(ulist, (++ulct) * sizeof(char *));
+ ulist[ulct - 1] = uuid;
+}
+
+const char *get_uuid(size_t i)
+{
+ return i < ulct ? ulist[i] : NULL;
+}
+
+size_t numof_uuids(void)
+{
+ return ulct;
+}
+
diff --git a/sys-utils/swapon-common.h b/sys-utils/swapon-common.h
new file mode 100644
index 0000000..53ba15e
--- /dev/null
+++ b/sys-utils/swapon-common.h
@@ -0,0 +1,26 @@
+#ifndef UTIL_LINUX_SWAPON_COMMON_H
+#define UTIL_LINUX_SWAPON_COMMON_H
+
+#include <libmount.h>
+
+extern struct libmnt_cache *mntcache;
+
+extern struct libmnt_table *get_fstab(void);
+extern struct libmnt_table *get_swaps(void);
+extern void free_tables(void);
+
+extern int match_swap(struct libmnt_fs *fs, void *data);
+extern int is_active_swap(const char *filename);
+
+extern int cannot_find(const char *special);
+
+extern void add_label(const char *label);
+extern const char *get_label(size_t i);
+extern size_t numof_labels(void);
+
+extern void add_uuid(const char *uuid);
+extern const char *get_uuid(size_t i);
+extern size_t numof_uuids(void);
+
+
+#endif /* UTIL_LINUX_SWAPON_COMMON_H */
diff --git a/sys-utils/swapon.8 b/sys-utils/swapon.8
new file mode 100644
index 0000000..ed4df55
--- /dev/null
+++ b/sys-utils/swapon.8
@@ -0,0 +1,241 @@
+.\" Copyright (c) 1980, 1991 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.
+.\"
+.\" @(#)swapon.8 6.3 (Berkeley) 3/16/91
+.\"
+.\" Sun Dec 27 12:31:30 1992: Modified by faith@cs.unc.edu
+.\" Sat Mar 6 20:46:02 1993: Modified by faith@cs.unc.edu
+.\" Sat Oct 9 09:35:30 1993: Converted to man format by faith@cs.unc.edu
+.\" Sat Nov 27 20:22:42 1993: Updated authorship information, faith@cs.unc.edu
+.\" Mon Sep 25 14:12:38 1995: Added -v and -p information
+.\" Tue Apr 30 03:32:07 1996: Added some text from A. Koppenhoefer
+.\"
+.TH SWAPON 8 "September 1995" "util-linux" "System Administration"
+.SH NAME
+swapon, swapoff \- enable/disable devices and files for paging and swapping
+.SH SYNOPSIS
+Get info:
+.br
+.in +5
+.B swapon \-s
+.RB [ \-h ]
+.RB [ \-V ]
+.sp
+.in -5
+Enable/disable:
+.br
+.in +5
+.B swapon
+.RB [ \-d ]
+.RB [ \-f ]
+.RB [ \-p
+.IR priority ]
+.RB [ \-v ]
+.IR specialfile ...
+.br
+.B swapoff
+.RB [ \-v ]
+.IR specialfile ...
+.sp
+.in -5
+Enable/disable all:
+.br
+.in +5
+.B swapon \-a
+.RB [ \-e ]
+.RB [ \-f ]
+.RB [ \-v ]
+.br
+.B swapoff \-a
+.RB [ \-v ]
+.in -5
+.SH DESCRIPTION
+.B swapon
+is used to specify devices on which paging and swapping are to take place.
+
+The device or file used is given by the
+.I specialfile
+parameter. It may be of the form
+.BI \-L " label"
+or
+.BI \-U " uuid"
+to indicate a device by label or uuid.
+
+Calls to
+.B swapon
+normally occur in the system boot scripts making all swap devices available, so
+that the paging and swapping activity is interleaved across several devices and
+files.
+
+.B swapoff
+disables swapping on the specified devices and files.
+When the
+.B \-a
+flag is given, swapping is disabled on all known swap devices and files
+(as found in
+.I /proc/swaps
+or
+.IR /etc/fstab ).
+
+.TP
+.B "\-a, \-\-all"
+All devices marked as ``swap'' in
+.I /etc/fstab
+are made available, except for those with the ``noauto'' option.
+Devices that are already being used as swap are silently skipped.
+.TP
+.B "\-d, \-\-discard"
+Discard freed swap pages before they are reused, if the swap
+device supports the discard or trim operation. This may improve
+performance on some Solid State Devices, but often it does not.
+The
+.I /etc/fstab
+mount option
+.BI discard
+may be also used to enable discard flag.
+.TP
+.B "\-e, \-\-ifexists"
+Silently skip devices that do not exist.
+The
+.I /etc/fstab
+mount option
+.BI nofail
+may be also used to skip non-existing device.
+
+.TP
+.B "\-f, \-\-fixpgsz"
+Reinitialize (exec /sbin/mkswap) the swap space if its page size does not
+match that of the the current running kernel.
+.B mkswap(2)
+initializes the whole device and does not check for bad blocks.
+.TP
+.B \-h, \-\-help
+Provide help.
+.TP
+.B "\-L \fIlabel\fP"
+Use the partition that has the specified
+.IR label .
+(For this, access to
+.I /proc/partitions
+is needed.)
+.TP
+.B "\-p, \-\-priority \fIpriority\fP"
+Specify the priority of the swap device.
+.I priority
+is a value between 0 and 32767. Higher numbers indicate higher
+priority. See
+.BR swapon (2)
+for a full description of swap priorities. Add
+.BI pri= value
+to the option field of
+.I /etc/fstab
+for use with
+.BR "swapon -a" .
+.TP
+.B "\-s, \-\-summary"
+Display swap usage summary by device. Equivalent to "cat /proc/swaps".
+Not available before Linux 2.1.25.
+.TP
+\fB\-\-show\fR [\fIcolumn,column\fR]
+Display definable device table similar to
+.B \-\-summary
+output. See \-\-help output for
+.I column
+list.
+.TP
+.B \-\-noheadings
+Do not print headings when displaying
+.B \-\-show
+output.
+.TP
+.B \-\-raw
+Display
+.B \-\-show
+output without aligning table columns.
+.TP
+.B \-\-inhuman
+Display swap size in bytes in
+.B \-\-show
+output instead of user friendly size and unit.
+.B "\-U \fIuuid\fP"
+Use the partition that has the specified
+.IR uuid .
+.TP
+.B "\-v, \-\-verbose"
+Be verbose.
+.TP
+.B "\-V, \-\-version"
+Display version.
+.SH NOTES
+You should not use
+.B swapon
+on a file with holes.
+Swap over NFS may not work.
+.PP
+.B swapon
+automatically detects and rewrites swap space signature with old software
+suspend data (e.g S1SUSPEND, S2SUSPEND, ...). The problem is that if we don't
+do it, then we get data corruption the next time an attempt at unsuspending is
+made.
+.PP
+.B swapon
+may not work correctly when using a swap file with some versions of btrfs.
+This is due to the swap file implementation in the kernel expecting to be able
+to write to the file directly, without the assistance of the file system.
+Since btrfs is a copy-on-write file system, the file location may not be
+static and corruption can result. Btrfs actively disallows the use of files
+on its file systems by refusing to map the file. This can be seen in the system
+log as "swapon: swapfile has holes." One possible workaround is to map the
+file to a loopback device. This will allow the file system to determine the
+mapping properly but may come with a performance impact.
+
+.SH SEE ALSO
+.BR swapon (2),
+.BR swapoff (2),
+.BR fstab (5),
+.BR init (8),
+.BR mkswap (8),
+.BR rc (8),
+.BR mount (8)
+.SH FILES
+.br
+.I /dev/sd??
+standard paging devices
+.br
+.I /etc/fstab
+ascii filesystem description table
+.SH HISTORY
+The
+.B swapon
+command appeared in 4.0BSD.
+.SH AVAILABILITY
+The swapon command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/sys-utils/swapon.c b/sys-utils/swapon.c
new file mode 100644
index 0000000..c9cabc1
--- /dev/null
+++ b/sys-utils/swapon.c
@@ -0,0 +1,823 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <ctype.h>
+
+#include <libmount.h>
+
+#include "c.h"
+#include "nls.h"
+#include "bitops.h"
+#include "blkdev.h"
+#include "pathnames.h"
+#include "xalloc.h"
+#include "closestream.h"
+
+#include "swapheader.h"
+#include "swapon-common.h"
+#include "strutils.h"
+#include "tt.h"
+
+#define PATH_MKSWAP "/sbin/mkswap"
+
+#ifdef HAVE_SYS_SWAP_H
+# include <sys/swap.h>
+#endif
+
+#ifndef SWAP_FLAG_DISCARD
+# define SWAP_FLAG_DISCARD 0x10000 /* discard swap cluster after use */
+#endif
+
+#ifndef SWAP_FLAG_PREFER
+# define SWAP_FLAG_PREFER 0x8000 /* set if swap priority specified */
+#endif
+
+#ifndef SWAP_FLAG_PRIO_MASK
+# define SWAP_FLAG_PRIO_MASK 0x7fff
+#endif
+
+#ifndef SWAP_FLAG_PRIO_SHIFT
+# define SWAP_FLAG_PRIO_SHIFT 0
+#endif
+
+#ifndef SWAPON_HAS_TWO_ARGS
+/* libc is insane, let's call the kernel */
+# include <sys/syscall.h>
+# define swapon(path, flags) syscall(SYS_swapon, path, flags)
+#endif
+
+#define QUIET 1
+#define CANONIC 1
+
+#define MAX_PAGESIZE (64 * 1024)
+
+enum {
+ SIG_SWAPSPACE = 1,
+ SIG_SWSUSPEND
+};
+
+#define SWAP_SIGNATURE "SWAPSPACE2"
+#define SWAP_SIGNATURE_SZ (sizeof(SWAP_SIGNATURE) - 1)
+
+static int all;
+static int priority = -1; /* non-prioritized swap by default */
+static int discard;
+
+/* If true, don't complain if the device/file doesn't exist */
+static int ifexists;
+static int fixpgsz;
+static int verbose;
+
+/* 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;
+};
+enum { COL_PATH, COL_TYPE, COL_SIZE, COL_USED, COL_PRIO };
+struct colinfo infos[] = {
+ [COL_PATH] = { "NAME", 0.20, 0, N_("device file or partition path") },
+ [COL_TYPE] = { "TYPE", 0.20, TT_FL_TRUNC, N_("type of the device")},
+ [COL_SIZE] = { "SIZE", 0.20, TT_FL_RIGHT, N_("size of the swap area")},
+ [COL_USED] = { "USED", 0.20, TT_FL_RIGHT, N_("bytes in use")},
+ [COL_PRIO] = { "PRIO", 0.20, TT_FL_RIGHT, N_("swap priority")},
+};
+#define NCOLS ARRAY_SIZE(infos)
+static int columns[NCOLS], ncolumns;
+
+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 add_tt_line(struct tt *tt, struct libmnt_fs *fs, int bytes)
+{
+ int i;
+ struct tt_line *line;
+
+ assert(tt);
+ assert(fs);
+
+ 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;
+ off_t size;
+
+ switch (get_column_id(i)) {
+ case COL_PATH:
+ rc = xasprintf(&str, "%s", mnt_fs_get_source(fs));
+ break;
+ case COL_TYPE:
+ rc = xasprintf(&str, "%s", mnt_fs_get_swaptype(fs));
+ break;
+ case COL_SIZE:
+ size = mnt_fs_get_size(fs);
+ size *= 1024; /* convert to bytes */
+ if (bytes)
+ rc = xasprintf(&str, "%jd", size);
+ else
+ str = size_to_human_string(SIZE_SUFFIX_1LETTER, size);
+ break;
+ case COL_USED:
+ size = mnt_fs_get_usedsize(fs);
+ size *= 1024; /* convert to bytes */
+ if (bytes)
+ rc = xasprintf(&str, "%jd", size);
+ else
+ str = size_to_human_string(SIZE_SUFFIX_1LETTER, size);
+ break;
+ case COL_PRIO:
+ rc = xasprintf(&str, "%d", mnt_fs_get_priority(fs));
+ break;
+ default:
+ break;
+ }
+
+ if (rc || str)
+ tt_line_set_data(line, i, str);
+ }
+ return;
+}
+
+static int display_summary(void)
+{
+ struct libmnt_table *st = get_swaps();
+ struct libmnt_iter *itr;
+ struct libmnt_fs *fs;
+
+ if (!st)
+ return -1;
+
+ itr = mnt_new_iter(MNT_ITER_FORWARD);
+ if (!itr)
+ err(EXIT_FAILURE, _("failed to initialize libmount iterator"));
+
+ if (mnt_table_get_nents(st) > 0)
+ printf(_("%s\t\t\t\tType\t\tSize\tUsed\tPriority\n"), _("Filename"));
+
+ while (mnt_table_next_fs(st, itr, &fs) == 0) {
+ printf("%-39s\t%s\t%jd\t%jd\t%d\n",
+ mnt_fs_get_source(fs),
+ mnt_fs_get_swaptype(fs),
+ mnt_fs_get_size(fs),
+ mnt_fs_get_usedsize(fs),
+ mnt_fs_get_priority(fs));
+ }
+
+ mnt_free_iter(itr);
+ return 0;
+}
+
+static int show_table(int tt_flags, int bytes)
+{
+ struct libmnt_table *st = get_swaps();
+ struct libmnt_iter *itr;
+ struct libmnt_fs *fs;
+
+ int i, rc = 0;
+ struct tt *tt;
+
+ if (!st)
+ return -1;
+
+ itr = mnt_new_iter(MNT_ITER_FORWARD);
+ if (!itr)
+ err(EXIT_FAILURE, _("failed to initialize libmount iterator"));
+
+ 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;
+ }
+ }
+
+ while (mnt_table_next_fs(st, itr, &fs) == 0)
+ add_tt_line(tt, fs, bytes);
+
+ mnt_free_iter(itr);
+ tt_print_table(tt);
+ done:
+ tt_free_table(tt);
+ return rc;
+}
+
+/* calls mkswap */
+static int swap_reinitialize(const char *device,
+ const char *label, const char *uuid)
+{
+ pid_t pid;
+ int status, ret;
+ char *cmd[7];
+ int idx=0;
+
+ warnx(_("%s: reinitializing the swap."), device);
+
+ switch((pid=fork())) {
+ case -1: /* fork error */
+ warn(_("fork failed"));
+ return -1;
+
+ case 0: /* child */
+ cmd[idx++] = PATH_MKSWAP;
+ if (label && *label) {
+ cmd[idx++] = "-L";
+ cmd[idx++] = (char *) label;
+ }
+ if (uuid && *uuid) {
+ cmd[idx++] = "-U";
+ cmd[idx++] = (char *) uuid;
+ }
+ cmd[idx++] = (char *) device;
+ cmd[idx++] = NULL;
+ execv(cmd[0], cmd);
+ err(EXIT_FAILURE, _("execv failed"));
+
+ default: /* parent */
+ do {
+ if ((ret = waitpid(pid, &status, 0)) < 0
+ && errno == EINTR)
+ continue;
+ else if (ret < 0) {
+ warn(_("waitpid failed"));
+ return -1;
+ }
+ } while (0);
+
+ /* mkswap returns: 0=suss, 1=error */
+ if (WIFEXITED(status) && WEXITSTATUS(status)==0)
+ return 0; /* ok */
+ break;
+ }
+ return -1; /* error */
+}
+
+static int swap_rewrite_signature(const char *devname, unsigned int pagesize)
+{
+ int fd, rc = -1;
+
+ fd = open(devname, O_WRONLY);
+ if (fd == -1) {
+ warn(_("cannot open %s"), devname);
+ return -1;
+ }
+
+ if (lseek(fd, pagesize - SWAP_SIGNATURE_SZ, SEEK_SET) < 0) {
+ warn(_("%s: lseek failed"), devname);
+ goto err;
+ }
+
+ if (write(fd, (void *) SWAP_SIGNATURE,
+ SWAP_SIGNATURE_SZ) != SWAP_SIGNATURE_SZ) {
+ warn(_("%s: write signature failed"), devname);
+ goto err;
+ }
+
+ rc = 0;
+err:
+ close(fd);
+ return rc;
+}
+
+static int swap_detect_signature(const char *buf, int *sig)
+{
+ if (memcmp(buf, "SWAP-SPACE", 10) == 0 ||
+ memcmp(buf, "SWAPSPACE2", 10) == 0)
+ *sig = SIG_SWAPSPACE;
+
+ else if (memcmp(buf, "S1SUSPEND", 9) == 0 ||
+ memcmp(buf, "S2SUSPEND", 9) == 0 ||
+ memcmp(buf, "ULSUSPEND", 9) == 0 ||
+ memcmp(buf, "\xed\xc3\x02\xe9\x98\x56\xe5\x0c", 8) == 0 ||
+ memcmp(buf, "LINHIB0001", 10) == 0)
+ *sig = SIG_SWSUSPEND;
+ else
+ return 0;
+
+ return 1;
+}
+
+static char *swap_get_header(int fd, int *sig, unsigned int *pagesize)
+{
+ char *buf;
+ ssize_t datasz;
+ unsigned int page;
+
+ *pagesize = 0;
+ *sig = 0;
+
+ buf = xmalloc(MAX_PAGESIZE);
+
+ datasz = read(fd, buf, MAX_PAGESIZE);
+ if (datasz == (ssize_t) -1)
+ goto err;
+
+ for (page = 0x1000; page <= MAX_PAGESIZE; page <<= 1) {
+ /* skip 32k pagesize since this does not seem to
+ * be supported */
+ if (page == 0x8000)
+ continue;
+ /* the smallest swap area is PAGE_SIZE*10, it means
+ * 40k, that's less than MAX_PAGESIZE */
+ if (datasz < 0 || (size_t) datasz < (page - SWAP_SIGNATURE_SZ))
+ break;
+ if (swap_detect_signature(buf + page - SWAP_SIGNATURE_SZ, sig)) {
+ *pagesize = page;
+ break;
+ }
+ }
+
+ if (*pagesize)
+ return buf;
+err:
+ free(buf);
+ return NULL;
+}
+
+/* returns real size of swap space */
+static unsigned long long swap_get_size(const char *hdr, const char *devname,
+ unsigned int pagesize)
+{
+ unsigned int last_page = 0;
+ int swap_version = 0;
+ int flip = 0;
+ struct swap_header_v1_2 *s;
+
+ s = (struct swap_header_v1_2 *) hdr;
+ if (s->version == 1) {
+ swap_version = 1;
+ last_page = s->last_page;
+ } else if (swab32(s->version) == 1) {
+ flip = 1;
+ swap_version = 1;
+ last_page = swab32(s->last_page);
+ }
+ if (verbose)
+ warnx(_("%s: found swap signature: version %d, "
+ "page-size %d, %s byte order"),
+ devname,
+ swap_version,
+ pagesize / 1024,
+ flip ? _("different") : _("same"));
+
+ return ((unsigned long long) last_page + 1) * pagesize;
+}
+
+static void swap_get_info(const char *hdr, char **label, char **uuid)
+{
+ struct swap_header_v1_2 *s = (struct swap_header_v1_2 *) hdr;
+
+ if (s && *s->volume_name && label)
+ *label = xstrdup(s->volume_name);
+
+ if (s && *s->uuid && uuid) {
+ const unsigned char *u = s->uuid;
+ char str[37];
+
+ snprintf(str, sizeof(str),
+ "%02x%02x%02x%02x-"
+ "%02x%02x-%02x%02x-"
+ "%02x%02x-%02x%02x%02x%02x%02x%02x",
+ u[0], u[1], u[2], u[3],
+ u[4], u[5], u[6], u[7],
+ u[8], u[9], u[10], u[11], u[12], u[13], u[14], u[15]);
+ *uuid = xstrdup(str);
+ }
+}
+
+static int swapon_checks(const char *special)
+{
+ struct stat st;
+ int fd = -1, sig;
+ char *hdr = NULL;
+ unsigned int pagesize;
+ unsigned long long devsize = 0;
+
+ if (stat(special, &st) < 0) {
+ warn(_("stat failed %s"), special);
+ goto err;
+ }
+
+ /* people generally dislike this warning - now it is printed
+ only when `verbose' is set */
+ if (verbose) {
+ int permMask = (S_ISBLK(st.st_mode) ? 07007 : 07077);
+
+ if ((st.st_mode & permMask) != 0)
+ warnx(_("%s: insecure permissions %04o, %04o suggested."),
+ special, st.st_mode & 07777,
+ ~permMask & 0666);
+
+ if (S_ISREG(st.st_mode) && st.st_uid != 0)
+ warnx(_("%s: insecure file owner %d, 0 (root) suggested."),
+ special, st.st_uid);
+ }
+
+ /* test for holes by LBT */
+ if (S_ISREG(st.st_mode)) {
+ if (st.st_blocks * 512 < st.st_size) {
+ warnx(_("%s: skipping - it appears to have holes."),
+ special);
+ goto err;
+ }
+ devsize = st.st_size;
+ }
+
+ fd = open(special, O_RDONLY);
+ if (fd == -1) {
+ warn(_("cannot open %s"), special);
+ goto err;
+ }
+
+ if (S_ISBLK(st.st_mode) && blkdev_get_size(fd, &devsize)) {
+ warn(_("%s: get size failed"), special);
+ goto err;
+ }
+
+ hdr = swap_get_header(fd, &sig, &pagesize);
+ if (!hdr) {
+ warn(_("%s: read swap header failed"), special);
+ goto err;
+ }
+
+ if (sig == SIG_SWAPSPACE && pagesize) {
+ unsigned long long swapsize =
+ swap_get_size(hdr, special, pagesize);
+ int syspg = getpagesize();
+
+ if (verbose)
+ warnx(_("%s: pagesize=%d, swapsize=%llu, devsize=%llu"),
+ special, pagesize, swapsize, devsize);
+
+ if (swapsize > devsize) {
+ if (verbose)
+ warnx(_("%s: last_page 0x%08llx is larger"
+ " than actual size of swapspace"),
+ special, swapsize);
+ } else if (syspg < 0 || (unsigned) syspg != pagesize) {
+ if (fixpgsz) {
+ char *label = NULL, *uuid = NULL;
+ int rc;
+
+ swap_get_info(hdr, &label, &uuid);
+
+ warnx(_("%s: swap format pagesize does not match."),
+ special);
+ rc = swap_reinitialize(special, label, uuid);
+ free(label);
+ free(uuid);
+ if (rc < 0)
+ goto err;
+ } else
+ warnx(_("%s: swap format pagesize does not match. "
+ "(Use --fixpgsz to reinitialize it.)"),
+ special);
+ }
+ } else if (sig == SIG_SWSUSPEND) {
+ /* We have to reinitialize swap with old (=useless) software suspend
+ * data. The problem is that if we don't do it, then we get data
+ * corruption the next time an attempt at unsuspending is made.
+ */
+ warnx(_("%s: software suspend data detected. "
+ "Rewriting the swap signature."),
+ special);
+ if (swap_rewrite_signature(special, pagesize) < 0)
+ goto err;
+ }
+
+ free(hdr);
+ close(fd);
+ return 0;
+err:
+ if (fd != -1)
+ close(fd);
+ free(hdr);
+ return -1;
+}
+
+static int do_swapon(const char *orig_special, int prio,
+ int fl_discard, int canonic)
+{
+ int status;
+ const char *special = orig_special;
+ int flags = 0;
+
+ if (verbose)
+ printf(_("swapon %s\n"), orig_special);
+
+ if (!canonic) {
+ special = mnt_resolve_spec(orig_special, mntcache);
+ if (!special)
+ return cannot_find(orig_special);
+ }
+
+ if (swapon_checks(special))
+ return -1;
+
+#ifdef SWAP_FLAG_PREFER
+ if (prio >= 0) {
+ if (prio > SWAP_FLAG_PRIO_MASK)
+ prio = SWAP_FLAG_PRIO_MASK;
+ flags = SWAP_FLAG_PREFER
+ | ((prio & SWAP_FLAG_PRIO_MASK)
+ << SWAP_FLAG_PRIO_SHIFT);
+ }
+#endif
+ if (fl_discard)
+ flags |= SWAP_FLAG_DISCARD;
+
+ status = swapon(special, flags);
+ if (status < 0)
+ warn(_("%s: swapon failed"), orig_special);
+
+ return status;
+}
+
+static int swapon_by_label(const char *label, int prio, int dsc)
+{
+ const char *special = mnt_resolve_tag("LABEL", label, mntcache);
+ return special ? do_swapon(special, prio, dsc, CANONIC) :
+ cannot_find(label);
+}
+
+static int swapon_by_uuid(const char *uuid, int prio, int dsc)
+{
+ const char *special = mnt_resolve_tag("UUID", uuid, mntcache);
+ return special ? do_swapon(special, prio, dsc, CANONIC) :
+ cannot_find(uuid);
+}
+
+static int swapon_all(void)
+{
+ struct libmnt_table *tb = get_fstab();
+ struct libmnt_iter *itr;
+ struct libmnt_fs *fs;
+ int status = 0;
+
+ if (!tb)
+ err(EXIT_FAILURE, _("failed to parse %s"), mnt_get_fstab_path());
+
+ itr = mnt_new_iter(MNT_ITER_FORWARD);
+ if (!itr)
+ err(EXIT_FAILURE, _("failed to initialize libmount iterator"));
+
+ while (mnt_table_find_next_fs(tb, itr, match_swap, NULL, &fs) == 0) {
+ /* defaults */
+ int pri = priority, dsc = discard, nofail = ifexists;
+ char *p, *src;
+
+ if (mnt_fs_get_option(fs, "noauto", NULL, NULL) == 0)
+ continue;
+ if (mnt_fs_get_option(fs, "discard", NULL, NULL) == 0)
+ dsc = 1;
+ if (mnt_fs_get_option(fs, "nofail", NULL, NULL) == 0)
+ nofail = 1;
+ if (mnt_fs_get_option(fs, "pri", &p, NULL) == 0 && p)
+ pri = atoi(p);
+
+ src = mnt_resolve_spec(mnt_fs_get_source(fs), mntcache);
+ if (!src) {
+ if (!nofail)
+ status |= cannot_find(mnt_fs_get_source(fs));
+ continue;
+ }
+
+ if (!is_active_swap(src) &&
+ (!nofail || !access(src, R_OK)))
+ status |= do_swapon(src, pri, dsc, CANONIC);
+ }
+
+ mnt_free_iter(itr);
+ return status;
+}
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fputs(USAGE_HEADER, out);
+
+ fprintf(out, _(" %s [options] [<spec>]\n"), program_invocation_short_name);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -a, --all enable all swaps from /etc/fstab\n"
+ " -d, --discard discard freed pages before they are reused\n"
+ " -e, --ifexists silently skip devices that do not exist\n"
+ " -f, --fixpgsz reinitialize the swap space if necessary\n"
+ " -p, --priority <prio> specify the priority of the swap device\n"
+ " -s, --summary display summary about used swap devices\n"
+ " --show[=<columns>] display summary in definable table\n"
+ " --noheadings don't print headings, use with --show\n"
+ " --raw use the raw output format, use with --show\n"
+ " --bytes display swap size in bytes in --show output\n"
+ " -v, --verbose verbose mode\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+
+ fputs(_("\nThe <spec> parameter:\n" \
+ " -L <label> synonym for LABEL=<label>\n"
+ " -U <uuid> synonym for UUID=<uuid>\n"
+ " LABEL=<label> specifies device by swap area label\n"
+ " UUID=<uuid> specifies device by swap area UUID\n"
+ " PARTLABEL=<label> specifies device by partition label\n"
+ " PARTUUID=<uuid> specifies device by partition UUID\n"
+ " <device> name of device to be used\n"
+ " <file> name of file to be used\n"), out);
+
+ fputs(_("\nAvailable columns (for --show):\n"), out);
+ for (size_t i = 0; i < NCOLS; i++)
+ fprintf(out, " %4s %s\n", infos[i].name, _(infos[i].help));
+
+ fprintf(out, USAGE_MAN_TAIL("swapon(8)"));
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+ int status = 0, c;
+ int show = 0, tt_flags = 0;
+ int bytes = 0;
+ size_t i;
+
+ enum {
+ SHOW_OPTION = CHAR_MAX + 1,
+ RAW_OPTION,
+ NOHEADINGS_OPTION,
+ BYTES_OPTION
+ };
+
+ static const struct option long_opts[] = {
+ { "priority", 1, 0, 'p' },
+ { "discard", 0, 0, 'd' },
+ { "ifexists", 0, 0, 'e' },
+ { "summary", 0, 0, 's' },
+ { "fixpgsz", 0, 0, 'f' },
+ { "all", 0, 0, 'a' },
+ { "help", 0, 0, 'h' },
+ { "verbose", 0, 0, 'v' },
+ { "version", 0, 0, 'V' },
+ { "show", 2, 0, SHOW_OPTION },
+ { "noheadings", 0, 0, NOHEADINGS_OPTION },
+ { "raw", 0, 0, RAW_OPTION },
+ { "bytes", 0, 0, BYTES_OPTION },
+ { NULL, 0, 0, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ mnt_init_debug(0);
+ mntcache = mnt_new_cache();
+
+ while ((c = getopt_long(argc, argv, "ahdefp:svVL:U:",
+ long_opts, NULL)) != -1) {
+ switch (c) {
+ case 'a': /* all */
+ ++all;
+ break;
+ case 'h': /* help */
+ usage(stdout);
+ break;
+ case 'p': /* priority */
+ priority = atoi(optarg);
+ break;
+ case 'L':
+ add_label(optarg);
+ break;
+ case 'U':
+ add_uuid(optarg);
+ break;
+ case 'd':
+ discard = 1;
+ break;
+ case 'e': /* ifexists */
+ ifexists = 1;
+ break;
+ case 'f':
+ fixpgsz = 1;
+ break;
+ case 's': /* status report */
+ status = display_summary();
+ return status;
+ case 'v': /* be chatty */
+ ++verbose;
+ break;
+ case SHOW_OPTION:
+ if (optarg) {
+ ncolumns = string_to_idarray(optarg,
+ columns,
+ ARRAY_SIZE(columns),
+ column_name_to_id);
+ if (ncolumns < 0)
+ return EXIT_FAILURE;
+ }
+ show = 1;
+ break;
+ case NOHEADINGS_OPTION:
+ tt_flags |= TT_FL_NOHEADINGS;
+ break;
+ case RAW_OPTION:
+ tt_flags |= TT_FL_RAW;
+ break;
+ case BYTES_OPTION:
+ bytes = 1;
+ break;
+ case 'V': /* version */
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 0:
+ break;
+ case '?':
+ default:
+ usage(stderr);
+ }
+ }
+ argv += optind;
+
+ if (show) {
+ if (!ncolumns) {
+ /* default columns */
+ columns[ncolumns++] = COL_PATH;
+ columns[ncolumns++] = COL_TYPE;
+ columns[ncolumns++] = COL_SIZE;
+ columns[ncolumns++] = COL_USED;
+ columns[ncolumns++] = COL_PRIO;
+ }
+ status = show_table(tt_flags, bytes);
+ return status;
+ }
+
+ if (!all && !numof_labels() && !numof_uuids() && *argv == NULL)
+ usage(stderr);
+
+ if (ifexists && !all)
+ usage(stderr);
+
+ if (all)
+ status |= swapon_all();
+
+ for (i = 0; i < numof_labels(); i++)
+ status |= swapon_by_label(get_label(i), priority, discard);
+
+ for (i = 0; i < numof_uuids(); i++)
+ status |= swapon_by_uuid(get_uuid(i), priority, discard);
+
+ while (*argv != NULL)
+ status |= do_swapon(*argv++, priority, discard, !CANONIC);
+
+ free_tables();
+ mnt_free_cache(mntcache);
+
+ return status;
+}
diff --git a/sys-utils/switch_root.8 b/sys-utils/switch_root.8
new file mode 100644
index 0000000..34ab0d0
--- /dev/null
+++ b/sys-utils/switch_root.8
@@ -0,0 +1,61 @@
+.\" Karel Zak <kzak@redhat.com>
+.TH SWITCH_ROOT 8 "June 2009" "util-linux" "System Administration"
+.SH NAME
+switch_root \- switch to another filesystem as the root of the mount tree
+.SH SYNOPSIS
+.B switch_root
+.RB [ \-hV ]
+.LP
+.B switch_root
+.I newroot
+.I init
+.RI [ arg ...]
+.SH DESCRIPTION
+.B switch_root
+moves already mounted /proc, /dev and /sys to
+.I newroot
+and makes
+.I newroot
+the new root filesystem and starts
+.I init
+process.
+
+.B WARNING: switch_root removes recursively all files and directories on the current root filesystem.
+
+.SH OPTIONS
+.IP "\fB\-h, \-\-help\fP"
+show help and exit
+.IP "\fB\-V, \-\-version\fP"
+show version number and exit
+
+.SH RETURN VALUE
+.B switch_root
+returns 0 on success and 1 on failure.
+
+.SH NOTES
+switch_root will fail to function if
+.B newroot
+is not the root of a mount. If you want to switch root into a directory that
+does not meet this requirement then you can first use a bind-mounting trick to
+turn any directory into a mount point:
+.sp
+.nf
+.RS
+mount --bind $DIR $DIR
+.RE
+.fi
+
+.SH "SEE ALSO"
+.BR mount (8)
+.BR chroot (2)
+.BR init (8)
+.BR mkinitrd (8)
+.SH AUTHORS
+.nf
+Peter Jones <pjones@redhat.com>
+Jeremy Katz <katzj@redhat.com>
+Karel Zak <kzak@redhat.com>
+.fi
+.SH AVAILABILITY
+The switch_root command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/sys-utils/switch_root.c b/sys-utils/switch_root.c
new file mode 100644
index 0000000..bcd801d
--- /dev/null
+++ b/sys-utils/switch_root.c
@@ -0,0 +1,225 @@
+/*
+ * switchroot.c - switch to new root directory and start init.
+ *
+ * Copyright 2002-2009 Red Hat, Inc. All rights reserved.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Peter Jones <pjones@redhat.com>
+ * Jeremy Katz <katzj@redhat.com>
+ */
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <dirent.h>
+
+#include "c.h"
+#include "nls.h"
+#include "closestream.h"
+
+#ifndef MS_MOVE
+#define MS_MOVE 8192
+#endif
+
+#ifndef MNT_DETACH
+#define MNT_DETACH 0x00000002 /* Just detach from the tree */
+#endif
+
+/* remove all files/directories below dirName -- don't cross mountpoints */
+static int recursiveRemove(int fd)
+{
+ struct stat rb;
+ DIR *dir;
+ int rc = -1;
+ int dfd;
+
+ if (!(dir = fdopendir(fd))) {
+ warn(_("failed to open directory"));
+ goto done;
+ }
+
+ /* fdopendir() precludes us from continuing to use the input fd */
+ dfd = dirfd(dir);
+
+ if (fstat(dfd, &rb)) {
+ warn(_("stat failed"));
+ goto done;
+ }
+
+ while(1) {
+ struct dirent *d;
+
+ errno = 0;
+ if (!(d = readdir(dir))) {
+ if (errno) {
+ warn(_("failed to read directory"));
+ goto done;
+ }
+ break; /* end of directory */
+ }
+
+ if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ continue;
+
+ if (d->d_type == DT_DIR) {
+ struct stat sb;
+
+ if (fstatat(dfd, d->d_name, &sb, AT_SYMLINK_NOFOLLOW)) {
+ warn(_("stat failed %s"), d->d_name);
+ continue;
+ }
+
+ /* remove subdirectories if device is same as dir */
+ if (sb.st_dev == rb.st_dev) {
+ int cfd;
+
+ cfd = openat(dfd, d->d_name, O_RDONLY);
+ if (cfd >= 0) {
+ recursiveRemove(cfd);
+ close(cfd);
+ }
+ } else
+ continue;
+ }
+
+ if (unlinkat(dfd, d->d_name,
+ d->d_type == DT_DIR ? AT_REMOVEDIR : 0))
+ warn(_("failed to unlink %s"), d->d_name);
+ }
+
+ rc = 0; /* success */
+
+done:
+ if (dir)
+ closedir(dir);
+ return rc;
+}
+
+static int switchroot(const char *newroot)
+{
+ /* Don't try to unmount the old "/", there's no way to do it. */
+ const char *umounts[] = { "/dev", "/proc", "/sys", "/run", NULL };
+ int i;
+ int cfd;
+ pid_t pid;
+ struct stat newroot_stat, sb;
+
+ if (stat(newroot, &newroot_stat) != 0) {
+ warn(_("stat failed %s"), newroot);
+ return -1;
+ }
+
+ for (i = 0; umounts[i] != NULL; i++) {
+ char newmount[PATH_MAX];
+
+ snprintf(newmount, sizeof(newmount), "%s%s", newroot, umounts[i]);
+
+ if ((stat(newmount, &sb) != 0) || (sb.st_dev != newroot_stat.st_dev)) {
+ /* mount point seems to be mounted already or stat failed */
+ umount2(umounts[i], MNT_DETACH);
+ continue;
+ }
+
+ if (mount(umounts[i], newmount, NULL, MS_MOVE, NULL) < 0) {
+ warn(_("failed to mount moving %s to %s"),
+ umounts[i], newmount);
+ warnx(_("forcing unmount of %s"), umounts[i]);
+ umount2(umounts[i], MNT_FORCE);
+ }
+ }
+
+ if (chdir(newroot)) {
+ warn(_("failed to change directory to %s"), newroot);
+ return -1;
+ }
+
+ cfd = open("/", O_RDONLY);
+
+ if (mount(newroot, "/", NULL, MS_MOVE, NULL) < 0) {
+ close(cfd);
+ warn(_("failed to mount moving %s to /"), newroot);
+ return -1;
+ }
+
+ if (chroot(".")) {
+ close(cfd);
+ warn(_("failed to change root"));
+ return -1;
+ }
+
+ if (cfd >= 0) {
+ pid = fork();
+ if (pid <= 0) {
+ recursiveRemove(cfd);
+ if (pid == 0)
+ exit(EXIT_SUCCESS);
+ }
+ close(cfd);
+ }
+ return 0;
+}
+
+static void __attribute__((__noreturn__)) usage(FILE *output)
+{
+ fputs(USAGE_HEADER, output);
+ fprintf(output, _(" %s [options] <newrootdir> <init> <args to init>\n"),
+ program_invocation_short_name);
+ fputs(USAGE_OPTIONS, output);
+ fputs(USAGE_HELP, output);
+ fputs(USAGE_VERSION, output);
+ fprintf(output, USAGE_MAN_TAIL("switch_root(8)"));
+
+ exit(output == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+ char *newroot, *init, **initargs;
+ atexit(close_stdout);
+
+ if (argv[1] && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")))
+ usage(stdout);
+ if (argv[1] && (!strcmp(argv[1], "--version") || !strcmp(argv[1], "-V"))) {
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ }
+ if (argc < 3)
+ usage(stderr);
+
+ newroot = argv[1];
+ init = argv[2];
+ initargs = &argv[2];
+
+ if (!*newroot || !*init)
+ usage(stderr);
+
+ if (switchroot(newroot))
+ errx(EXIT_FAILURE, _("failed. Sorry."));
+
+ if (access(init, X_OK))
+ warn(_("cannot access %s"), init);
+
+ execv(init, initargs);
+ err(EXIT_FAILURE, _("failed to execute %s"), init);
+}
+
diff --git a/sys-utils/tunelp.8 b/sys-utils/tunelp.8
new file mode 100644
index 0000000..444cfb4
--- /dev/null
+++ b/sys-utils/tunelp.8
@@ -0,0 +1,147 @@
+.\" This file Copyright (C) 1992-1997 Michael K. Johnson <johnsonm@redhat.com>
+.\" This file Copyright (C) 1998 Andrea Arcangeli <andrea@e-mind.com>
+.\" It may be distributed under the terms of the GNU General Public License,
+.\" version 2, or any higher version. See section COPYING of the GNU General
+.\" Public license for conditions under which this file may be redistributed.
+.\"
+.\" Polished a bit - aeb
+.TH TUNELP 8 "October 2011" "util-linux" "System Administration"
+.SH NAME
+tunelp \- set various parameters for the lp device
+.SH SYNOPSIS
+.B tunelp
+[options]
+.I device
+.SH DESCRIPTION
+\fBtunelp\fP sets several parameters for the /dev/lp\fI?\fP devices, for
+better performance (or for any performance at all, if your printer won't work
+without it...) Without parameters, it tells whether the device is using
+interrupts, and if so, which one. With parameters, it sets the device
+characteristics accordingly.
+.SH OPTIONS
+.TP
+\fB\-i\fR, \fB\-\-irq\fR \fIargument\fR
+specifies the IRQ to use for the parallel port in question. If this is set
+to something non-zero, \-t and \-c have no effect. If your port does not use
+interrupts, this option will make printing stop. The command
+.B tunelp -i 0
+restores non-interrupt driven (polling) action, and your printer should work
+again. If your parallel port does support interrupts, interrupt-driven
+printing should be somewhat faster and efficient, and will probably be
+desirable.
+.IP
+NOTE: This option will have no effect with kernel 2.1.131 or later since the
+irq is handled by the parport driver. You can change the parport irq for
+example via
+.IR /proc/parport/*/irq .
+Read
+.I /usr/src/linux/Documentation/parport.txt
+for more details on parport.
+.TP
+\fB\-t\fR, \fB\-\-time\fR \fImilliseconds\fR
+is the amount of time in jiffies that the driver waits if the printer doesn't
+take a character for the number of tries dictated by the \-c parameter. 10
+is the default value. If you want fastest possible printing, and don't care
+about system load, you may set this to 0. If you don't care how fast your
+printer goes, or are printing text on a slow printer with a buffer, then 500
+(5 seconds) should be fine, and will give you very low system load. This
+value generally should be lower for printing graphics than text, by a factor
+of approximately 10, for best performance.
+.TP
+\fB\-c\fR, \fB\-\-chars\fR \fIcharacters\fR
+is the number of times to try to output a character to the printer before
+sleeping for \-t \fITIME\fP. It is the number of times around a loop that
+tries to send a character to the printer. 120 appears to be a good value for
+most printers in polling mode. 1000 is the default, because there are some
+printers that become jerky otherwise, but you \fImust\fP set this to `1' to
+handle the maximal CPU efficiency if you are using interrupts. If you have a
+very fast printer, a value of 10 might make more sense even if in polling
+mode. If you have a \fIreally\fP old printer, you can increase this further.
+.IP
+Setting \-t \fITIME\fP to 0 is equivalent to setting \-c \fICHARS\fP to
+infinity.
+.TP
+\fB\-w\fR, \fB\-\-wait\fR \fImilliseconds\fR
+is the number of usec we wait while playing with the strobe signal. While
+most printers appear to be able to deal with an extremely short strobe, some
+printers demand a longer one. Increasing this from the default 1 may make it
+possible to print with those printers. This may also make it possible to use
+longer cables. It's also possible to decrease this value to 0 if your
+printer is fast enough or your machine is slow enough.
+.TP
+\fB\-a\fR, \fB\-\-abort\fR \fI<on|off>\fR
+This is whether to abort on printer error - the default is not to. If you
+are sitting at your computer, you probably want to be able to see an error
+and fix it, and have the printer go on printing. On the other hand, if you
+aren't, you might rather that your printer spooler find out that the printer
+isn't ready, quit trying, and send you mail about it. The choice is yours.
+.TP
+\fB\-o\fR, \fB\-\-check\-status\fR \fI<on|off>\fR
+This option is much like \-a. It makes any
+.I open()
+of this device check to see that the device is on-line and not reporting any
+out of paper or other errors. This is the correct setting for most versions
+of lpd.
+.TP
+\fB\-C\fR, \fB\-\-careful\fR \fI<on|off>\fR
+This option adds extra ("careful") error checking. When this option is on,
+the printer driver will ensure that the printer is on-line and not reporting
+any out of paper or other errors before sending data. This is particularly
+useful for printers that normally appear to accept data when turned off.
+.IP
+NOTE: This option is obsolete because it's the default in 2.1.131 kernel or
+later.
+.TP
+\fB\-s\fR, \fB\-\-status\fR
+This option returns the current printer status, both as a decimal number from
+0..255, and as a list of active flags. When this option is specified, \-q
+off, turning off the display of the current IRQ, is implied.
+.TP
+\fB\-T\fR, \fB\-\-trust\-irq\fR \fI<on|off>\fR
+This option is obsolete. It was added in Linux 2.1.131, and removed again in
+Linux 2.3.10. The below is for these old kernels only.
+.IP
+This option tells the lp driver to trust or not the IRQ. This option makes
+sense only if you are using interrupts. If you tell the lp driver to trust
+the irq, then, when the lp driver will get an irq, it will send the next
+pending character to the printer unconditionally, even if the printer still
+claims to be BUSY. This is the only way to sleep on interrupt (and so the
+handle the irq printing efficiently) at least on Epson Stylus Color Printers.
+The lp driver automagically detects if you could get improved performance by
+setting this flag, and in such case it will warn you with a kernel message.
+.IP
+NOTE: Trusting the irq is reported to corrupt the printing on some hardware,
+you must try to know if your printer will work or not...
+.TP
+\fB\-r\fR, \fB\-\-reset\fR
+This option resets the port. It requires a Linux kernel version of 1.1.80 or
+later.
+.TP
+\fB\-q\fR, \fB\-\-print-irq\fR \fI<on|off>\fR
+This option sets printing the display of the current IRQ setting.
+.SH NOTES
+.BR \-o ,
+.BR \-C ,
+and
+.B \-s
+all require a Linux kernel version of 1.1.76 or later.
+.PP
+.B \-C
+requires a Linux version prior to 2.1.131.
+.PP
+.B \-T
+requires a Linux version of 2.1.131 or later.
+.SH BUGS
+By some unfortunate coincidence the ioctl LPSTRICT of 2.0.36 has the same
+number as the ioctl LPTRUSTIRQ introduced in 2.1.131. So, use of the \-T
+option on a 2.0.36 kernel with an tunelp compiled under 2.1.131 or later may
+have unexpected effects.
+.SH FILES
+.I /dev/lp?
+.br
+.I /proc/parport/*/*
+.SH AVAILABILITY
+The tunelp 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/sys-utils/tunelp.c b/sys-utils/tunelp.c
new file mode 100644
index 0000000..5efd602
--- /dev/null
+++ b/sys-utils/tunelp.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 1992-1997 Michael K. Johnson, johnsonm@redhat.com
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License, version 2, or any later version. See file COPYING for
+ * information on distribution conditions.
+ */
+
+/*
+ * $Log: tunelp.c,v $
+ * Revision 1.9 1998/06/08 19:37:11 janl
+ * Thus compiles tunelp with 2.1.103 kernels
+ *
+ * Revision 1.8 1997/07/06 00:14:06 aebr
+ * Fixes to silence -Wall.
+ *
+ * Revision 1.7 1997/06/20 16:10:38 janl
+ * tunelp refreshed from authors archive.
+ *
+ * Revision 1.9 1997/06/20 12:56:43 johnsonm
+ * Finished fixing license terms.
+ *
+ * Revision 1.8 1997/06/20 12:34:59 johnsonm
+ * Fixed copyright and license.
+ *
+ * Revision 1.7 1995/03/29 11:16:23 johnsonm
+ * TYPO fixed...
+ *
+ * Revision 1.6 1995/03/29 11:12:15 johnsonm
+ * Added third argument to ioctl needed with new kernels
+ *
+ * Revision 1.5 1995/01/13 10:33:43 johnsonm
+ * Chris's changes for new ioctl numbers and backwards compatibility
+ * and the reset ioctl.
+ *
+ * Revision 1.4 1995/01/03 17:42:14 johnsonm
+ * -s isn't supposed to take an argument; removed : after s in getopt...
+ *
+ * Revision 1.3 1995/01/03 07:36:49 johnsonm
+ * Fixed typo
+ *
+ * Revision 1.2 1995/01/03 07:33:44 johnsonm
+ * revisions for lp driver updates in Linux 1.1.76
+ *
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ *
+ * 1999-05-07 Merged LPTRUSTIRQ patch by Andrea Arcangeli (1998/11/29), aeb
+ *
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "lp.h"
+#include "nls.h"
+#include "xalloc.h"
+#include "closestream.h"
+
+#define EXIT_BAD_VALUE 3
+#define EXIT_LP_IO_ERR 4
+
+struct command {
+ long op;
+ long val;
+ struct command *next;
+};
+
+static void __attribute__((__noreturn__)) print_usage(FILE *out)
+{
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(" %s [options] <device>\n"), program_invocation_short_name);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -i, --irq <num> specify parallel port irq\n"), out);
+ fputs(_(" -t, --time <ms> driver wait time in milliseconds\n"), out);
+ fputs(_(" -c, --chars <num> number of output characters before sleep\n"), out);
+ fputs(_(" -w, --wait <us> strobe wait in micro seconds\n"), out);
+ /* TRANSLATORS: do not translate <on|off> arguments. The
+ argument reader does not recognize locale, unless `on' is
+ exactly that very same string. */
+ fputs(_(" -a, --abort <on|off> abort on error\n"), out);
+ fputs(_(" -o, --check-status <on|off> check printer status before printing\n"), out);
+ fputs(_(" -C, --careful <on|off> extra checking to status check\n"), out);
+ fputs(_(" -s, --status query printer status\n"), out);
+ fputs(_(" -T, --trust-irq <on|off> make driver to trust irq\n"), out);
+ fputs(_(" -r, --reset reset the port\n"), out);
+ fputs(_(" -q, --print-irq <on|off> display current irq setting\n"), out);
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ fprintf(out, USAGE_MAN_TAIL("tunelp(8)"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static long get_val(char *val)
+{
+ long ret;
+ if (!(sscanf(val, "%ld", &ret) == 1))
+ errx(EXIT_BAD_VALUE, _("bad value"));
+ return ret;
+}
+
+static long get_onoff(char *val)
+{
+ if (!strncasecmp("on", val, 2))
+ return 1;
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int c, fd, irq, status, show_irq, offset = 0, retval;
+ char *filename;
+ struct stat statbuf;
+ struct command *cmds, *cmdst;
+ static const struct option longopts[] = {
+ {"irq", required_argument, NULL, 'i'},
+ {"time", required_argument, NULL, 't'},
+ {"chars", required_argument, NULL, 'c'},
+ {"wait", required_argument, NULL, 'w'},
+ {"abort", required_argument, NULL, 'a'},
+ {"check-status", required_argument, NULL, 'o'},
+ {"careful", required_argument, NULL, 'C'},
+ {"status", no_argument, NULL, 's'},
+ {"trust-irq", required_argument, NULL, 'T'},
+ {"reset", no_argument, NULL, 'r'},
+ {"print-irq", required_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);
+
+ if (argc < 2)
+ print_usage(stderr);
+
+ cmdst = cmds = xmalloc(sizeof(struct command));
+ cmds->next = 0;
+
+ show_irq = 1;
+ while ((c = getopt_long(argc, argv, "t:c:w:a:i:ho:C:sq:rT:vV", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'h':
+ print_usage(stdout);
+ break;
+ case 'i':
+ cmds->op = LPSETIRQ;
+ cmds->val = get_val(optarg);
+ cmds->next = xmalloc(sizeof(struct command));
+ cmds = cmds->next;
+ cmds->next = 0;
+ break;
+ case 't':
+ cmds->op = LPTIME;
+ cmds->val = get_val(optarg);
+ cmds->next = xmalloc(sizeof(struct command));
+ cmds = cmds->next;
+ cmds->next = 0;
+ break;
+ case 'c':
+ cmds->op = LPCHAR;
+ cmds->val = get_val(optarg);
+ cmds->next = xmalloc(sizeof(struct command));
+ cmds = cmds->next;
+ cmds->next = 0;
+ break;
+ case 'w':
+ cmds->op = LPWAIT;
+ cmds->val = get_val(optarg);
+ cmds->next = xmalloc(sizeof(struct command));
+ cmds = cmds->next;
+ cmds->next = 0;
+ break;
+ case 'a':
+ cmds->op = LPABORT;
+ cmds->val = get_onoff(optarg);
+ cmds->next = xmalloc(sizeof(struct command));
+ cmds = cmds->next;
+ cmds->next = 0;
+ break;
+ case 'q':
+ if (get_onoff(optarg)) {
+ show_irq = 1;
+ } else {
+ show_irq = 0;
+ }
+#ifdef LPGETSTATUS
+ case 'o':
+ cmds->op = LPABORTOPEN;
+ cmds->val = get_onoff(optarg);
+ cmds->next = xmalloc(sizeof(struct command));
+ cmds = cmds->next;
+ cmds->next = 0;
+ break;
+ case 'C':
+ cmds->op = LPCAREFUL;
+ cmds->val = get_onoff(optarg);
+ cmds->next = xmalloc(sizeof(struct command));
+ cmds = cmds->next;
+ cmds->next = 0;
+ break;
+ case 's':
+ show_irq = 0;
+ cmds->op = LPGETSTATUS;
+ cmds->val = 0;
+ cmds->next = xmalloc(sizeof(struct command));
+ cmds = cmds->next;
+ cmds->next = 0;
+ break;
+#endif
+#ifdef LPRESET
+ case 'r':
+ cmds->op = LPRESET;
+ cmds->val = 0;
+ cmds->next = xmalloc(sizeof(struct command));
+ cmds = cmds->next;
+ cmds->next = 0;
+ break;
+#endif
+#ifdef LPTRUSTIRQ
+ case 'T':
+ /* Note: this will do the wrong thing on
+ * 2.0.36 when compiled under 2.2.x
+ */
+ cmds->op = LPTRUSTIRQ;
+ cmds->val = get_onoff(optarg);
+ cmds->next = xmalloc(sizeof(struct command));
+ cmds = cmds->next;
+ cmds->next = 0;
+ break;
+#endif
+ case 'v':
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ default:
+ print_usage(stderr);
+ }
+ }
+
+ if (optind != argc - 1)
+ print_usage(stderr);
+
+ filename = xstrdup(argv[optind]);
+ fd = open(filename, O_WRONLY | O_NONBLOCK, 0);
+ /* Need to open O_NONBLOCK in case ABORTOPEN is already set
+ * and printer is off or off-line or in an error condition.
+ * Otherwise we would abort...
+ */
+ if (fd < 0)
+ err(EXIT_FAILURE, "%s", filename);
+
+ if (fstat(fd, &statbuf))
+ err(EXIT_FAILURE, "%s: stat() failed", filename);
+
+ if (!S_ISCHR(statbuf.st_mode)) {
+ warnx(_("%s not an lp device"), filename);
+ print_usage(stderr);
+ }
+ /* Allow for binaries compiled under a new kernel to work on
+ * the old ones The irq argument to ioctl isn't touched by
+ * the old kernels, but we don't want to cause the kernel to
+ * complain if we are using a new kernel
+ */
+ if (LPGETIRQ >= 0x0600 && ioctl(fd, LPGETIRQ, &irq) < 0
+ && errno == EINVAL)
+ /* We don't understand the new ioctls */
+ offset = 0x0600;
+
+ cmds = cmdst;
+ while (cmds->next) {
+#ifdef LPGETSTATUS
+ if (cmds->op == LPGETSTATUS) {
+ status = 0xdeadbeef;
+ retval = ioctl(fd, LPGETSTATUS - offset, &status);
+ if (retval < 0)
+ warnx(_("LPGETSTATUS error"));
+ else {
+ if (status == (int)0xdeadbeef)
+ /* a few 1.1.7x kernels will do this */
+ status = retval;
+ printf(_("%s status is %d"), filename, status);
+ if (!(status & LP_PBUSY))
+ printf(_(", busy"));
+ if (!(status & LP_PACK))
+ printf(_(", ready"));
+ if ((status & LP_POUTPA))
+ printf(_(", out of paper"));
+ if ((status & LP_PSELECD))
+ printf(_(", on-line"));
+ if (!(status & LP_PERRORP))
+ printf(_(", error"));
+ printf("\n");
+ }
+ } else
+#endif /* LPGETSTATUS */
+ if (ioctl(fd, cmds->op - offset, cmds->val) < 0)
+ warn(_("ioctl failed"));
+ cmdst = cmds;
+ cmds = cmds->next;
+ free(cmdst);
+ }
+
+ if (show_irq) {
+ irq = 0xdeadbeef;
+ retval = ioctl(fd, LPGETIRQ - offset, &irq);
+ if (retval == -1)
+ err(EXIT_LP_IO_ERR, _("LPGETIRQ error"));
+ if (irq == (int)0xdeadbeef)
+ /* up to 1.1.77 will do this */
+ irq = retval;
+ if (irq)
+ printf(_("%s using IRQ %d\n"), filename, irq);
+ else
+ printf(_("%s using polling\n"), filename);
+ }
+ free(filename);
+ close(fd);
+
+ return EXIT_SUCCESS;
+}
diff --git a/sys-utils/umount.8 b/sys-utils/umount.8
new file mode 100644
index 0000000..d073c55
--- /dev/null
+++ b/sys-utils/umount.8
@@ -0,0 +1,175 @@
+.\" Copyright (c) 1996 Andries Brouwer
+.\" This page is somewhat derived from a page that was
+.\" (c) 1980, 1989, 1991 The Regents of the University of California
+.\" and had been heavily modified by Rik Faith and myself.
+.\"
+.\" This is free documentation; 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.
+.\"
+.\" The GNU General Public License's references to "object code"
+.\" and "executables" are to be interpreted as the output of any
+.\" document formatting or typesetting system, including
+.\" intermediate and printed output.
+.\"
+.\" This manual 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.
+.\"
+.TH UMOUNT 8 "August 2012" "util-linux" "System Administration"
+.SH NAME
+umount \- unmount file systems
+.SH SYNOPSIS
+.B umount
+.RB [ \-hV ]
+.LP
+.B umount \-a
+.RB [ \-dflnrv ]
+.RB [ \-t
+.IR vfstype ]
+.RB [ \-O
+.IR options ]
+.br
+.B umount
+.RB [ \-dflnrv ]
+.RI { dir | device }...
+.SH DESCRIPTION
+The
+.B umount
+command detaches the file system(s) mentioned from the file hierarchy. A
+file system is specified by giving the directory where it has been
+mounted. Giving the special device on which the file system lives may
+also work, but is obsolete, mainly because it will fail in case this
+device was mounted on more than one directory.
+.PP
+Note that a file system cannot be unmounted when it is 'busy' - for
+example, when there are open files on it, or when some process has its
+working directory there, or when a swap file on it is in use. The
+offending process could even be
+.B umount
+itself - it opens libc, and libc in its turn may open for example locale
+files. A lazy unmount avoids this problem.
+.SH OPTIONS
+.TP
+\fB\-a\fR, \fB\-\-all\fR
+All of the file systems described in
+.I /etc/mtab
+are unmounted. (With
+.B umount
+version 2.7 and later: the
+.I proc
+filesystem is not unmounted.)
+.TP
+\fB\-c\fR, \fB\-\-no\-canonicalize\fR
+Do not canonicalize paths. For more details about this option see the
+.B mount(8)
+man page.
+.TP
+\fB\-d\fR, \fB\-\-detach\-loop\fR
+In case the unmounted device was a loop device, also free this loop
+device.
+.TP
+\fB\-\-fake\fP
+Causes everything to be done except for the actual system call; this
+'fakes' unmounting the filesystem. It can be used to remove entries from
+.I /etc/mtab
+that were unmounted earlier with the
+.B \-n
+option.
+.TP
+\fB\-f\fR, \fB\-\-force\fR
+Force unmount (in case of an unreachable NFS system). (Requires kernel
+2.1.116 or later.)
+.TP
+\fB\-i\fR, \fB\-\-internal\-only\fR
+Do not call the /sbin/umount.<filesystem> helper even if it exists. By
+default /sbin/umount.<filesystem> helper is called if one exists.
+.TP
+\fB\-n\fR, \fB\-\-no\-mtab\fR
+Unmount without writing in
+.IR /etc/mtab .
+.TP
+\fB\-l\fR, \fB\-\-lazy\fR
+Lazy unmount. Detach the filesystem from the filesystem hierarchy now,
+and cleanup all references to the filesystem as soon as it is not busy
+anymore. (Requires kernel 2.4.11 or later.)
+.TP
+\fB\-O\fR, \fB\-\-test\-opts\fR \fIoptions,list\fR
+Indicate that the actions should only be taken on file systems with the
+specified options in
+.IR /etc/fstab .
+More than one option type may be specified in a comma separated list.
+Each option can be prefixed with
+.B no
+to specify options for which no action should be taken.
+.TP
+\fB\-r\fR, \fB\-\-read\-only\fR
+In case unmounting fails, try to remount read-only.
+.TP
+\fB\-t\fR, \fB\-\-types\fR \fIvfstype,ext2,ext3\fR
+Indicate that the actions should only be taken on file systems of the
+specified
+.IR type .
+More than one type may be specified in a comma separated list. The list
+of file system types can be prefixed with
+.B no
+to specify the file system types on which no action should be taken.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+Verbose mode.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Print help message and exit.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Print version and exit.
+.SH "THE LOOP DEVICE"
+The
+.B umount
+command will free the loop device (if any) associated with the mount, in
+case it finds the option 'loop=...' in
+.IR /etc/mtab ,
+or when the \-d option was given. Any pending loop devices can be freed
+using 'losetup -d', see
+.BR losetup (8).
+.SH NOTES
+The syntax of external umount helpers is:
+.PP
+.BI /sbin/umount. <suffix>
+.RI { dir | device }
+.RB [ \-nlfvr ]
+.RB [ \-t
+.IR type.subtype ]
+.PP
+where the <suffix> is filesystem type or a value from "uhelper=" or
+"helper=" mtab option. The \-t option is used for filesystems with
+subtypes support (for example /sbin/mount.fuse -t fuse.sshfs).
+.PP
+The uhelper= (unprivileged umount helper) is possible to use when
+non-root user wants to umount a mountpoint which is not defined in the
+/etc/fstab file (e.g. devices mounted by udisk).
+.PP
+The helper= mount option redirects all umount requests to the
+/sbin/umount.<helper> independently on UID.
+.SH FILES
+.I /etc/mtab
+table of mounted file systems
+.SH "SEE ALSO"
+.BR umount (2),
+.BR mount (8),
+.BR losetup (8)
+.SH HISTORY
+A
+.B umount
+command appeared in Version 6 AT&T UNIX.
+.SH AVAILABILITY
+The umount 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/sys-utils/umount.c b/sys-utils/umount.c
new file mode 100644
index 0000000..97b2f44
--- /dev/null
+++ b/sys-utils/umount.c
@@ -0,0 +1,399 @@
+/*
+ * umount(8) -- mount a filesystem
+ *
+ * Copyright (C) 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 <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <libmount.h>
+
+#include "nls.h"
+#include "c.h"
+#include "env.h"
+#include "optutils.h"
+#include "exitcodes.h"
+#include "closestream.h"
+
+static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__)),
+ const char *filename, int line)
+{
+ if (filename)
+ warnx(_("%s: parse error: ignore entry at line %d."),
+ filename, line);
+ return 0;
+}
+
+
+static void __attribute__((__noreturn__)) print_version(void)
+{
+ const char *ver = NULL;
+ const char **features = NULL, **p;
+
+ mnt_get_library_version(&ver);
+ mnt_get_library_features(&features);
+
+ printf(_("%s from %s (libmount %s"),
+ program_invocation_short_name,
+ PACKAGE_STRING,
+ ver);
+ p = features;
+ while (p && *p) {
+ fputs(p == features ? ": " : ", ", stdout);
+ fputs(*p++, stdout);
+ }
+ fputs(")\n", stdout);
+ exit(MOUNT_EX_SUCCESS);
+}
+static void __attribute__((__noreturn__)) usage(FILE *out)
+{
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(
+ " %1$s [-hV]\n"
+ " %1$s -a [options]\n"
+ " %1$s [options] <source> | <directory>\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_OPTIONS, out);
+ fprintf(out, _(
+ " -a, --all umount all filesystems\n"
+ " -c, --no-canonicalize don't canonicalize paths\n"
+ " -d, --detach-loop if mounted loop device, also free this loop device\n"
+ " --fake dry run; skip the umount(2) syscall\n"
+ " -f, --force force unmount (in case of an unreachable NFS system)\n"));
+ fprintf(out, _(
+ " -i, --internal-only don't call the umount.<type> helpers\n"
+ " -n, --no-mtab don't write to /etc/mtab\n"
+ " -l, --lazy detach the filesystem now, and cleanup all later\n"));
+ fprintf(out, _(
+ " -O, --test-opts <list> limit the set of filesystems (use with -a)\n"
+ " -r, --read-only In case unmounting fails, try to remount read-only\n"
+ " -t, --types <list> limit the set of filesystem types\n"
+ " -v, --verbose say what is being done\n"));
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ fprintf(out, USAGE_MAN_TAIL("umount(8)"));
+
+ exit(out == stderr ? MOUNT_EX_USAGE : MOUNT_EX_SUCCESS);
+}
+
+static void __attribute__((__noreturn__)) exit_non_root(const char *option)
+{
+ const uid_t ruid = getuid();
+ const uid_t euid = geteuid();
+
+ if (ruid == 0 && euid != 0) {
+ /* user is root, but setuid to non-root */
+ if (option)
+ errx(MOUNT_EX_USAGE,
+ _("only root can use \"--%s\" option "
+ "(effective UID is %u)"),
+ option, euid);
+ errx(MOUNT_EX_USAGE, _("only root can do that "
+ "(effective UID is %u)"), euid);
+ }
+ if (option)
+ errx(MOUNT_EX_USAGE, _("only root can use \"--%s\" option"), option);
+ errx(MOUNT_EX_USAGE, _("only root can do that"));
+}
+
+/*
+ * Handles generic errors like ENOMEM, ...
+ *
+ * rc = 0 success
+ * <0 error (usually -errno)
+ *
+ * Returns exit status (MOUNT_EX_*) and prints error message.
+ */
+static int handle_generic_errors(int rc, const char *msg, ...)
+{
+ va_list va;
+
+ va_start(va, msg);
+ errno = -rc;
+
+ switch(errno) {
+ case EINVAL:
+ case EPERM:
+ vwarn(msg, va);
+ rc = MOUNT_EX_USAGE;
+ break;
+ case ENOMEM:
+ vwarn(msg, va);
+ rc = MOUNT_EX_SYSERR;
+ break;
+ default:
+ vwarn(msg, va);
+ rc = MOUNT_EX_FAIL;
+ break;
+ }
+ va_end(va);
+ return rc;
+}
+
+static int mk_exit_code(struct libmnt_context *cxt, int rc)
+{
+ int syserr;
+ const char *tgt = mnt_context_get_target(cxt);
+
+ if (mnt_context_helper_executed(cxt))
+ /*
+ * /sbin/umount.<type> called, return status
+ */
+ return mnt_context_get_helper_status(cxt);
+
+ if (rc == 0 && mnt_context_get_status(cxt) == 1)
+ /*
+ * Libmount success && syscall success.
+ */
+ return MOUNT_EX_SUCCESS;
+
+
+ if (!mnt_context_syscall_called(cxt)) {
+ /*
+ * libmount errors (extra library checks)
+ */
+ return handle_generic_errors(rc, _("%s: umount failed"), tgt);
+
+ } else if (mnt_context_get_syscall_errno(cxt) == 0) {
+ /*
+ * umount(2) syscall success, but something else failed
+ * (probably error in mtab processing).
+ */
+ if (rc < 0)
+ return handle_generic_errors(rc,
+ _("%s: filesystem umounted, but mount(8) failed"),
+ tgt);
+
+ return MOUNT_EX_SOFTWARE; /* internal error */
+
+ }
+
+ /*
+ * umount(2) errors
+ */
+ syserr = mnt_context_get_syscall_errno(cxt);
+
+ switch(syserr) {
+ case ENXIO:
+ warnx(_("%s: invalid block device"), tgt); /* ??? */
+ break;
+ case EINVAL:
+ warnx(_("%s: not mounted"), tgt);
+ break;
+ case EIO:
+ warnx(_("%s: can't write superblock"), tgt);
+ break;
+ case EBUSY:
+ warnx(_("%s: target is busy.\n"
+ " (In some cases useful info about processes that use\n"
+ " the device is found by lsof(8) or fuser(1))"),
+ tgt);
+ break;
+ case ENOENT:
+ warnx(_("%s: not found"), tgt);
+ break;
+ case EPERM:
+ warnx(_("%s: must be superuser to umount"), tgt);
+ break;
+ case EACCES:
+ warnx(_("%s: block devices not permitted on fs"), tgt);
+ break;
+ default:
+ errno = syserr;
+ warn(_("%s"), tgt);
+ break;
+ }
+ return MOUNT_EX_FAIL;
+}
+
+static int umount_all(struct libmnt_context *cxt)
+{
+ struct libmnt_iter *itr;
+ struct libmnt_fs *fs;
+ int mntrc, ignored, rc = 0;
+
+ itr = mnt_new_iter(MNT_ITER_BACKWARD);
+ if (!itr) {
+ warn(_("failed to initialize libmount iterator"));
+ return -ENOMEM;
+ }
+
+ while (mnt_context_next_umount(cxt, itr, &fs, &mntrc, &ignored) == 0) {
+
+ const char *tgt = mnt_fs_get_target(fs);
+
+ if (ignored) {
+ if (mnt_context_is_verbose(cxt))
+ printf(_("%-25s: ignored\n"), tgt);
+ } else {
+ rc |= mk_exit_code(cxt, mntrc);
+
+ if (mnt_context_is_verbose(cxt))
+ printf("%-25s: successfully umounted\n", tgt);
+ }
+ }
+
+ mnt_free_iter(itr);
+ return rc;
+}
+
+static int umount_one(struct libmnt_context *cxt, const char *spec)
+{
+ int rc;
+
+ if (!spec)
+ return -EINVAL;
+
+ if (mnt_context_set_target(cxt, spec))
+ err(MOUNT_EX_SYSERR, _("failed to set umount target"));
+
+ rc = mnt_context_umount(cxt);
+ rc = mk_exit_code(cxt, rc);
+
+ mnt_reset_context(cxt);
+ return rc;
+}
+
+int main(int argc, char **argv)
+{
+ int c, rc = 0, all = 0;
+ struct libmnt_context *cxt;
+ char *types = NULL;
+
+ enum {
+ UMOUNT_OPT_FAKE = CHAR_MAX + 1,
+ };
+
+ static const struct option longopts[] = {
+ { "all", 0, 0, 'a' },
+ { "detach-loop", 0, 0, 'd' },
+ { "fake", 0, 0, UMOUNT_OPT_FAKE },
+ { "force", 0, 0, 'f' },
+ { "help", 0, 0, 'h' },
+ { "internal-only", 0, 0, 'i' },
+ { "lazy", 0, 0, 'l' },
+ { "no-canonicalize", 0, 0, 'c' },
+ { "no-mtab", 0, 0, 'n' },
+ { "read-only", 0, 0, 'r' },
+ { "test-opts", 1, 0, 'O' },
+ { "types", 1, 0, 't' },
+ { "verbose", 0, 0, 'v' },
+ { "version", 0, 0, 'V' },
+ { NULL, 0, 0, 0 }
+ };
+
+ sanitize_env();
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ mnt_init_debug(0);
+ cxt = mnt_new_context();
+ if (!cxt)
+ err(MOUNT_EX_SYSERR, _("libmount context allocation failed"));
+
+ mnt_context_set_tables_errcb(cxt, table_parser_errcb);
+
+ while ((c = getopt_long(argc, argv, "acdfhilnrO:t:vV",
+ longopts, NULL)) != -1) {
+
+
+ /* only few options are allowed for non-root users */
+ if (mnt_context_is_restricted(cxt) && !strchr("hdilVv", c))
+ exit_non_root(option_to_longopt(c, longopts));
+
+ switch(c) {
+ case 'a':
+ all = 1;
+ break;
+ case 'c':
+ mnt_context_disable_canonicalize(cxt, TRUE);
+ break;
+ case 'd':
+ mnt_context_enable_loopdel(cxt, TRUE);
+ break;
+ case UMOUNT_OPT_FAKE:
+ mnt_context_enable_fake(cxt, TRUE);
+ break;
+ case 'f':
+ mnt_context_enable_force(cxt, TRUE);
+ break;
+ case 'h':
+ usage(stdout);
+ break;
+ case 'i':
+ mnt_context_disable_helpers(cxt, TRUE);
+ break;
+ case 'l':
+ mnt_context_enable_lazy(cxt, TRUE);
+ break;
+ case 'n':
+ mnt_context_disable_mtab(cxt, TRUE);
+ break;
+ case 'r':
+ mnt_context_enable_rdonly_umount(cxt, TRUE);
+ break;
+ case 'O':
+ if (mnt_context_set_options_pattern(cxt, optarg))
+ err(MOUNT_EX_SYSERR, _("failed to set options pattern"));
+ break;
+ case 't':
+ types = optarg;
+ break;
+ case 'v':
+ mnt_context_enable_verbose(cxt, TRUE);
+ break;
+ case 'V':
+ print_version();
+ break;
+ default:
+ usage(stderr);
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (all) {
+ if (!types)
+ types = "noproc,nodevfs,nodevpts,nosysfs,norpc_pipefs,nonfsd";
+
+ mnt_context_set_fstype_pattern(cxt, types);
+ rc = umount_all(cxt);
+
+ } else if (argc < 1) {
+ usage(stderr);
+
+ } else while (argc--)
+ rc += umount_one(cxt, *argv++);
+
+ mnt_free_context(cxt);
+ return rc;
+}
+
diff --git a/sys-utils/unshare.1 b/sys-utils/unshare.1
new file mode 100644
index 0000000..d7938c5
--- /dev/null
+++ b/sys-utils/unshare.1
@@ -0,0 +1,62 @@
+.\" Process this file with
+.\" groff -man -Tascii lscpu.1
+.\"
+.TH UNSHARE 1 "October 2008" "util-linux" "User Commands"
+.SH NAME
+unshare \- run program with some namespaces unshared from parent
+.SH SYNOPSIS
+.B unshare
+.RI [ options ]
+program
+.RI [ arguments ]
+.SH DESCRIPTION
+Unshares specified namespaces from parent process and then executes specified
+program. Unshareable namespaces are:
+.TP
+.BR "mount namespace"
+mounting and unmounting filesystems will not affect rest of the system
+(\fBCLONE_NEWNS\fP flag), except for filesystems which are explicitly marked as
+shared (by mount --make-shared). See /proc/self/mountinfo for the shared flags.
+.TP
+.BR "UTS namespace"
+setting hostname, domainname will not affect rest of the system
+(\fBCLONE_NEWUTS\fP flag).
+.TP
+.BR "IPC namespace"
+process will have independent namespace for System V message queues, semaphore
+sets and shared memory segments (\fBCLONE_NEWIPC\fP flag).
+.TP
+.BR "network namespace"
+process will have independent IPv4 and IPv6 stacks, IP routing tables, firewall
+rules, the \fI/proc/net\fP and \fI/sys/class/net\fP directory trees, sockets
+etc. (\fBCLONE_NEWNET\fP flag).
+.TP
+See the clone(2) for exact semantics of the flags.
+.SH OPTIONS
+.TP
+.BR \-h , " \-\-help"
+Print a help message,
+.TP
+.BR \-m , " \-\-mount"
+Unshare the mount namespace,
+.TP
+.BR \-u , " \-\-uts"
+Unshare the UTC namespace,
+.TP
+.BR \-i , " \-\-ipc"
+Unshare the IPC namespace,
+.TP
+.BR \-n , " \-\-net"
+Unshare the network namespace.
+.SH NOTES
+The unshare command drops potential privileges before executing the
+target program. This allows to setuid unshare.
+.SH SEE ALSO
+unshare(2), clone(2)
+.SH BUGS
+None known so far.
+.SH AUTHOR
+Mikhail Gusarov <dottedmag@dottedmag.net>
+.SH AVAILABILITY
+The unshare command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
new file mode 100644
index 0000000..9de997b
--- /dev/null
+++ b/sys-utils/unshare.c
@@ -0,0 +1,137 @@
+/*
+ * unshare(1) - command-line interface for unshare(2)
+ *
+ * Copyright (C) 2009 Mikhail Gusarov <dottedmag@dottedmag.net>
+ *
+ * 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, 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.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "nls.h"
+#include "c.h"
+#include "closestream.h"
+
+#ifndef CLONE_NEWSNS
+# define CLONE_NEWNS 0x00020000
+#endif
+#ifndef CLONE_NEWUTS
+# define CLONE_NEWUTS 0x04000000
+#endif
+#ifndef CLONE_NEWIPC
+# define CLONE_NEWIPC 0x08000000
+#endif
+#ifndef CLONE_NEWNET
+# define CLONE_NEWNET 0x40000000
+#endif
+
+#ifndef HAVE_UNSHARE
+# include <sys/syscall.h>
+
+static int unshare(int flags)
+{
+ return syscall(SYS_unshare, flags);
+}
+#endif
+
+static void usage(int status)
+{
+ FILE *out = status == EXIT_SUCCESS ? stdout : stderr;
+
+ fputs(USAGE_HEADER, out);
+ fprintf(out,
+ _(" %s [options] <program> [args...]\n"), program_invocation_short_name);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -m, --mount unshare mounts namespace\n"
+ " -u, --uts unshare UTS namespace (hostname etc)\n"
+ " -i, --ipc unshare System V IPC namespace\n"
+ " -n, --net unshare network namespace\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ fprintf(out, USAGE_MAN_TAIL("unshare(1)"));
+
+ exit(status);
+}
+
+int main(int argc, char *argv[])
+{
+ static const struct option longopts[] = {
+ { "help", no_argument, 0, 'h' },
+ { "version", no_argument, 0, 'V'},
+ { "mount", no_argument, 0, 'm' },
+ { "uts", no_argument, 0, 'u' },
+ { "ipc", no_argument, 0, 'i' },
+ { "net", no_argument, 0, 'n' },
+ { NULL, 0, 0, 0 }
+ };
+
+ int unshare_flags = 0;
+
+ int c;
+
+ setlocale(LC_MESSAGES, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while((c = getopt_long(argc, argv, "hVmuin", longopts, NULL)) != -1) {
+ switch(c) {
+ case 'h':
+ usage(EXIT_SUCCESS);
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'm':
+ unshare_flags |= CLONE_NEWNS;
+ break;
+ case 'u':
+ unshare_flags |= CLONE_NEWUTS;
+ break;
+ case 'i':
+ unshare_flags |= CLONE_NEWIPC;
+ break;
+ case 'n':
+ unshare_flags |= CLONE_NEWNET;
+ break;
+ default:
+ usage(EXIT_FAILURE);
+ }
+ }
+
+ if(optind >= argc)
+ usage(EXIT_FAILURE);
+
+ if(-1 == unshare(unshare_flags))
+ err(EXIT_FAILURE, _("unshare failed"));
+
+ /* drop potential root euid/egid if we had been setuid'd */
+ if (setgid(getgid()) < 0)
+ err(EXIT_FAILURE, _("cannot set group id"));
+
+ if (setuid(getuid()) < 0)
+ err(EXIT_FAILURE, _("cannot set user id"));
+
+ execvp(argv[optind], argv + optind);
+
+ err(EXIT_FAILURE, _("exec %s failed"), argv[optind]);
+}
diff --git a/sys-utils/wdctl.8 b/sys-utils/wdctl.8
new file mode 100644
index 0000000..0c9a3ac
--- /dev/null
+++ b/sys-utils/wdctl.8
@@ -0,0 +1,61 @@
+.\" wdctl.8 --
+.\" Copyright (C) 2012 Karel Zak <kzak@redhat.com>
+.\" May be distributed under the GNU General Public License
+.TH WDCTL "8" "June 2012" "util-linux" "System Administration"
+.SH NAME
+wdctl \-
+show hardware watchdog status
+.SH SYNOPSIS
+.B wdctl
+.RB [options]
+.RB [device...]
+.SH DESCRIPTION
+Show hardware watchdog status. The default device is
+.IR /dev/watchdog .
+If more than one device is specified then the output is separated by
+one blank line.
+.PP
+Note that number of supported watchdog features is hardware specific.
+.SH OPTIONS
+.IP "\fB\-f\fR, \fB\-\-flags \fIlist\fP"
+Print selected flags only.
+.IP "\fB\-F\fR, \fB\-\-noflags\fP"
+Do not print information about flags.
+.IP "\fB\-n\fR, \fB\-\-noheadings\fP"
+Do not print a header line for flags table.
+.IP "\fB\-I\fR, \fB\-\-noident\fP"
+Do not print watchdog identity information.
+.IP "\fB\-T\fR, \fB\-\-notimeouts\fP"
+Do not print watchdog timeouts.
+.IP "\fB\-s\fR, \fB\-\-settimeout \fIseconds\fP"
+Set the watchdog timeout in seconds.
+.IP "\fB\-o\fR, \fB\-\-output \fIlist\fP"
+Define the output columns to use in table of watchdog flags. If no
+output arrangement is specified, then a default set is used. Use
+.B \-\-help
+to get list of all supported columns.
+.IP "\fB\-O\fR, \fB\-\-oneline\fP"
+Print all wanted information on one line in key="value" output format.
+.IP "\fB\-V\fR, \fB\-\-version\fP"
+Output version information and exit.
+.IP "\fB\-r\fR, \fB\-\-raw\fP"
+Use the raw output format.
+.IP "\fB\-x\fR, \fB\-\-flags-only\fP"
+Same as \fB\-I \-T\fP.
+.IP "\fB\-h\fR, \fB\-\-help\fP"
+Print a help text and exit.
+.SH AUTHORS
+.MT kzak@\:redhat\:.com
+Karel Zak
+.ME
+.br
+.MT lennart@\:poettering\:.net
+Lennart Poettering
+.ME
+.SH AVAILABILITY
+The
+.B wdctl
+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/sys-utils/wdctl.c b/sys-utils/wdctl.c
new file mode 100644
index 0000000..6be6fd4
--- /dev/null
+++ b/sys-utils/wdctl.c
@@ -0,0 +1,577 @@
+/*
+ * wdctl(8) - show hardware watchdog status
+ *
+ * Copyright (C) 2012 Lennart Poettering
+ * Copyright (C) 2012 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, 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.
+ */
+#include <sys/ioctl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <assert.h>
+#include <linux/watchdog.h>
+
+#include "nls.h"
+#include "c.h"
+#include "closestream.h"
+#include "optutils.h"
+#include "pathnames.h"
+#include "strutils.h"
+#include "tt.h"
+
+struct wdflag {
+ uint32_t flag;
+ const char *name;
+ const char *description;
+};
+
+static const struct wdflag wdflags[] = {
+ { WDIOF_CARDRESET, "CARDRESET", N_("Card previously reset the CPU") },
+ { WDIOF_EXTERN1, "EXTERN1", N_("External relay 1") },
+ { WDIOF_EXTERN2, "EXTERN2", N_("External relay 2") },
+ { WDIOF_FANFAULT, "FANFAULT", N_("Fan failed") },
+ { WDIOF_KEEPALIVEPING, "KEEPALIVEPING", N_("Keep alive ping reply") },
+ { WDIOF_MAGICCLOSE, "MAGICCLOSE", N_("Supports magic close char") },
+ { WDIOF_OVERHEAT, "OVERHEAT", N_("Reset due to CPU overheat") },
+ { WDIOF_POWEROVER, "POWEROVER", N_("Power over voltage") },
+ { WDIOF_POWERUNDER, "POWERUNDER", N_("Power bad/power fault") },
+ { WDIOF_PRETIMEOUT, "PRETIMEOUT", N_("Pretimeout (in seconds)") },
+ { WDIOF_SETTIMEOUT, "SETTIMEOUT", N_("Set timeout (in seconds)") }
+};
+
+
+/* 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;
+};
+
+enum { COL_FLAG, COL_DESC, COL_STATUS, COL_BSTATUS, COL_DEVICE };
+
+/* columns descriptions */
+static struct colinfo infos[] = {
+ [COL_FLAG] = { "FLAG", 14, 0, N_("flag name") },
+ [COL_DESC] = { "DESCRIPTION", 0.1, TT_FL_TRUNC, N_("flag description") },
+ [COL_STATUS] = { "STATUS", 1, TT_FL_RIGHT, N_("flag status") },
+ [COL_BSTATUS] = { "BOOT-STATUS", 1, TT_FL_RIGHT, N_("flag boot status") },
+ [COL_DEVICE] = { "DEVICE", 0.1, 0, N_("watchdog device name") }
+
+};
+
+#define NCOLS ARRAY_SIZE(infos)
+static int columns[NCOLS], ncolumns;
+
+struct wdinfo {
+ char *device;
+
+ int timeout;
+ int timeleft;
+ int pretimeout;
+
+ uint32_t status;
+ uint32_t bstatus;
+
+ struct watchdog_info ident;
+
+ unsigned int has_timeout : 1,
+ has_timeleft : 1,
+ has_pretimeout : 1;
+};
+
+/* converts flag name to flag bit */
+static long name2bit(const char *name, size_t namesz)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(wdflags); i++) {
+ const char *cn = wdflags[i].name;
+ if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
+ return wdflags[i].flag;
+ }
+ warnx(_("unknown flag: %s"), name);
+ return -1;
+}
+
+static int column2id(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 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(unsigned num)
+{
+ return &infos[ get_column_id(num) ];
+}
+
+static void usage(FILE *out)
+{
+ size_t i;
+
+ fputs(USAGE_HEADER, out);
+ fprintf(out,
+ _(" %s [options] [<device> ...]\n"), program_invocation_short_name);
+
+ fputs(USAGE_OPTIONS, out);
+
+ fputs(_(" -f, --flags <list> print selected flags only\n"
+ " -F, --noflags don't print information about flags\n"
+ " -I, --noident don't print watchdog identity information\n"
+ " -n, --noheadings don't print headings for flags table\n"
+ " -O, --oneline print all information on one line\n"
+ " -o, --output <list> output columns of the flags\n"
+ " -r, --raw use raw output format for flags table\n"
+ " -T, --notimeouts don't print watchdog timeouts\n"
+ " -s, --settimeout <sec> set watchdog timeout\n"
+ " -x, --flags-only print only flags table (same as -I -T)\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ fputs(USAGE_SEPARATOR, out);
+
+ fprintf(out, _("The default device is %s.\n"), _PATH_WATCHDOG_DEV);
+ fputs(USAGE_SEPARATOR, out);
+
+ fputs(_("Available columns:\n"), out);
+ for (i = 0; i < ARRAY_SIZE(infos); i++)
+ fprintf(out, " %13s %s\n", infos[i].name, _(infos[i].help));
+
+ fprintf(out, USAGE_MAN_TAIL("wdctl(8)"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static void add_flag_line(struct tt *tt, struct wdinfo *wd, const struct wdflag *fl)
+{
+ int i;
+ struct tt_line *line;
+
+ line = tt_add_line(tt, NULL);
+ if (!line) {
+ warn(_("failed to add line to output"));
+ return;
+ }
+
+ for (i = 0; i < ncolumns; i++) {
+ const char *str = NULL;
+
+ switch (get_column_id(i)) {
+ case COL_FLAG:
+ str = fl->name;
+ break;
+ case COL_DESC:
+ str = fl->description;
+ break;
+ case COL_STATUS:
+ str = wd->status & fl->flag ? "1" : "0";
+ break;
+ case COL_BSTATUS:
+ str = wd->bstatus & fl->flag ? "1" : "0";
+ break;
+ case COL_DEVICE:
+ str = wd->device;
+ break;
+ default:
+ break;
+ }
+
+ if (str)
+ tt_line_set_data(line, i, str);
+ }
+}
+
+static int show_flags(struct wdinfo *wd, int tt_flags, uint32_t wanted)
+{
+ size_t i;
+ int rc = -1;
+ struct tt *tt;
+ uint32_t flags;
+
+ /* create output table */
+ tt = tt_new_table(tt_flags);
+ if (!tt) {
+ warn(_("failed to initialize output table"));
+ return -1;
+ }
+
+ /* define columns */
+ for (i = 0; i < (size_t) 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"));
+ goto done;
+ }
+ }
+
+ /* fill-in table with data
+ * -- one line for each supported flag (option) */
+ flags = wd->ident.options;
+
+ for (i = 0; i < ARRAY_SIZE(wdflags); i++) {
+ if (wanted && !(wanted & wdflags[i].flag))
+ ; /* ignore */
+ else if (flags & wdflags[i].flag)
+ add_flag_line(tt, wd, &wdflags[i]);
+
+ flags &= ~wdflags[i].flag;
+ }
+
+ if (flags)
+ warnx(_("%s: unknown flags 0x%x\n"), wd->device, flags);
+
+ tt_print_table(tt);
+ rc = 0;
+done:
+ tt_free_table(tt);
+ return rc;
+}
+/*
+ * Warning: successfully opened watchdog has to be properly closed with magic
+ * close character otherwise the machine will be rebooted!
+ *
+ * Don't use err() or exit() here!
+ */
+static int set_watchdog(struct wdinfo *wd, int timeout)
+{
+ int fd;
+ sigset_t sigs, oldsigs;
+ int rc = 0;
+
+ assert(wd->device);
+
+ sigemptyset(&oldsigs);
+ sigfillset(&sigs);
+ sigprocmask(SIG_BLOCK, &sigs, &oldsigs);
+
+ fd = open(wd->device, O_WRONLY|O_CLOEXEC);
+
+ if (fd < 0) {
+ if (errno == EBUSY)
+ warnx(_("%s: watchdog already in use, terminating."),
+ wd->device);
+ warn(_("cannot open %s"), wd->device);
+ return -1;
+ }
+
+ for (;;) {
+ /* We just opened this to query the state, not to arm
+ * it hence use the magic close character */
+ static const char v = 'V';
+
+ if (write(fd, &v, 1) >= 0)
+ break;
+ if (errno != EINTR) {
+ warn(_("%s: failed to disarm watchdog"), wd->device);
+ break;
+ }
+ /* Let's try hard, since if we don't get this right
+ * the machine might end up rebooting. */
+ }
+
+ if (ioctl(fd, WDIOC_SETTIMEOUT, &timeout) != 0) {
+ rc = errno;
+ warn(_("cannot set timeout for %s"), wd->device);
+ }
+
+ close(fd);
+ sigprocmask(SIG_SETMASK, &oldsigs, NULL);
+ printf("Set timeout to %d seconds\n", timeout);
+
+ return rc;
+}
+
+/*
+ * Warning: successfully opened watchdog has to be properly closed with magic
+ * close character otherwise the machine will be rebooted!
+ *
+ * Don't use err() or exit() here!
+ */
+static int read_watchdog(struct wdinfo *wd)
+{
+ int fd;
+ sigset_t sigs, oldsigs;
+
+ assert(wd->device);
+
+ sigemptyset(&oldsigs);
+ sigfillset(&sigs);
+ sigprocmask(SIG_BLOCK, &sigs, &oldsigs);
+
+ fd = open(wd->device, O_WRONLY|O_CLOEXEC);
+
+ if (fd < 0) {
+ if (errno == EBUSY)
+ warnx(_("%s: watchdog already in use, terminating."),
+ wd->device);
+ warn(_("cannot open %s"), wd->device);
+ return -1;
+ }
+
+ if (ioctl(fd, WDIOC_GETSUPPORT, &wd->ident) < 0)
+ warn(_("%s: failed to get information about watchdog"), wd->device);
+ else {
+ ioctl(fd, WDIOC_GETSTATUS, &wd->status);
+ ioctl(fd, WDIOC_GETBOOTSTATUS, &wd->bstatus);
+
+ if (ioctl(fd, WDIOC_GETTIMEOUT, &wd->timeout) >= 0)
+ wd->has_timeout = 1;
+ if (ioctl(fd, WDIOC_GETPRETIMEOUT, &wd->pretimeout) >= 0)
+ wd->has_pretimeout = 1;
+ if (ioctl(fd, WDIOC_GETTIMELEFT, &wd->timeleft) >= 0)
+ wd->has_timeleft = 1;
+ }
+
+ for (;;) {
+ /* We just opened this to query the state, not to arm
+ * it hence use the magic close character */
+ static const char v = 'V';
+
+ if (write(fd, &v, 1) >= 0)
+ break;
+ if (errno != EINTR) {
+ warn(_("%s: failed to disarm watchdog"), wd->device);
+ break;
+ }
+ /* Let's try hard, since if we don't get this right
+ * the machine might end up rebooting. */
+ }
+
+ close(fd);
+ sigprocmask(SIG_SETMASK, &oldsigs, NULL);
+
+ return 0;
+}
+
+static void print_oneline(struct wdinfo *wd, uint32_t wanted,
+ int noident, int notimeouts, int noflags)
+{
+ printf("%s:", wd->device);
+
+ if (!noident) {
+ printf(" VERSION=\"%x\"", wd->ident.firmware_version);
+
+ printf(" IDENTITY=");
+ tt_fputs_quoted((char *) wd->ident.identity, stdout);
+ }
+ if (!notimeouts) {
+ if (wd->has_timeout)
+ printf(" TIMEOUT=\"%i\"", wd->timeout);
+ if (wd->has_pretimeout)
+ printf(" PRETIMEOUT=\"%i\"", wd->pretimeout);
+ if (wd->has_timeleft)
+ printf(" TIMELEFT=\"%i\"", wd->timeleft);
+ }
+
+ if (!noflags) {
+ size_t i;
+ uint32_t flags = wd->ident.options;
+
+ for (i = 0; i < ARRAY_SIZE(wdflags); i++) {
+ const struct wdflag *fl;
+
+ if ((wanted && !(wanted & wdflags[i].flag)) ||
+ !(flags & wdflags[i].flag))
+ continue;
+
+ fl= &wdflags[i];
+
+ printf(" %s=\"%s\"", fl->name,
+ wd->status & fl->flag ? "1" : "0");
+ printf(" %s_BOOT=\"%s\"", fl->name,
+ wd->bstatus & fl->flag ? "1" : "0");
+
+ }
+ }
+
+ fputc('\n', stdout);
+}
+
+static void show_timeouts(struct wdinfo *wd)
+{
+ if (wd->has_timeout)
+ printf(_("%-15s%2i seconds\n"), _("Timeout:"), wd->timeout);
+ if (wd->has_pretimeout)
+ printf(_("%-15s%2i seconds\n"), _("Pre-timeout:"), wd->pretimeout);
+ if (wd->has_timeleft)
+ printf(_("%-15s%2i seconds\n"), _("Timeleft:"), wd->timeleft);
+}
+
+int main(int argc, char *argv[])
+{
+ struct wdinfo wd;
+ int c, tt_flags = 0, res = EXIT_SUCCESS, count = 0;
+ char noflags = 0, noident = 0, notimeouts = 0, oneline = 0;
+ uint32_t wanted = 0;
+ int timeout = 0;
+
+ static const struct option long_opts[] = {
+ { "flags", required_argument, NULL, 'f' },
+ { "flags-only", no_argument, NULL, 'x' },
+ { "help", no_argument, NULL, 'h' },
+ { "noflags", no_argument, NULL, 'F' },
+ { "noheadings", no_argument, NULL, 'n' },
+ { "noident", no_argument, NULL, 'I' },
+ { "notimeouts", no_argument, NULL, 'T' },
+ { "settimeout", required_argument, NULL, 's' },
+ { "output", required_argument, NULL, 'o' },
+ { "oneline", no_argument, NULL, 'O' },
+ { "raw", no_argument, NULL, 'r' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */
+ { 'F','f' }, /* noflags,flags*/
+ { 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,
+ "d:f:hFnITo:s:OrVx", long_opts, NULL)) != -1) {
+
+ err_exclusive_options(c, long_opts, excl, excl_st);
+
+ switch(c) {
+ case 'o':
+ ncolumns = string_to_idarray(optarg,
+ columns, ARRAY_SIZE(columns),
+ column2id);
+ if (ncolumns < 0)
+ return EXIT_FAILURE;
+ break;
+ case 's':
+ timeout = strtos32_or_err(optarg, _("invalid timeout argument"));
+ break;
+ case 'f':
+ if (string_to_bitmask(optarg, (unsigned long *) &wanted, name2bit) != 0)
+ return EXIT_FAILURE;
+ break;
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ case 'F':
+ noflags = 1;
+ break;
+ case 'I':
+ noident = 1;
+ break;
+ case 'T':
+ notimeouts = 1;
+ break;
+ case 'n':
+ tt_flags |= TT_FL_NOHEADINGS;
+ break;
+ case 'r':
+ tt_flags |= TT_FL_RAW;
+ break;
+ case 'O':
+ oneline = 1;
+ break;
+ case 'x':
+ noident = 1;
+ notimeouts = 1;
+ break;
+
+ case '?':
+ default:
+ usage(stderr);
+ }
+ }
+
+ if (!ncolumns) {
+ /* default columns */
+ columns[ncolumns++] = COL_FLAG;
+ columns[ncolumns++] = COL_DESC;
+ columns[ncolumns++] = COL_STATUS;
+ columns[ncolumns++] = COL_BSTATUS;
+ }
+
+ do {
+ int rc;
+
+ memset(&wd, 0, sizeof(wd));
+
+ if (optind == argc)
+ wd.device = _PATH_WATCHDOG_DEV;
+ else
+ wd.device = argv[optind++];
+
+ if (count)
+ fputc('\n', stdout);
+ count++;
+
+ if (timeout) {
+ rc = set_watchdog(&wd, timeout);
+ if (rc) {
+ res = EXIT_FAILURE;
+ }
+ }
+
+ rc = read_watchdog(&wd);
+ if (rc) {
+ res = EXIT_FAILURE;
+ continue;
+ }
+
+ if (oneline) {
+ print_oneline(&wd, wanted, noident, notimeouts, noflags);
+ continue;
+ }
+
+ /* pretty output */
+ if (!noident) {
+ printf("%-15s%s\n", _("Device:"), wd.device);
+ printf(_("%-15s%s [version %x]\n"),
+ ("Identity:"),
+ wd.ident.identity,
+ wd.ident.firmware_version);
+ }
+ if (!notimeouts)
+ show_timeouts(&wd);
+ if (!noflags)
+ show_flags(&wd, tt_flags, wanted);
+ } while (optind < argc);
+
+ return res;
+}