diff options
Diffstat (limited to 'sys-utils')
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; +} |