SubProjects = alias apply basename chroot date dirname echo env expr false \
find getopt hostname id jot kill killall lastcomm locate logname mktemp \
- nice nohup printenv printf pwd renice script shlock sleep \
+ nice nohup path_helper printenv printf pwd renice script shlock sleep \
tee test time true uname users w whereis which who xargs yes
ifeq ($(Embedded),NO)
#!/bin/sh
# $FreeBSD: src/usr.bin/alias/generic.sh,v 1.2 2005/10/24 22:32:19 cperciva Exp $
# This file is in the public domain.
-builtin ${0##*/} ${1+"$@"}
+builtin `echo ${0##*/} | tr \[:upper:] \[:lower:]` ${1+"$@"}
sets the date to
.Dq Li "June 13, 1985, 4:27 PM" .
.Pp
-.Dl "date ""+%Y%m%d%H%M.%S"""
+.Dl "date ""+%m%d%H%M%Y.%S"""
.Pp
may be used on one machine to print out the date
suitable for setting on another.
-.Qq ( Li "+%m%d%H%M%Y.%S"
-for use on
-.Tn Linux . )
.Pp
The command:
.Pp
.It 2
Able to set the local date, but unable to set it globally
.El
+.Pp
+For more information about legacy mode, see
+.Xr compat 5 .
.Sh SEE ALSO
.Xr gettimeofday 2 ,
.Xr strftime 3 ,
YFILES = getdate.y
MANPAGES = find.1
-Extra_CC_Flags = -Wall -mdynamic-no-pic \
+Extra_CC_Flags = -Wall -Werror -mdynamic-no-pic \
-D__FBSDID=__RCSID -D_DARWIN_USE_64_BIT_INODE
Extra_LD_Flags = -dead_strip
* SUCH DAMAGE.
*
* @(#)extern.h 8.3 (Berkeley) 4/16/94
- * $FreeBSD: src/usr.bin/find/extern.h,v 1.23 2006/05/14 20:23:00 krion Exp $
+ * $FreeBSD: src/usr.bin/find/extern.h,v 1.24 2008/02/23 16:29:04 imp Exp $
*/
#include <sys/cdefs.h>
creat_f c_perm;
creat_f c_print;
creat_f c_regex;
+creat_f c_samefile;
creat_f c_simple;
creat_f c_size;
creat_f c_type;
exec_f f_empty;
exec_f f_exec;
exec_f f_expr;
+exec_f f_false;
exec_f f_flags;
exec_f f_fstype;
exec_f f_group;
exec_f f_print;
exec_f f_print0;
exec_f f_prune;
+exec_f f_quit;
exec_f f_regex;
exec_f f_size;
exec_f f_type;
.\" SUCH DAMAGE.
.\"
.\" @(#)find.1 8.7 (Berkeley) 5/9/95
-.\" $FreeBSD: src/usr.bin/find/find.1,v 1.81 2006/12/13 17:02:50 ru Exp $
+.\" $FreeBSD: src/usr.bin/find/find.1,v 1.86 2008/03/03 08:32:58 ru Exp $
.\"
-.Dd December 13, 2006
+.Dd February 24, 2008
.Dt FIND 1
.Os
.Sh NAME
.Nm
.Op Fl H | Fl L | Fl P
.Op Fl EXdsx
-.Op Fl f Ar pathname
-.Ar pathname ...
-.Ar expression
+.Op Fl f Ar path
+.Ar path ...
+.Op Ar expression
.Nm
.Op Fl H | Fl L | Fl P
.Op Fl EXdsx
-.Fl f Ar pathname
-.Op Ar pathname ...
-.Ar expression
+.Fl f Ar path
+.Op Ar path ...
+.Op Ar expression
.Sh DESCRIPTION
The
.Nm
utility recursively descends the directory tree for each
-.Ar pathname
+.Ar path
listed, evaluating an
.Ar expression
(composed of the
.Ic -regex
and
.Ic -iregex
-options as extended (modern) regular expressions rather than basic
+primaries as extended (modern) regular expressions rather than basic
regular expressions (BRE's).
The
.Xr re_format 7
.Ic -depth
primary of
.St -p1003.1-2001 .
+The
.Fl d
+option
can be useful when
.Nm
is used with
.Ic -atime
primary description for information on supported time units.
.\" .It Ic -acl
-.\" May be used in conjunction with other options to locate
+.\" May be used in conjunction with other primaries to locate
.\" files with extended ACLs.
.\" See
.\" .Xr acl 3
Please refer to the
.Ic -atime
primary description for information on supported time units.
+.It Ic -d
+Same as
+.Ic depth .
+GNU find implements this as a primary in mistaken emulation of
+.Fx
+.Xr find 1 .
.It Ic -delete
Delete found files and/or directories.
Always returns true.
the string
.Dq Li {}
is not qualified.
+.It Ic -execdir Ar utility Oo Ar argument ... Oc Li {} +
+Same as
+.Ic -execdir ,
+except that
+.Dq Li {}
+is replaced with as many pathnames as possible for each invocation of
+.Ar utility .
+This behaviour is similar to that of
+.Xr xargs 1 .
.It Ic -flags Oo Cm - Ns | Ns Cm + Oc Ns Ar flags , Ns Ar notflags
The flags are specified using symbolic names (see
.Xr chflags 1 ) .
.Nm
is being executed and the latter matches any file system which is
mounted read-only.
+.It Ic -gid Ar gname
+The same thing as
+.Ar -group Ar gname
+for compatibility with GNU find.
+GNU find imposes a restriction that
+.Ar gname
+is numeric, while
+.Xr find 1
+does not.
.It Ic -group Ar gname
True if the file belongs to the group
.Ar gname .
is numeric and there is no such group name, then
.Ar gname
is treated as a group ID.
+.It Ic -ignore_readdir_race
+This option is for GNU find compatibility and is ignored.
+.It Ic -ilname Ar pattern
+Like
+.Ic -lname ,
+but the match is case insensitive.
+This is a GNU find extension.
.It Ic -iname Ar pattern
Like
.Ic -name ,
Like
.Ic -regex ,
but the match is case insensitive.
+.It Ic -iwholename Ar pattern
+The same thing as
+.Ic -ipath ,
+for GNU find compatibility.
.It Ic -links Ar n
True if the file has
.Ar n
links.
+.It Ic -lname Ar pattern
+Like
+.Ic -name ,
+but the contents of the symbolic link are matched instead of the file
+name.
+This is a GNU find extension.
.It Ic -ls
This primary always evaluates to true.
The following information for the current file is written to standard output:
.Dq Li -> .
The format is identical to that produced by
.Bk -words
-.Nm ls Fl dgils .
+.Dq Nm ls Fl dgils .
.Ek
.It Ic -maxdepth Ar n
Always true; descend at most
.Ic -maxdepth
primary is specified, it applies to the entire expression even if it would
not normally be evaluated.
-.Ic -maxdepth Li 0
+.Dq Ic -maxdepth Li 0
limits the whole search to the command line arguments.
.It Ic -mindepth Ar n
Always true; do not apply any tests or actions at levels less than
.Ic -mindepth
primary is specified, it applies to the entire expression even if it would
not normally be evaluated.
-.Ic -mindepth Li 1
+.Dq Ic -mindepth Li 1
processes all but the command line arguments.
.It Ic -mmin Ar n
True if the difference between the file last modification time and the time
.It Ic -mnewer Ar file
Same as
.Ic -newer .
+.It Ic -mount
+The same thing as
+.Ic -xdev ,
+for GNU find compatibility.
.It Ic -mtime Ar n Ns Op Cm smhdw
If no units are specified, this primary evaluates to
true if the difference between the file last modification time and the time
.Ar file .
.It Ic -newer Ns Ar X Ns Ar Y Ar file
True if the current file has a more recent last access time
-.Ar ( X Ns = Ns Cm a ) ,
+.Pq Ar X Ns = Ns Cm a ,
inode creation time
-.Ar ( X Ns = Ns Cm B ) ,
+.Pq Ar X Ns = Ns Cm B ,
change time
-.Ar ( X Ns = Ns Cm c ) ,
+.Pq Ar X Ns = Ns Cm c ,
or modification time
-.Ar ( X Ns = Ns Cm m )
+.Pq Ar X Ns = Ns Cm m
than the last access time
-.Ar ( Y Ns = Ns Cm a ) ,
+.Pq Ar Y Ns = Ns Cm a ,
inode creation time
-.Ar ( Y Ns = Ns Cm B ) ,
+.Pq Ar Y Ns = Ns Cm B ,
change time
-.Ar ( Y Ns = Ns Cm c ) ,
+.Pq Ar Y Ns = Ns Cm c ,
or modification time
-.Ar ( Y Ns = Ns Cm m )
+.Pq Ar Y Ns = Ns Cm m
of
.Ar file .
In addition, if
.Ic -newer .
.It Ic -nogroup
True if the file belongs to an unknown group.
+.It Ic -noignore_readdir_race
+This option is for GNU find compatibility and is ignored.
+.It Ic -noleaf
+This option is for GNU find compatibility.
+In GNU find it disables an optimization not relevant to
+.Xr find 1 ,
+so it is ignored.
.It Ic -nouser
True if the file belongs to an unknown user.
.It Ic -ok Ar utility Oo Ar argument ... Oc Li \&;
.Dq Li xyzzy
or
.Dq Li /foo/ .
+.It Ic -samefile Ar name
+True if the file is a hard link to
+.Ar name .
+If the command option
+.Ic -L
+is specified, it is also true if the file is a symbolic link and
+points to
+.Ar name .
.It Ic -size Ar n Ns Op Cm ckMGTP
True if the file's size, rounded up, in 512-byte blocks is
.Ar n .
.It Cm P
petabytes (1024 terabytes)
.El
-.Pp
.It Ic -type Ar t
True if the file is of the specified type.
Possible file types are as follows:
.It Cm s
socket
.El
+.It Ic -uid Ar uname
+The same thing as
+.Ar -user Ar uname
+for compatibility with GNU find.
+GNU find imposes a restriction that
+.Ar uname
+is numeric, while
+.Xr find 1
+does not.
.It Ic -user Ar uname
True if the file belongs to the user
.Ar uname .
is numeric and there is no such user name, then
.Ar uname
is treated as a user ID.
+.It Ic -wholename Ar pattern
+The same thing as
+.Ic -path ,
+for GNU find compatibility.
.El
.Pp
All primaries which take a numeric argument allow the number to be
The primaries may be combined using the following operators.
The operators are listed in order of decreasing precedence.
.Pp
-.Bl -tag -width "( expression )" -compact
+.Bl -tag -width indent -compact
.It Cm \&( Ar expression Cm \&)
This evaluates to true if the parenthesized expression evaluates to
true.
.Pp
.It Cm \&! Ar expression
-.It Cm -false Ar expression
.It Cm -not Ar expression
This is the unary
.Tn NOT
operator.
It evaluates to true if the expression is false.
.Pp
+.It Cm -false
+Always false.
+.It Cm -true
+Always true.
+.Pp
.It Ar expression Cm -and Ar expression
.It Ar expression expression
The
standard.
.Pp
All the single character options except
-.Ic -H
+.Fl H
and
-.Ic -L
+.Fl L
as well as
.Ic -amin , -anewer , -cmin , -cnewer , -delete , -empty , -fstype ,
.Ic -iname , -inum , -iregex , -ls , -maxdepth , -mindepth , -mmin ,
.Ic -path , -print0 , -regex
and all of the
-.Ic -B
+.Ic -B*
birthtime related primaries are extensions to
.St -p1003.1-2001 .
.Pp
#include "find.h"
+#ifdef __APPLE__
+static int find_compare(const FTSENT **s1, const FTSENT **s2);
+#else /* !__APPLE__ */
static int find_compare(const FTSENT * const *s1, const FTSENT * const *s2);
+#endif /* __APPLE__ */
/*
* find_compare --
* order within each directory.
*/
static int
+#ifdef __APPLE__
+find_compare(const FTSENT **s1, const FTSENT **s2)
+#else /* !__APPLE__ */
find_compare(const FTSENT * const *s1, const FTSENT * const *s2)
+#endif /* __APPLE__ */
{
return (strcoll((*s1)->fts_name, (*s2)->fts_name));
if (COMPAT_MODE("bin/find", "unix2003") && getuid()
&& stat_ret == 0
&& ((statInfo.st_mode & S_IFMT) == S_IFDIR)) {
- if ((statInfo.st_mode & (S_IXUSR + S_IXGRP + S_IXOTH)) != 0) {
+ if (access(paths[pathIndex], X_OK) == 0) {
myPaths = addPath(myPaths, paths[pathIndex]);
} else {
if (stat_errno != ENAMETOOLONG) { /* if name is too long, just let existing logic handle it */
* SUCH DAMAGE.
*
* @(#)find.h 8.1 (Berkeley) 6/6/93
- * $FreeBSD: src/usr.bin/find/find.h,v 1.19 2006/05/14 20:23:01 krion Exp $
+ * $FreeBSD: src/usr.bin/find/find.h,v 1.20 2008/02/23 16:29:04 imp Exp $
*/
#include <regex.h>
#define F_EXECPLUS 0x00020000 /* -exec ... {} + */
#define F_TIME_B 0x00040000 /* one of -Btime, -Bnewer, -newerB* */
#define F_TIME2_B 0x00080000 /* one of -newer?B */
+#define F_LINK 0x00100000 /* lname or ilname */
/* node definition */
typedef struct _plandata {
#endif /* not lint */
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/usr.bin/find/function.c,v 1.58 2006/05/27 18:27:41 krion Exp $");
+__FBSDID("$FreeBSD: src/usr.bin/find/function.c,v 1.60 2008/02/24 00:01:06 imp Exp $");
#include <sys/param.h>
#include <sys/ucred.h>
#ifdef __APPLE__
#include <sys/sysctl.h>
+#include <libgen.h>
#include <get_compat.h>
#else
#define COMPAT_MODE(func, mode) 1
else
xtime = entry->fts_statp->st_mtime;
- if (COMPAT_MODE("bin/find", "unix2003") || plan->flags & F_EXACTTIME)
- COMPARE((now - xtime) / 86400, plan->t_data);
+ if (plan->flags & F_EXACTTIME)
+ COMPARE(now - xtime, plan->t_data);
else
- COMPARE((now - xtime + 86400 - 1) / 86400, plan->t_data);
+ COMPARE((now - xtime + (COMPAT_MODE("bin/find", "unix2003") ? 0 : 86400 - 1)) / 86400, plan->t_data);
}
PLAN *
new = palloc(option);
new->t_data = find_parsetime(new, option->name, value);
- if (!(new->flags & F_EXACTTIME))
+ if (!(new->flags & F_EXACTTIME) && !COMPAT_MODE("bin/find", "unix2003"))
TIME_CORRECT(new);
return new;
}
/*
* always_true --
*
- * Always true, used for -maxdepth, -mindepth, -xdev and -follow
+ * Always true, used for -maxdepth, -mindepth, -xdev, -follow, and -true
*/
int
f_always_true(PLAN *plan __unused, FTSENT *entry __unused)
g = getgrnam(gname);
if (g == NULL) {
char* cp = gname;
- if( gname[0] == '-' || gname[0] == '+' )
+ if (gname[0] == '-' || gname[0] == '+')
gname++;
gid = atoi(gname);
if (gid == 0 && gname[0] != '0')
return new;
}
+/*
+ * -samefile FN
+ *
+ * True if the file has the same inode (eg hard link) FN
+ */
+
+/* f_samefile is just f_inum */
+PLAN *
+c_samefile(OPTION *option, char ***argvp)
+{
+ char *fn;
+ PLAN *new;
+ struct stat sb;
+
+ fn = nextarg(option, argvp);
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(option);
+ if (stat(fn, &sb))
+ err(1, "%s", fn);
+ new->i_data = sb.st_ino;
+ return new;
+}
+
/*
* -links n functions --
*
int
f_name(PLAN *plan, FTSENT *entry)
{
- return !fnmatch(plan->c_data, entry->fts_name,
+ char fn[PATH_MAX];
+ const char *name;
+
+ if (plan->flags & F_LINK) {
+ name = fn;
+ if (readlink(entry->fts_path, fn, sizeof(fn)) == -1)
+ return 0;
+ } else if (entry->fts_namelen == 0) {
+ name = basename(entry->fts_path);
+ } else
+ name = entry->fts_name;
+ return !fnmatch(plan->c_data, name,
plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0);
}
return new;
}
-/* c_simple covers c_prune, c_openparen, c_closeparen, c_not, c_or */
+/* c_simple covers c_prune, c_openparen, c_closeparen, c_not, c_or, c_true, c_false */
PLAN *
c_simple(OPTION *option, char ***argvp __unused)
}
/* c_or == c_simple */
+
+/*
+ * -false
+ *
+ * Always false.
+ */
+int
+f_false(PLAN *plan __unused, FTSENT *entry __unused)
+{
+ return 0;
+}
+
+/* c_false == c_simple */
+
+/*
+ * -quit
+ *
+ * Exits the program
+ */
+int
+f_quit(PLAN *plan __unused, FTSENT *entry __unused)
+{
+ exit(0);
+}
+
+/* c_quit == c_simple */
#include <time.h>
#include <unistd.h>
#ifdef __APPLE__
-#include <utmp.h>
+/* definition from utmp.h */
+#define UT_NAMESIZE 8
#endif /* __APPLE__ */
#include "find.h"
#endif /* not lint */
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/usr.bin/find/main.c,v 1.15 2003/06/14 13:00:21 markm Exp $");
+__FBSDID("$FreeBSD: src/usr.bin/find/main.c,v 1.16 2008/03/03 08:32:58 ru Exp $");
#include <sys/types.h>
#include <sys/stat.h>
static void
usage(void)
{
- (void)fprintf(stderr,
-"usage: find [-H | -L | -P] [-EXdsx] [-f file] [file ...] [expression]\n");
+ (void)fprintf(stderr, "%s\n%s\n",
+"usage: find [-H | -L | -P] [-EXdsx] [-f path] path ... [expression]",
+" find [-H | -L | -P] [-EXdsx] -f path [path ...] [expression]");
exit(1);
}
#endif /* not lint */
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/usr.bin/find/option.c,v 1.25 2006/04/05 23:06:11 ceri Exp $");
+__FBSDID("$FreeBSD: src/usr.bin/find/option.c,v 1.26 2008/02/23 16:29:04 imp Exp $");
#include <sys/types.h>
#include <sys/stat.h>
int typecompare(const void *, const void *);
/* NB: the following table must be sorted lexically. */
+/* Options listed with C++ comments are in gnu find, but not our find */
static OPTION const options[] = {
{ "!", c_simple, f_not, 0 },
{ "(", c_simple, f_openparen, 0 },
{ "-cmin", c_Xmin, f_Xmin, F_TIME_C },
{ "-cnewer", c_newer, f_newer, F_TIME_C },
{ "-ctime", c_Xtime, f_Xtime, F_TIME_C },
+ { "-d", c_depth, f_depth, 0 },
+// -daystart
{ "-delete", c_delete, f_delete, 0 },
{ "-depth", c_depth, f_depth, 0 },
{ "-empty", c_empty, f_empty, 0 },
{ "-exec", c_exec, f_exec, 0 },
{ "-execdir", c_exec, f_exec, F_EXECDIR },
- { "-false", c_simple, f_not, 0 },
+ { "-false", c_simple, f_false, 0 },
{ "-flags", c_flags, f_flags, 0 },
+// -fls
{ "-follow", c_follow, f_always_true, 0 },
+// -fprint
+// -fprint0
+// -fprintf
{ "-fstype", c_fstype, f_fstype, 0 },
+ { "-gid", c_group, f_group, 0 },
{ "-group", c_group, f_group, 0 },
+ { "-ignore_readdir_race",c_simple, f_always_true,0 },
+ { "-ilname", c_name, f_name, F_LINK | F_IGNCASE },
{ "-iname", c_name, f_name, F_IGNCASE },
{ "-inum", c_inum, f_inum, 0 },
{ "-ipath", c_name, f_path, F_IGNCASE },
{ "-iregex", c_regex, f_regex, F_IGNCASE },
+ { "-iwholename",c_name, f_path, F_IGNCASE },
{ "-links", c_links, f_links, 0 },
+ { "-lname", c_name, f_name, F_LINK },
{ "-ls", c_ls, f_ls, 0 },
{ "-maxdepth", c_mXXdepth, f_always_true, F_MAXDEPTH },
{ "-mindepth", c_mXXdepth, f_always_true, 0 },
{ "-mmin", c_Xmin, f_Xmin, 0 },
{ "-mnewer", c_newer, f_newer, 0 },
+ { "-mount", c_xdev, f_always_true, 0 },
{ "-mtime", c_Xtime, f_Xtime, 0 },
{ "-name", c_name, f_name, 0 },
{ "-newer", c_newer, f_newer, 0 },
{ "-newermm", c_newer, f_newer, 0 },
{ "-newermt", c_newer, f_newer, F_TIME2_T },
{ "-nogroup", c_nogroup, f_nogroup, 0 },
+ { "-noignore_readdir_race",c_simple, f_always_true,0 },
+ { "-noleaf", c_simple, f_always_true, 0 },
{ "-not", c_simple, f_not, 0 },
{ "-nouser", c_nouser, f_nouser, 0 },
{ "-o", c_simple, f_or, 0 },
{ "-perm", c_perm, f_perm, 0 },
{ "-print", c_print, f_print, 0 },
{ "-print0", c_print, f_print0, 0 },
+// -printf
{ "-prune", c_simple, f_prune, 0 },
+ { "-quit", c_simple, f_quit, 0 },
{ "-regex", c_regex, f_regex, 0 },
+ { "-samefile", c_samefile, f_inum, 0 },
{ "-size", c_size, f_size, 0 },
+ { "-true", c_simple, f_always_true, 0 },
{ "-type", c_type, f_type, 0 },
+ { "-uid", c_user, f_user, 0 },
{ "-user", c_user, f_user, 0 },
+ { "-wholename", c_name, f_path, 0 },
{ "-xdev", c_xdev, f_always_true, 0 },
+// -xtype
};
/*
CFILES = hostname.c
MANPAGES = hostname.1
-Extra_CC_Flags = -Wall -mdynamic-no-pic \
- -DKERNEL_PRIVATE
+Extra_CC_Flags = -Wall -mdynamic-no-pic -D__FBSDID=__RCSID
Extra_LD_Flags = -dead_strip
include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make
+.\"-
.\" Copyright (c) 1983, 1988, 1990, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
.\" 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.
.\" SUCH DAMAGE.
.\"
.\" @(#)hostname.1 8.2 (Berkeley) 4/28/95
-.\" $FreeBSD: src/bin/hostname/hostname.1,v 1.14 2002/04/16 20:02:56 charnier Exp $
+.\" $FreeBSD: src/bin/hostname/hostname.1,v 1.21 2006/12/08 07:47:08 kientzle Exp $
.\"
-.Dd April 28, 1995
+.Dd December 7, 2006
.Dt HOSTNAME 1
.Os
.Sh NAME
.Nd set or print name of current host system
.Sh SYNOPSIS
.Nm
-.Op Fl s
+.Op Fl fs
.Op Ar name-of-host
.Sh DESCRIPTION
The
.Nm
utility prints the name of the current host.
-The super-user can set the hostname by supplying an argument.
+The super-user can
+set the hostname by supplying an argument.
+To keep the hostname between reboots, run
+.Sq scutil --set HostName Ar name-of-host .
.Pp
Options:
.Bl -tag -width flag
+.It Fl f
+Include domain information in the printed name.
+This is the default behavior.
.It Fl s
Trim off any domain information from the printed
name.
.El
.Sh SEE ALSO
-.Xr gethostname 3
+.Xr gethostname 3 ,
+.Xr scutil 8
.Sh HISTORY
The
.Nm
-/*
+/*-
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* 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.
* SUCH DAMAGE.
*/
+#if 0
#ifndef lint
static char const copyright[] =
"@(#) Copyright (c) 1988, 1993\n\
#endif /* not lint */
#ifndef lint
-#if 0
static char sccsid[] = "@(#)hostname.c 8.1 (Berkeley) 5/31/93";
-#endif
#endif /* not lint */
+#endif
#include <sys/cdefs.h>
-__RCSID("$FreeBSD: src/bin/hostname/hostname.c,v 1.14 2002/06/30 05:13:53 obrien Exp $");
+__FBSDID("$FreeBSD: src/bin/hostname/hostname.c,v 1.19 2006/12/08 07:47:08 kientzle Exp $");
#include <sys/param.h>
char *p, hostname[MAXHOSTNAMELEN];
sflag = 0;
- while ((ch = getopt(argc, argv, "s")) != -1)
+ while ((ch = getopt(argc, argv, "fs")) != -1)
switch (ch) {
+ case 'f':
+ /*
+ * On Linux, "hostname -f" prints FQDN.
+ * BSD "hostname" always prints FQDN by
+ * default, so we accept but ignore -f.
+ */
+ break;
case 's':
sflag = 1;
break;
usage(void)
{
- (void)fprintf(stderr, "usage: hostname [-s] [name-of-host]\n");
+ (void)fprintf(stderr, "usage: hostname [-fs] [name-of-host]\n");
exit(1);
}
uid_t uid, euid;
int cnt, ngroups;
#ifdef __APPLE__
- gid_t *groups;
+ gid_t *groups = NULL;
#else
gid_t groups[NGROUPS + 1];
#endif
}
else {
#ifdef __APPLE__
- groups = malloc(NGROUPS + 1);
+ groups = malloc((NGROUPS + 1) * sizeof(gid_t));
#endif
ngroups = getgroups(NGROUPS + 1, groups);
}
+#ifdef __APPLE__
+ if (ngroups < 0)
+ warn("failed to retrieve group list");
+#endif
+
if (pw != NULL)
printf("uid=%u(%s)", uid, pw->pw_name);
else
CFILES = jot.c
MANPAGES = jot.1
-Extra_CC_Flags = -Wall -mdynamic-no-pic
+Extra_CC_Flags = -Wall -mdynamic-no-pic -D__FBSDID=__RCSID
Extra_LD_Flags = -dead_strip
include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make
.\" SUCH DAMAGE.
.\"
.\" @(#)jot.1 8.1 (Berkeley) 6/6/93
-.\" $FreeBSD: src/usr.bin/jot/jot.1,v 1.14 2002/07/03 12:24:11 ru Exp $
+.\" $FreeBSD: src/usr.bin/jot/jot.1,v 1.23 2006/11/06 15:11:50 dds Exp $
.\"
-.Dd June 6, 1993
+.Dd November 6, 2006
.Dt JOT 1
.Os
.Sh NAME
While at least one of them must appear,
any of the other three may be omitted, and
will be considered as such if given as
-.Fl "" .
+.Fl ""
+or as an empty string.
Any three of these arguments determines the fourth.
If four are specified and the given and computed values of
.Ar reps
If fewer than three are specified, defaults are assigned
left to right, except for
.Ar s ,
-which assumes its default unless both
+which assumes a default of 1 or -1 if both
.Ar begin
and
.Ar end
The last argument must be a real number.
.Pp
Random numbers are obtained through
-.Xr random 3 .
+.Xr arc4random 3
+when no seed is specified,
+and through
+.Xr random 3
+when a seed is given.
+When
+.Nm
+is asked to generate random integers or characters with begin
+and end values in the range of the random number generator function
+and no format is specified with one of the
+.Fl w ,
+.Fl b ,
+or
+.Fl p
+options,
+.Nm
+will arrange for all the values in the range to appear in the output
+with an equal probability.
+In all other cases be careful to ensure that the output format's
+rounding or truncation will not skew the distribution of output
+values in an unintended way.
+.Pp
The name
.Nm
derives in part from
.Nm iota ,
a function in APL.
+.Sh EXIT STATUS
+.Ex -std
.Sh EXAMPLES
The command
+.Dl jot - 1 10
+.Pp
+prints the integers from 1 to 10,
+while the command
.Dl jot 21 -1 1.00
.Pp
prints 21 evenly spaced numbers increasing from -1 to 1.
.Pp
and thirty
.Xr ed 1
-substitution commands applying to lines 2, 7, 12, etc. is
+substitution commands applying to lines 2, 7, 12, etc.\& is
the result of
.Dl jot -w %ds/old/new/ 30 2 - 5
.Pp
-The stuttering sequence 9, 9, 8, 8, 7, etc. can be
+The stuttering sequence 9, 9, 8, 8, 7, etc.\& can be
produced by suitable choice of step size,
as in
.Dl jot - 9 0 -.5
and to print all lines 80 characters or longer,
.Dl grep `jot -s \&"\&" -b \&. 80`
.Sh DIAGNOSTICS
-.Ex -std
The following diagnostic messages deserve special explanation:
.Bl -diag
.It "illegal or unsupported format '%s'"
More than one conversion format specifier has been supplied,
but only one is allowed.
.El
-.Sh AUTHOR
-John A. Kunze <jak@ucop.edu>
.Sh SEE ALSO
.Xr ed 1 ,
.Xr expand 1 ,
.Xr rs 1 ,
.Xr yes 1 ,
+.Xr arc4random 3 ,
.Xr printf 3 ,
.Xr random 3
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Bx 4.2 .
#endif
#endif
#include <sys/cdefs.h>
-__RCSID("$FreeBSD: src/usr.bin/jot/jot.c,v 1.24 2002/07/05 15:58:27 mike Exp $");
+__FBSDID("$FreeBSD: src/usr.bin/jot/jot.c,v 1.37 2006/12/09 15:23:20 delphij Exp $");
/*
* jot - print sequential or random data
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
+#include <stdbool.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
+/* Defaults */
#define REPS_DEF 100
#define BEGIN_DEF 1
#define ENDER_DEF 100
#define STEP_DEF 1
-#define is_default(s) (strcmp((s), "-") == 0)
+/* Flags of options that have been set */
+#define HAVE_STEP 1
+#define HAVE_ENDER 2
+#define HAVE_BEGIN 4
+#define HAVE_REPS 8
-double begin;
-double ender;
-double s;
-long reps;
-int randomize;
-int infinity;
-int boring;
-int prec;
-int longdata;
-int intdata;
-int chardata;
-int nosign;
-int nofinalnl;
-const char *sepstring = "\n";
-char format[BUFSIZ];
+#define is_default(s) (*(s) == 0 || strcmp((s), "-") == 0)
-void getformat(void);
-int getprec(char *);
-int putdata(double, long);
+static bool boring;
+static int prec;
+static bool longdata;
+static bool intdata;
+static bool chardata;
+static bool nosign;
+static const char *sepstring = "\n";
+static char format[BUFSIZ];
+
+static void getformat(void);
+static int getprec(const char *);
+static int putdata(double, bool);
static void usage(void);
int
main(int argc, char **argv)
{
- double xd, yd;
- long id;
- double *x = &xd;
- double *y = &yd;
- long *i = &id;
- unsigned int mask = 0;
- int n = 0;
+ bool have_format = false;
+ bool infinity = false;
+ bool nofinalnl = false;
+ bool randomize = false;
+ bool use_random = false;
int ch;
+ int mask = 0;
+ int n = 0;
+ double begin;
+ double divisor;
+ double ender;
+ double s;
+ double x, y;
+ long i;
+ long reps;
- while ((ch = getopt(argc, argv, "rb:w:cs:np:")) != -1)
+ while ((ch = getopt(argc, argv, "b:cnp:rs:w:")) != -1)
switch (ch) {
- case 'r':
- randomize = 1;
- break;
- case 'c':
- chardata = 1;
- break;
- case 'n':
- nofinalnl = 1;
- break;
case 'b':
- boring = 1;
+ boring = true;
/* FALLTHROUGH */
case 'w':
if (strlcpy(format, optarg, sizeof(format)) >=
sizeof(format))
errx(1, "-%c word too long", ch);
+ have_format = true;
break;
- case 's':
- sepstring = optarg;
+ case 'c':
+ chardata = true;
+ break;
+ case 'n':
+ nofinalnl = true;
break;
case 'p':
prec = atoi(optarg);
if (prec <= 0)
errx(1, "bad precision value");
+ have_format = true;
+ break;
+ case 'r':
+ randomize = true;
+ break;
+ case 's':
+ sepstring = optarg;
break;
default:
usage();
if (!is_default(argv[3])) {
if (!sscanf(argv[3], "%lf", &s))
errx(1, "bad s value: %s", argv[3]);
- mask |= 01;
+ mask |= HAVE_STEP;
+ if (randomize)
+ use_random = true;
}
+ /* FALLTHROUGH */
case 3:
if (!is_default(argv[2])) {
if (!sscanf(argv[2], "%lf", &ender))
ender = argv[2][strlen(argv[2])-1];
- mask |= 02;
+ mask |= HAVE_ENDER;
if (!prec)
n = getprec(argv[2]);
}
+ /* FALLTHROUGH */
case 2:
if (!is_default(argv[1])) {
if (!sscanf(argv[1], "%lf", &begin))
begin = argv[1][strlen(argv[1])-1];
- mask |= 04;
+ mask |= HAVE_BEGIN;
if (!prec)
prec = getprec(argv[1]);
if (n > prec) /* maximum precision */
prec = n;
}
+ /* FALLTHROUGH */
case 1:
if (!is_default(argv[0])) {
if (!sscanf(argv[0], "%ld", &reps))
errx(1, "bad reps value: %s", argv[0]);
- mask |= 010;
+ mask |= HAVE_REPS;
}
break;
case 0:
getformat();
while (mask) /* 4 bit mask has 1's where last 4 args were given */
switch (mask) { /* fill in the 0's by default or computation */
- case 001:
- reps = REPS_DEF;
- mask = 011;
- break;
- case 002:
- reps = REPS_DEF;
- mask = 012;
- break;
- case 003:
- reps = REPS_DEF;
- mask = 013;
- break;
- case 004:
- reps = REPS_DEF;
- mask = 014;
- break;
- case 005:
+ case HAVE_STEP:
+ case HAVE_ENDER:
+ case HAVE_ENDER | HAVE_STEP:
+ case HAVE_BEGIN:
+ case HAVE_BEGIN | HAVE_STEP:
reps = REPS_DEF;
- mask = 015;
+ mask |= HAVE_REPS;
break;
- case 006:
- reps = REPS_DEF;
- mask = 016;
+ case HAVE_BEGIN | HAVE_ENDER:
+ s = ender > begin ? 1 : -1;
+ mask |= HAVE_STEP;
break;
- case 007:
- if (randomize) {
+ case HAVE_BEGIN | HAVE_ENDER | HAVE_STEP:
+ if (randomize)
reps = REPS_DEF;
- mask = 0;
- break;
- }
- if (s == 0.0) {
+ else if (s == 0.0)
reps = 0;
- mask = 0;
- break;
- }
- reps = (ender - begin + s) / s;
+ else
+ reps = (ender - begin + s) / s;
if (reps <= 0)
errx(1, "impossible stepsize");
mask = 0;
break;
- case 010:
+ case HAVE_REPS:
+ case HAVE_REPS | HAVE_STEP:
begin = BEGIN_DEF;
- mask = 014;
+ mask |= HAVE_BEGIN;
break;
- case 011:
- begin = BEGIN_DEF;
- mask = 015;
- break;
- case 012:
- s = (randomize ? time(NULL) : STEP_DEF);
- mask = 013;
+ case HAVE_REPS | HAVE_ENDER:
+ s = STEP_DEF;
+ mask = HAVE_REPS | HAVE_ENDER | HAVE_STEP;
break;
- case 013:
+ case HAVE_REPS | HAVE_ENDER | HAVE_STEP:
if (randomize)
begin = BEGIN_DEF;
else if (reps == 0)
begin = ender - reps * s + s;
mask = 0;
break;
- case 014:
- s = (randomize ? -1.0 : STEP_DEF);
- mask = 015;
+ case HAVE_REPS | HAVE_BEGIN:
+ s = STEP_DEF;
+ mask = HAVE_REPS | HAVE_BEGIN | HAVE_STEP;
break;
- case 015:
+ case HAVE_REPS | HAVE_BEGIN | HAVE_STEP:
if (randomize)
ender = ENDER_DEF;
else
ender = begin + reps * s - s;
mask = 0;
break;
- case 016:
- if (randomize)
- s = -1.0;
- else if (reps == 0)
+ case HAVE_REPS | HAVE_BEGIN | HAVE_ENDER:
+ if (reps == 0)
errx(1, "infinite sequences cannot be bounded");
else if (reps == 1)
s = 0.0;
s = (ender - begin) / (reps - 1);
mask = 0;
break;
- case 017: /* if reps given and implied, */
+ case HAVE_REPS | HAVE_BEGIN | HAVE_ENDER | HAVE_STEP:
+ /* if reps given and implied, */
if (!randomize && s != 0.0) {
long t = (ender - begin + s) / s;
if (t <= 0)
errx(1, "bad mask");
}
if (reps == 0)
- infinity = 1;
+ infinity = true;
if (randomize) {
- *x = (ender - begin) * (ender > begin ? 1 : -1);
- for (*i = 1; *i <= reps || infinity; (*i)++) {
- *y = arc4random() / (double)UINT32_MAX;
- if (putdata(*y * *x + begin, reps - *i))
+ if (use_random) {
+ srandom((unsigned long)s);
+ divisor = (double)INT32_MAX + 1;
+ } else
+ divisor = (double)UINT32_MAX + 1;
+
+ /*
+ * Attempt to DWIM when the user has specified an
+ * integer range within that of the random number
+ * generator: distribute the numbers equally in
+ * the range [begin .. ender]. Jot's default %.0f
+ * format would make the appearance of the first and
+ * last specified value half as likely as the rest.
+ */
+ if (!have_format && prec == 0 &&
+ begin >= 0 && begin < divisor &&
+ ender >= 0 && ender < divisor) {
+ ender += 1;
+ nosign = true;
+ intdata = true;
+ (void)strlcpy(format,
+ chardata ? "%c" : "%u", sizeof(format));
+ }
+ x = (ender - begin) * (ender > begin ? 1 : -1);
+ for (i = 1; i <= reps || infinity; i++) {
+ if (use_random)
+ y = random() / divisor;
+ else
+ y = arc4random() / divisor;
+ if (putdata(y * x + begin, !(reps - i)))
errx(1, "range error in conversion");
}
} else
- for (*i = 1, *x = begin; *i <= reps || infinity; (*i)++, *x += s)
- if (putdata(*x, reps - *i))
+ for (i = 1, x = begin; i <= reps || infinity; i++, x += s)
+ if (putdata(x, !(reps - i)))
errx(1, "range error in conversion");
if (!nofinalnl)
putchar('\n');
exit(0);
}
-int
-putdata(double x, long int notlast)
+/*
+ * Send x to stdout using the specified format.
+ * Last is true if this is the set's last value.
+ * Return 0 if OK, or a positive number if the number passed was
+ * outside the range specified by the various flags.
+ */
+static int
+putdata(double x, bool last)
{
if (boring)
} else
printf(format, x);
- if (notlast != 0)
+ if (!last)
fputs(sepstring, stdout);
return (0);
exit(1);
}
-int
-getprec(char *str)
+/*
+ * Return the number of digits following the number's decimal point.
+ * Return 0 if no decimal point is found.
+ */
+static int
+getprec(const char *str)
{
- char *p;
- char *q;
+ const char *p;
+ const char *q;
for (p = str; *p; p++)
if (*p == '.')
if (!*p)
return (0);
for (q = ++p; *p; p++)
- if (!isdigit(*p))
+ if (!isdigit((unsigned char)*p))
break;
return (p - q);
}
-void
+/*
+ * Set format, intdata, chardata, longdata, and nosign
+ * based on the command line arguments.
+ */
+static void
getformat(void)
{
char *p, *p2;
if (boring) /* no need to bother */
return;
for (p = format; *p; p++) /* look for '%' */
- if (*p == '%' && *(p+1) != '%') /* leave %% alone */
- break;
+ if (*p == '%') {
+ if (p[1] == '%')
+ p++; /* leave %% alone */
+ else
+ break;
+ }
sz = sizeof(format) - strlen(format) - 1;
if (!*p && !chardata) {
if (snprintf(p, sz, "%%.%df", prec) >= (int)sz)
} else if (!*p && chardata) {
if (strlcpy(p, "%c", sz) >= sz)
errx(1, "-w word too long");
- intdata = 1;
+ intdata = true;
} else if (!*(p+1)) {
if (sz <= 0)
errx(1, "-w word too long");
*/
p2 = p++;
dot = hash = space = sign = numbers = 0;
- while (!isalpha(*p)) {
- if (isdigit(*p)) {
+ while (!isalpha((unsigned char)*p)) {
+ if (isdigit((unsigned char)*p)) {
numbers++;
p++;
} else if ((*p == '#' && !(numbers|dot|sign|space|
goto fmt_broken;
}
if (*p == 'l') {
- longdata = 1;
+ longdata = true;
if (*++p == 'l') {
if (p[1] != '\0')
p++;
}
switch (*p) {
case 'o': case 'u': case 'x': case 'X':
- intdata = nosign = 1;
+ intdata = nosign = true;
break;
case 'd': case 'i':
- intdata = 1;
+ intdata = true;
break;
case 'D':
if (!longdata) {
- intdata = 1;
+ intdata = true;
break;
}
case 'O': case 'U':
if (!longdata) {
- intdata = nosign = 1;
+ intdata = nosign = true;
break;
}
case 'c':
if (!(intdata | longdata)) {
- chardata = 1;
+ chardata = true;
break;
}
case 'h': case 'n': case 'p': case 'q': case 's': case 'L':
else if (*p == '%' && *(p+1) == '%')
p++;
else if (*p == '%' && !*(p+1)) {
- strcat(format, "%");
+ if (strlcat(format, "%", sizeof(format)) >=
+ sizeof(format))
+ errx(1, "-w word too long");
break;
}
}
#include <unistd.h>
#include <locale.h>
+#include <getopt.h>
+#define OPTIONS ("c:dej:lmst:u:vz")
+
static void __dead2
usage(void)
{
exit(1);
}
+/*
+ * kludge_signal_args - remove any signal option (-SIGXXX, -##) from the argv array.
+ */
+void
+kludge_signal_args(int *argc, char **argv, int *sig)
+{
+ int i;
+ int shift = 0;
+ int kludge = 1;
+ char *ptr;
+ const char *const *p;
+ char *ep;
+
+ /* i = 1, skip program name */
+ for (i = 1; i < *argc; i++) {
+ /* Stop if we encounter either a non-option or -- */
+ if (*argv[i] != '-' || strcmp(argv[i], "--") == 0)
+ kludge = 0;
+ ptr = argv[i] + 1;
+ if (kludge && strchr(OPTIONS, *ptr) == NULL) {
+ if (isalpha(*ptr)) {
+ if (strcmp(ptr, "help") == 0)
+ usage();
+ if (strncasecmp(ptr, "sig", 3) == 0)
+ ptr += 3;
+ for (*sig = NSIG, p = sys_signame + 1; --*sig; ++p)
+ if (strcasecmp(*p, ptr) == 0) {
+ *sig = p - sys_signame;
+ break;
+ }
+ if (!*sig)
+ nosig(ptr);
+ } else if (isdigit(*ptr)) {
+ *sig = strtol(ptr, &ep, 10);
+ if (*ep)
+ errx(1, "illegal signal number: %s", ptr);
+ if (*sig < 0 || *sig >= NSIG)
+ nosig(ptr);
+ } else
+ nosig(ptr);
+
+ shift++;
+ continue;
+ }
+
+ argv[i - shift] = argv[i];
+ }
+
+ for (i = *argc - shift; i < *argc; i++) {
+ argv[i] = NULL;
+ }
+
+ *argc -= shift;
+}
+
int
main(int ac, char **av)
{
#endif /* !__APPLE__ */
dev_t thistdev;
int sig = SIGTERM;
- const char *const *p;
char *ep;
int errors = 0;
#ifndef __APPLE__
size_t size;
int matched;
int killed = 0;
+ int ch;
setlocale(LC_ALL, "");
- av++;
- ac--;
+ kludge_signal_args(&ac, av, &sig);
- while (ac > 0) {
- if (strcmp(*av, "-l") == 0) {
+ while ((ch = getopt(ac, av, OPTIONS)) != -1) {
+ switch (ch) {
+ case 'c':
+ cmd = optarg;
+ break;
+ case 'd':
+ dflag++;
+ break;
+ case 'e':
+ eflag++;
+ break;
+#ifndef __APPLE__
+ case 'j':
+ jflag++;
+ jid = strtol(optarg, &ep, 10);
+ if (*ep)
+ errx(1, "illegal jid: %s", optarg);
+ if (jail_attach(jid) == -1)
+ err(1, "jail_attach(): %d", jid);
+ break;
+#endif /* __APPLE__ */
+ case 'l':
printsig(stdout);
exit(0);
- }
- if (strcmp(*av, "-help") == 0)
- usage();
- if (**av == '-') {
- ++*av;
- switch (**av) {
-#ifndef __APPLE__
- case 'j':
- ++*av;
- if (**av == '\0')
- ++av;
- --ac;
- jflag++;
- if (!*av)
- errx(1, "must specify jid");
- jid = strtol(*av, &ep, 10);
- if (!*av || *ep)
- errx(1, "illegal jid: %s", *av);
- if (jail_attach(jid) == -1)
- err(1, "jail_attach(): %d", jid);
- break;
-#endif /* !__APPLE__ */
- case 'u':
- ++*av;
- if (**av == '\0')
- ++av;
- --ac;
- user = *av;
- break;
- case 't':
- ++*av;
- if (**av == '\0')
- ++av;
- --ac;
- tty = *av;
- break;
- case 'c':
- ++*av;
- if (**av == '\0')
- ++av;
- --ac;
- cmd = *av;
- break;
- case 'v':
- vflag++;
- break;
- case 's':
- sflag++;
- break;
- case 'd':
- dflag++;
- break;
- case 'e':
- eflag++;
- break;
- case 'm':
- mflag++;
- break;
- case 'z':
- zflag++;
- break;
- default:
- if (isalpha((unsigned char)**av)) {
- if (strncasecmp(*av, "sig", 3) == 0)
- *av += 3;
- for (sig = NSIG, p = sys_signame + 1;
- --sig; ++p)
- if (strcasecmp(*p, *av) == 0) {
- sig = p - sys_signame;
- break;
- }
- if (!sig)
- nosig(*av);
- } else if (isdigit((unsigned char)**av)) {
- sig = strtol(*av, &ep, 10);
- if (!*av || *ep)
- errx(1, "illegal signal number: %s", *av);
- if (sig < 0 || sig >= NSIG)
- nosig(*av);
- } else
- nosig(*av);
- }
- ++av;
- --ac;
- } else {
+ case 'm':
+ mflag++;
+ break;
+ case 's':
+ sflag++;
+ break;
+ case 't':
+ tty = optarg;
break;
+ case 'u':
+ user = optarg;
+ break;
+ case 'v':
+ vflag++;
+ break;
+ case 'z':
+ zflag++;
+ break;
+ default:
+ usage();
}
}
+ ac -= optind;
+ av += optind;
+
#ifdef __APPLE__
if (user == NULL && tty == NULL && cmd == NULL && ac == 0)
#else /* !__APPLE__*/
#include <time.h>
#include <tzfile.h>
#include <unistd.h>
-#include <utmp.h>
+/* definitions from utmp.h */
+#define UT_NAMESIZE 8
+#define UT_LINESIZE 8
#include "pathnames.h"
time_t expand __P((u_int));
fastfind.c \
locate.1 locate.updatedb.8 locate.rc \
concatdb.sh mklocatedb.sh updatedb.sh
+LAUNCHD_PLISTS = com.apple.locate.plist
Extra_CC_Flags = -Wall -mdynamic-no-pic
Extra_LD_Flags = -dead_strip
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.apple.locate</string>
+ <key>Disabled</key>
+ <true/>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/usr/libexec/locate.updatedb</string>
+ </array>
+ <key>LowPriorityIO</key>
+ <true/>
+ <key>Nice</key>
+ <integer>5</integer>
+ <key>KeepAlive</key>
+ <dict>
+ <key>PathState</key>
+ <dict>
+ <key>/var/db/locate.database</key>
+ <false/>
+ </dict>
+ </dict>
+ <key>StartCalendarInterval</key>
+ <dict>
+ <key>Hour</key>
+ <integer>3</integer>
+ <key>Minute</key>
+ <integer>15</integer>
+ <key>Weekday</key>
+ <integer>6</integer>
+ </dict>
+ <key>AbandonProcessGroup</key>
+ <true/>
+</dict>
+</plist>
count += c - OFFSET;
}
+#ifdef __APPLE__
+ if (count < 0) {
+ errx(1, "Your locate database appears to be corrupt. "
+ "Run 'sudo /usr/libexec/locate.updatedb' to "
+ "regenerate the database.");
+ }
+#endif /* __APPLE__ */
+
/* overlay old path */
p = path + count;
foundchar = p - 1;
option was specified.
.El
.Sh FILES
-.Bl -tag -width /etc/periodic/weekly/310.locate -compact
+.Bl -tag -width /System/Library/LaunchDaemons/com.apple.locate.plist -compact
.It Pa /var/db/locate.database
locate database
.It Pa /usr/libexec/locate.updatedb
Script to update the locate database
-.It Pa /etc/periodic/weekly/310.locate
-Script that starts the database rebuild
+.It Pa /System/Library/LaunchDaemons/com.apple.locate.plist
+Job that starts the database rebuild
.El
.Sh SEE ALSO
.Xr find 1 ,
This is because
locate only reports files that are present in the database, which is
typically only regenerated once a week by the
-.Pa /etc/periodic/weekly/310.locate
-script.
+.Pa /System/Library/LaunchDaemons/com.apple.locate.plist
+job.
Use
.Xr find 1
to locate files that are of a more transitory nature.
#include <sys/param.h>
#include <ctype.h>
#include <err.h>
+#ifdef __APPLE__
+#include <errno.h>
+#endif /* __APPLE__ */
#include <fnmatch.h>
#include <locale.h>
#include <stdio.h>
*(s+1) = NULL;
}
}
+#ifdef __APPLE__
+ else if ((fp = fopen(db, "r")) == NULL) {
+ if (errno == ENOENT && !strcmp(db, _PATH_FCODES)) {
+ fprintf(stderr, "\n"
+ "WARNING: The locate database (%s) does not exist.\n"
+ "To create the database, run the following command:\n"
+ "\n"
+ " sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.locate.plist\n"
+ "\n"
+ "Please be aware that the database can take some time to generate; once\n"
+ "the database has been created, this message will no longer appear.\n"
+ "\n",
+ db);
+ exit(1);
+ }
+ err(1, "`%s'", db);
+ }
+#else /* !__APPLE__ */
else if ((fp = fopen(db, "r")) == NULL)
err(1, "`%s'", db);
+#endif /* __APPLE__ */
/* count only chars or lines */
if (f_statistic) {
utility updates the database used by
.Xr locate 1 .
It is typically run once a week by the
-.Pa /etc/periodic/weekly/310.locate
-script.
+.Pa /System/Library/LaunchDaemons/com.apple.locate.plist
+job.
.Pp
The contents of the newly built database can be controlled by the
.Pa /etc/locate.rc
.El
.Sh SEE ALSO
.Xr locate 1 ,
-.Xr periodic 8
+.Xr launchd 8
.Rs
.%A Woods, James A.
.%D 1983
# $FreeBSD: src/usr.bin/locate/locate/updatedb.sh,v 1.20 2005/11/12 12:45:08 grog Exp $
if [ "$(id -u)" = "0" ]; then
- echo ">>> WARNING" 1>&2
- echo ">>> Executing updatedb as root. This WILL reveal all filenames" 1>&2
- echo ">>> on your machine to all login users, which is a security risk." 1>&2
+ rc=0
+ export FCODES=`mktemp -t updatedb`
+ chown nobody $FCODES
+ tmpdb=`su -fm nobody -c "$0"` || rc=1
+ if [ $rc = 0 ]; then
+ install -m 0444 -o nobody -g wheel $FCODES /var/db/locate.database
+ fi
+ rm $FCODES
+ exit $rc
fi
: ${LOCATE_CONFIG="/etc/locate.rc"}
if [ -f "$LOCATE_CONFIG" -a -r "$LOCATE_CONFIG" ]; then
PATH=$LIBEXECDIR:/bin:/usr/bin:$PATH; export PATH
+# 6497475
+set -o noglob
: ${mklocatedb:=locate.mklocatedb} # make locate database program
: ${FCODES:=/var/db/locate.database} # the database
: ${SEARCHPATHS:="/"} # directories to be put in the database
-: ${PRUNEPATHS:="/tmp /var/tmp */Backups.backupdb"} # unwanted directories
+: ${PRUNEPATHS:="/private/tmp /private/var/folders /private/var/tmp */Backups.backupdb"} # unwanted directories
: ${FILESYSTEMS:="hfs ufs"} # allowed filesystems
: ${find:=find}
.\" SUCH DAMAGE.
.\"
.\" From: $OpenBSD: mktemp.1,v 1.8 1998/03/19 06:13:37 millert Exp $
-.\" $FreeBSD: src/usr.bin/mktemp/mktemp.1,v 1.7.2.6 2001/07/22 12:40:27 dd Exp $
+.\" $FreeBSD: src/usr.bin/mktemp/mktemp.1,v 1.21 2006/09/29 15:20:46 ru Exp $
.\"
-.Dd November 20, 1996
+.Dd December 30, 2005
.Dt MKTEMP 1
.Os
.Sh NAME
The
.Nm
utility takes each of the given file name templates and overwrites a
-portion of it to create a file name. This file name is unique
-and suitable for use by the application. The template may be
+portion of it to create a file name.
+This file name is unique
+and suitable for use by the application.
+The template may be
any file name with some number of
.Ql X Ns s
appended
will
result in
.Nm
-testing roughly 26 ** 6 combinations.
+selecting 1 of 56800235584 (62 ** 6) possible file names.
.Pp
If
.Nm
.Fl t Ar prefix
option is given,
.Nm
-will generate an template string based on the
+will generate a template string based on the
.Ar prefix
and the
.Ev TMPDIR
.Fl t
flag.
.Pp
-.Nm Mktemp
-is provided to allow shell scripts to safely use temporary files.
+The
+.Nm
+utility is provided to allow shell scripts to safely use temporary files.
Traditionally, many shell scripts take the name of the program with
-the pid as a suffix and use that as a temporary file name. This
+the pid as a suffix and use that as a temporary file name.
+This
kind of naming scheme is predictable and the race condition it creates
-is easy for an attacker to win. A safer, though still inferior, approach
-is to make a temporary directory using the same naming scheme. While
+is easy for an attacker to win.
+A safer, though still inferior, approach
+is to make a temporary directory using the same naming scheme.
+While
this does allow one to guarantee that a temporary file will not be
-subverted, it still allows a simple denial of service attack. For these
+subverted, it still allows a simple denial of service attack.
+For these
reasons it is suggested that
.Nm
be used instead.
.It Fl d
Make a directory instead of a file.
.It Fl q
-Fail silently if an error occurs. This is useful if
+Fail silently if an error occurs.
+This is useful if
a script does not want error output to go to standard error.
.It Fl t Ar prefix
Generate a template (using the supplied
.It Fl u
Operate in
.Dq unsafe
-mode. The temp file will be unlinked before
+mode.
+The temp file will be unlinked before
.Nm
-exits. This is slightly better than
+exits.
+This is slightly better than
.Xr mktemp 3
-but still introduces a race condition. Use of this
+but still introduces a race condition.
+Use of this
option is not encouraged.
.El
-.Sh DIAGNOSTICS
+.Sh EXIT STATUS
The
.Nm
utility
first appeared in
.Fx 2.2.7 .
This man page is taken from
-.Ox
+.Ox .
#ifndef lint
static const char rcsid[] =
- "$FreeBSD: src/usr.bin/mktemp/mktemp.c,v 1.2.6.1 2000/10/15 11:37:43 alex Exp $";
+ "$FreeBSD: src/usr.bin/mktemp/mktemp.c,v 1.5 2002/03/22 01:33:17 imp Exp $";
#endif /* not lint */
-static void usage __P((void));
+static void usage(void);
int
main(int argc, char **argv)
{
int c, fd, ret;
- char *tmpdir, *prefix;
+ char *tmpdir;
+ const char *prefix;
char *name;
int dflag, qflag, tflag, uflag;
tmpdir = getenv("TMPDIR");
if (tmpdir == NULL)
asprintf(&name, "%s%s.XXXXXXXX", _PATH_TMP, prefix);
- else
- asprintf(&name, "%s/%s.XXXXXXXX", tmpdir, prefix);
+ else {
+ int len = strlen(tmpdir);
+ if (len > 0 && tmpdir[len - 1] == '/')
+ asprintf(&name, "%s%s.XXXXXXXX", tmpdir, prefix);
+ else
+ asprintf(&name, "%s/%s.XXXXXXXX", tmpdir, prefix);
+ }
/* if this fails, the program is in big trouble already */
if (name == NULL) {
if (qflag)
argv++;
argc--;
}
+
if (dflag) {
if (mkdtemp(name) == NULL) {
ret = 1;
-.\" $NetBSD: nohup.1,v 1.6 1997/10/19 10:23:25 lukem Exp $
-.\"
.\" Copyright (c) 1989, 1990, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
.\" SUCH DAMAGE.
.\"
.\" @(#)nohup.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD: src/usr.bin/nohup/nohup.1,v 1.13 2005/01/17 07:44:27 ru Exp $
.\"
-.Dd June 6, 1993
+.Dd July 19, 2001
.Dt NOHUP 1
.Os
.Sh NAME
.Nm nohup
-.Nd invoke a command immune to hangups
+.Nd invoke a utility immune to hangups
.Sh SYNOPSIS
.Nm
+.Op Fl Fl
.Ar utility
-.Op Ar arg ...
+.Op Ar arguments
.Sh DESCRIPTION
The
.Nm
utility invokes
-.Ar command
-with
-its arguments
+.Ar utility
+with its
+.Ar arguments
and at this time sets the signal
.Dv SIGHUP
-to be ignored.
+to be ignored.
If the standard output is a terminal, the standard output is
appended to the file
.Pa nohup.out
in the current directory.
If standard error is a terminal, it is directed to the same place
as the standard output.
+.Pp
+Some shells may provide a builtin
+.Nm
+command which is similar or identical to this utility.
+Consult the
+.Xr builtin 1
+manual page.
.Sh ENVIRONMENT
-The following variable is utilized by
-.Nm .
+The following variables are utilized by
+.Nm :
.Bl -tag -width flag
.It Ev HOME
If the output file
utility uses the directory named by
.Ev HOME
to create the file.
+.It Ev PATH
+Used to locate the requested
+.Ar utility
+if the name contains no
+.Ql /
+characters.
.El
-.Sh DIAGNOSTICS
+.Sh EXIT STATUS
The
.Nm
-utility shall exit with one of the following values:
-.Bl -tag -width Ds
+utility exits with one of the following values:
+.Bl -tag -width Ds
.It 126
The
.Ar utility
-was found but could not be invoked.
+was found, but could not be invoked.
.It 127
The
.Ar utility
-could not be found or an error occurred in
+could not be found or an error occurred in
.Nm .
.El
.Pp
-Otherwise, the exit status of
+Otherwise, the exit status of
.Nm
-shall be that of
+will be that of
.Ar utility .
.Sh SEE ALSO
+.Xr builtin 1 ,
+.Xr csh 1 ,
.Xr signal 3
.Sh STANDARDS
The
.Nm
-command is expected to be
+utility is expected to be
.St -p1003.2
compatible.
+.Sh BUGS
+Two or more instances of
+.Nm
+can append to the same file, which makes for a confusing output.
(void)signal(SIGHUP, SIG_IGN);
#ifdef __APPLE__
- if (_vprocmgr_move_subset_to_user(geteuid(), "Background") != NULL)
- err(EXIT_MISC, "can't migrate to background session");
+ if (_vprocmgr_detach_from_console(0) != NULL)
+ err(EXIT_MISC, "can't detach from console");
#endif
execvp(*argv, argv);
exit_status = (errno == ENOENT) ? EXIT_NOTFOUND : EXIT_NOEXEC;
--- /dev/null
+Project = path_helper
+Install_Dir = /usr/libexec
+
+CFILES = path_helper.c
+MANPAGES = path_helper.8
+
+Extra_CC_Flags = -Wall -Werror -mdynamic-no-pic
+Extra_LD_Flags = -dead_strip
+
+include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make
--- /dev/null
+.\"
+.\" Copyright (c) 2007 Apple Inc. All rights reserved.
+.\"
+.\" @APPLE_LICENSE_HEADER_START@
+.\"
+.\" This file contains Original Code and/or Modifications of Original Code
+.\" as defined in and that are subject to the Apple Public Source License
+.\" Version 2.0 (the 'License'). You may not use this file except in
+.\" compliance with the License. Please obtain a copy of the License at
+.\" http://www.opensource.apple.com/apsl/ and read it before using this
+.\" file.
+.\"
+.\" The Original Code and all software distributed under the License are
+.\" distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+.\" EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+.\" INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+.\" FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+.\" Please see the License for the specific language governing rights and
+.\" limitations under the License.
+.\"
+.\" @APPLE_LICENSE_HEADER_END@
+.\"
+.Dd March 15, 2007
+.Dt path_helper 8
+.Os "Mac OS X"
+.Sh NAME
+.Nm path_helper
+.Nd helper for constructing PATH environment variable
+.Sh SYNOPSIS
+.Nm
+.Op Fl c | Fl s
+.Sh DESCRIPTION
+The
+.Nm
+utility reads the contents of the files in the directories
+.Pa /etc/paths.d
+and
+.Pa /etc/manpaths.d
+and appends their contents to the
+.Ev PATH
+and
+.Ev MANPATH
+environment variables respectively.
+(The
+.Ev MANPATH
+environment variable will not be modified unless it is already set
+in the environment.)
+.Pp
+Files in these directories should contain one path element per line.
+.Pp
+Prior to reading these directories, default
+.Ev PATH
+and
+.Ev MANPATH
+values are obtained from the files
+.Pa /etc/paths
+and
+.Pa /etc/manpaths
+respectively.
+.Pp
+Options:
+.Bl -tag -width Ds
+.It Fl c
+Generate C-shell commands on stdout. This is the default if
+.Ev SHELL
+ends with "csh".
+.It Fl s
+Generate Bourne shell commands on stdout. This is the default if
+.Ev SHELL
+does not end with "csh".
+.El
+.Sh NOTE
+The
+.Nm
+utility should not be invoked directly.
+It is intended only for use by the shell profile.
--- /dev/null
+/*
+ * Copyright (c) 2008 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+
+static void usage() {
+ fprintf(stderr, "usage: path_helper [-c | -s]");
+ exit(1);
+}
+
+// Append path segment if it does not exist. Reallocate
+// the path buffer as necessary.
+
+int append_path_segment(char** path, const char* segment) {
+ if (*path == NULL || segment == NULL) return -1;
+
+ size_t pathlen = strlen(*path);
+ size_t seglen = strlen(segment);
+
+ if (seglen == 0) return 0;
+
+ // Does the segment already exist in the path?
+ // (^|:)segment(:|$)
+ char* match = strstr(*path, segment);
+ while (match) {
+ if ((match == *path || match[-1] == ':') &&
+ (match[seglen] == ':' || match[seglen] == 0)) {
+ return 0;
+ }
+ match = strstr(match+1, segment);
+ }
+
+ // size = pathlen + ':' + segment + '\0'
+ size_t size = pathlen + seglen + 2;
+ *path = reallocf(*path, size);
+ if (*path == NULL) return -1;
+
+ if (pathlen > 0) strlcat(*path, ":", size);
+ strlcat(*path, segment, size);
+ return 0;
+}
+
+// Convert fgetln output into a sanitized segment
+// escape quotes, dollars, etc.
+char* read_segment(const char* line, size_t len) {
+ int escapes = 0;
+ size_t i, j;
+
+ for (i = 0; i < len; ++i) {
+ char c = line[i];
+ if (c == '\"' || c == '\'' || c == '$') {
+ ++escapes;
+ }
+ }
+
+ size_t size = len + escapes + 1;
+
+ char* segment = malloc(size);
+ if (segment == NULL) return NULL;
+
+ for (i = 0, j = 0; i < len; ++i, ++j) {
+ char c = line[i];
+ if (c == '\"' || c == '\'' || c == '$') {
+ segment[j++] = '\\';
+ segment[j] = c;
+ } else if (c == '\n') {
+ segment[j] = 0;
+ break;
+ } else {
+ segment[j] = line[i];
+ }
+ }
+
+ return segment;
+}
+
+// Construct a path variable, starting with the contents
+// of the given environment variable, adding the contents
+// of the default file and files in the path directory.
+
+char* construct_path(char* env_var, char* defaults_path, char* dir_path) {
+ FTS* fts;
+ FTSENT* ent;
+
+ char* result = calloc(sizeof(char), 1);
+
+ char* dirpathv[] = { defaults_path, dir_path, NULL };
+ fts = fts_open(dirpathv, FTS_PHYSICAL | FTS_XDEV, NULL);
+ if (!fts) {
+ perror(dir_path);
+ return NULL;
+ }
+
+ while ((ent = fts_read(fts)) != NULL) {
+ // only interested in regular files, one level deep
+ if (ent->fts_info != FTS_F) {
+ if (ent->fts_level >= 1) fts_set(fts, ent, FTS_SKIP);
+ continue;
+ }
+
+ FILE* f = fopen(ent->fts_accpath, "r");
+ if (f == NULL) {
+ perror(ent->fts_accpath);
+ continue;
+ }
+
+ for (;;) {
+ size_t len;
+ char* line = fgetln(f, &len);
+ if (line == NULL) break;
+ char* segment = read_segment(line, len);
+
+ append_path_segment(&result, segment);
+ }
+
+ fclose(f);
+ }
+ fts_close(fts);
+
+ // merge in any existing custom PATH elemenets
+ char* str = getenv(env_var);
+ if (str) str = strdup(str);
+ while (str) {
+ char* sep = strchr(str, ':');
+ if (sep) *sep = 0;
+
+ append_path_segment(&result, str);
+ if (sep) {
+ str = sep + 1;
+ } else {
+ str = NULL;
+ }
+ }
+
+ return result;
+}
+
+enum {
+ STYLE_CSH,
+ STYLE_SH
+};
+
+int main(int argc, char* argv[]) {
+ int style = STYLE_SH;
+
+ if (argc > 2) usage();
+
+ // default to csh style, if $SHELL ends with "csh".
+ char* shell = getenv("SHELL");
+ if (shell) {
+ char* str = strstr(shell, "csh");
+ if (str) style = STYLE_CSH;
+ }
+
+ if (argc == 2 && strcmp(argv[1], "-c") == 0) style = STYLE_CSH;
+ if (argc == 2 && strcmp(argv[1], "-s") == 0) style = STYLE_SH;
+
+ char* path = construct_path("PATH", "/etc/paths", "/etc/paths.d");
+ char* manpath = NULL;
+
+ // only adjust manpath if already set
+ int do_manpath = (getenv("MANPATH") != NULL);
+ if (do_manpath) {
+ manpath = construct_path("MANPATH", "/etc/manpaths", "/etc/manpaths.d");
+ }
+
+ if (style == STYLE_CSH) {
+ printf("setenv PATH \"%s\";\n", path);
+ if (do_manpath) printf("setenv MANPATH \"%s\";\n", manpath);
+ } else {
+ printf("PATH=\"%s\"; export PATH;\n", path);
+ if (do_manpath) printf("MANPATH=\"%s\"; export MANPATH;\n", manpath);
+ }
+
+ return 0;
+}
-.\" $NetBSD: printf.1,v 1.10 1998/08/22 14:54:48 garbled Exp $
-.\"
.\" Copyright (c) 1989, 1990, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.\" from: @(#)printf.1 8.1 (Berkeley) 6/6/93
+.\" @(#)printf.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD: src/usr.bin/printf/printf.1,v 1.34 2005/06/14 11:50:52 ru Exp $
.\"
-.Dd November 5, 1993
+.Dd April 14, 2005
.Dt PRINTF 1
.Os
.Sh NAME
.Nd formatted output
.Sh SYNOPSIS
.Nm
-.Ar format
-.Op Ar arguments ...
+.Ar format Op Ar arguments ...
.Sh DESCRIPTION
The
.Nm
utility formats and prints its arguments, after the first, under control
of the
-.Ar format .
+.Ar format .
The
.Ar format
is a character string which contains three types of objects: plain characters,
which are simply copied to standard output, character escape sequences which
are converted and copied to the standard output, and format specifications,
each of which causes printing of the next successive
-.Ar argument .
+.Ar argument .
.Pp
The
.Ar arguments
after the first are treated as strings if the corresponding format is
either
-.Cm b ,
-.Cm c
+.Cm c , b
or
.Cm s ;
otherwise it is evaluated as a C constant, with the following extensions:
.It
A leading plus or minus sign is allowed.
.It
-If the leading character is a single or double quote, the value is the
+If the leading character is a single or double quote, the value is the
.Tn ASCII
code of the next character.
.El
.Pp
The format string is reused as often as necessary to satisfy the
-.Ar arguments .
+.Ar arguments .
Any extra format specifications are evaluated with zero or the null
string.
.Pp
-Character escape sequences are in backslash notation as defined in
-.St -ansiC .
+Character escape sequences are in backslash notation as defined in the
+.St -ansiC ,
+with extensions.
The characters and their meanings
are as follows:
-.Bl -tag -width Ds -offset indent
-.It Cm \ee
-Write an <escape> character.
+.Pp
+.Bl -tag -width Ds -offset indent -compact
.It Cm \ea
Write a <bell> character.
.It Cm \eb
Write a <backspace> character.
+.It Cm \ec
+Ignore remaining characters in this string.
.It Cm \ef
Write a <form-feed> character.
.It Cm \en
Write a <single quote> character.
.It Cm \e\e
Write a backslash character.
-.It Cm \e Ns Ar num
+.It Cm \e Ns Ar num
+.It Cm \e0 Ns Ar num
Write an 8-bit character whose
.Tn ASCII
value is the 1-, 2-, or 3-digit
.Bl -tag -width Ds
.It Cm #
A `#' character
-specifying that the value should be printed in an ``alternative form''.
+specifying that the value should be printed in an ``alternate form''.
For
-.Cm c ,
-.Cm d ,
+.Cm c , d ,
and
-.Cm s ,
-formats, this option has no effect. For the
+.Cm s ,
+formats, this option has no effect.
+For the
.Cm o
formats the precision of the number is increased to force the first
-character of the output string to a zero. For the
+character of the output string to a zero.
+For the
.Cm x
.Pq Cm X
format, a non-zero result has the string
.Li 0x
.Pq Li 0X
-prepended to it. For
-.Cm e ,
-.Cm E ,
-.Cm f ,
-.Cm g ,
+prepended to it.
+For
+.Cm e , E , f , g ,
and
-.Cm G ,
+.Cm G ,
formats, the result will always contain a decimal point, even if no
digits follow the point (normally, a decimal point only appears in the
-results of those formats if a digit follows the decimal point). For
+results of those formats if a digit follows the decimal point).
+For
.Cm g
and
.Cm G
a sign placed before the number when using signed formats.
.It Sq \&\ \&
A space specifying that a blank should be left before a positive number
-for a signed format. A `+' overrides a space if both are used;
+for a signed format.
+A `+' overrides a space if both are used;
.It Cm \&0
A zero `0' character indicating that zero-padding should be used
-rather than blank-padding. A `\-' overrides a `0' if both are used;
+rather than blank-padding.
+A `\-' overrides a `0' if both are used;
.El
.It "Field Width:"
An optional digit string specifying a
which specifies the number of digits to appear after the decimal point,
for
.Cm e
-and
+and
.Cm f
formats, or the maximum number of characters to be printed
from a string; if the digit string is missing, the precision is treated
as zero;
.It Format:
A character which indicates the type of format to use (one of
-.Cm diouxXfwEgGbcs ) .
+.Cm diouxXfFeEgGaAcsb ) .
+The uppercase formats differ from their lowercase counterparts only in
+that the output of the former is entirely in uppercase.
+The floating-point format specifiers
+.Pq Cm fFeEgGaA
+may be prefixed by an
+.Cm L
+to request that additional precision be used, if available.
.El
.Pp
A field width or precision may be
.Ar argument
is printed as a signed decimal (d or i), unsigned octal, unsigned decimal,
or unsigned hexadecimal (X or x), respectively.
-.It Cm f
+.It Cm fF
The
.Ar argument
-is printed in the style
-.Sm off
-.Pf [\-]ddd Cm \&. No ddd
-.Sm on
-where the number of d's
+is printed in the style `[\-]ddd.ddd' where the number of d's
after the decimal point is equal to the precision specification for
the argument.
If the precision is missing, 6 digits are given; if the precision
is explicitly 0, no digits and no decimal point are printed.
+The values \*[If] and \*[Na] are printed as
+.Ql inf
+and
+.Ql nan ,
+respectively.
.It Cm eE
The
.Ar argument
-is printed in the style
+is printed in the style
+.Cm e
.Sm off
-.Pf [\-]d Cm \&. No ddd Cm e No \\*(Pmdd
+.Sq Op - Ar d.ddd No \(+- Ar dd
.Sm on
where there
is one digit before the decimal point and the number after is equal to
the precision specification for the argument; when the precision is
missing, 6 digits are produced.
-An upper-case E is used for an `E' format.
+The values \*[If] and \*[Na] are printed as
+.Ql inf
+and
+.Ql nan ,
+respectively.
.It Cm gG
The
.Ar argument
is printed in style
.Cm f
+.Pq Cm F
or in style
.Cm e
.Pq Cm E
whichever gives full precision in minimum space.
-.It Cm b
-Characters from the string
+.It Cm aA
+The
.Ar argument
-are printed with backslash-escape sequences expanded.
+is printed in style
+.Sm off
+.Sq Op - Ar h.hhh No \(+- Li p Ar d
+.Sm on
+where there is one digit before the hexadecimal point and the number
+after is equal to the precision specification for the argument;
+when the precision is missing, enough digits are produced to convey
+the argument's exact double-precision floating-point representation.
+The values \*[If] and \*[Na] are printed as
+.Ql inf
+and
+.Ql nan ,
+respectively.
.It Cm c
The first character of
.Ar argument
are printed until the end is reached or until the number of characters
indicated by the precision specification is reached; however if the
precision is 0 or missing, all characters in the string are printed.
+.It Cm b
+As for
+.Cm s ,
+but interpret character escapes in backslash notation in the string
+.Ar argument .
.It Cm \&%
Print a `%'; no argument is used.
.El
.Pp
+The decimal point
+character is defined in the program's locale (category
+.Dv LC_NUMERIC ) .
+.Pp
In no case does a non-existent or small field width cause truncation of
a field; padding takes place only if the specified field width exceeds
the actual width.
-.Sh RETURN VALUES
-.Nm
-exits 0 on success, 1 on failure.
+.Sh EXIT STATUS
+.Ex -std
+.Sh COMPATIBILITY
+The traditional
+.Bx
+behavior of converting arguments of numeric formats not beginning
+with a digit to the
+.Tn ASCII
+code of the first character is not supported.
.Sh SEE ALSO
.Xr echo 1 ,
.Xr printf 3
.Sh STANDARDS
The
.Nm
-utility mostly conforms to
-.St -p1003.2-92 .
+command is expected to be mostly compatible with the
+.St -p1003.2
+specification.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 Reno .
+It is modeled
+after the standard library function,
+.Xr printf 3 .
.Sh BUGS
Since the floating point numbers are translated from
.Tn ASCII
to floating-point and
then back again, floating-point precision may be lost.
+(By default, the number is translated to an IEEE-754 double-precision
+value before being printed.
+The
+.Cm L
+modifier may produce additional precision, depending on the hardware platform.)
+.Pp
+.Tn ANSI
+hexadecimal character constants were deliberately not provided.
+.Pp
+The escape sequence \e000 is the string terminator.
+When present in the argument for the
+.Cm b
+format, the argument will be truncated at the \e000 character.
+.Pp
+Multibyte characters are not recognized in format strings (this is only
+a problem if
+.Ql %
+can appear inside a multibyte character).
.Pp
Parsing of - arguments is also somewhat different from
.Xr printf 3 ,
-/* $NetBSD: printf.c,v 1.19 1998/02/03 03:10:15 perry Exp $ */
-
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
-#ifndef lint
#if !defined(BUILTIN) && !defined(SHELL)
-__COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\
- The Regents of the University of California. All rights reserved.\n");
-#endif
+#ifndef lint
+static char const copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
#endif
#ifndef lint
#if 0
-static char sccsid[] = "@(#)printf.c 8.2 (Berkeley) 3/22/95";
-#else
-__RCSID("$NetBSD: printf.c,v 1.19 1998/02/03 03:10:15 perry Exp $");
+static char const sccsid[] = "@(#)printf.c 8.1 (Berkeley) 7/20/93";
#endif
+static const char rcsid[] =
+ "$FreeBSD: src/usr.bin/printf/printf.c,v 1.37 2005/08/05 08:18:00 stefanf Exp $";
#endif /* not lint */
#include <sys/types.h>
-#include <ctype.h>
#include <err.h>
#include <errno.h>
+#include <inttypes.h>
#include <limits.h>
-#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
#include <string.h>
#include <unistd.h>
-static int print_escape_str __P((const char *));
-static size_t print_escape __P((const char *));
-
-static int getchr __P((void));
-static double getdouble __P((void));
-static int getint __P((void));
-static long getlong __P((void));
-static unsigned long getulong __P ((void));
-static char *getstr __P((void));
-static char *mklong __P((const char *, int));
-static void check_conversion __P((const char *, const char *));
-static void usage __P((void));
-
-static int rval;
-static char **gargv;
-
-#ifdef BUILTIN
-int progprintf __P((int, char **));
-#else
-int main __P((int, char **));
-#endif
-
-#define isodigit(c) ((c) >= '0' && (c) <= '7')
-#define octtobin(c) ((c) - '0')
-#define hextobin(c) ((c) >= 'A' && (c) <= 'F' ? c - 'A' + 10 : (c) >= 'a' && (c) <= 'f' ? c - 'a' + 10 : c - '0')
-
#ifdef SHELL
#define main printfcmd
-#ifdef __APPLE__
#include "bltin/bltin.h"
+#include "memalloc.h"
#else
-#include "../../bin/sh/bltin/bltin.h"
-#endif
-
-#ifdef __STDC__
-#include <stdarg.h>
-#else
-#include <vararg.h>
-#endif
-
-static void warnx __P((const char *fmt, ...));
-
-static void
-#ifdef __STDC__
-warnx(const char *fmt, ...)
-#else
-warnx(fmt, va_alist)
- const char *fmt;
- va_dcl
+#define warnx1(a, b, c) warnx(a)
+#define warnx2(a, b, c) warnx(a, b)
+#define warnx3(a, b, c) warnx(a, b, c)
#endif
-{
-
- char buf[64];
- va_list ap;
-#ifdef __STDC__
- va_start(ap, fmt);
-#else
- va_start(ap);
+#ifndef BUILTIN
+#include <locale.h>
#endif
- vsprintf(buf, fmt, ap);
- va_end(ap);
-
- error(buf);
-}
-#endif /* SHELL */
-#define PF(f, func) { \
- if (fieldwidth) \
- if (precision) \
- (void)printf(f, fieldwidth, precision, func); \
+#define PF(f, func) do { \
+ char *b = NULL; \
+ if (havewidth) \
+ if (haveprec) \
+ (void)asprintf(&b, f, fieldwidth, precision, func); \
else \
- (void)printf(f, fieldwidth, func); \
- else if (precision) \
- (void)printf(f, precision, func); \
+ (void)asprintf(&b, f, fieldwidth, func); \
+ else if (haveprec) \
+ (void)asprintf(&b, f, precision, func); \
else \
- (void)printf(f, func); \
-}
+ (void)asprintf(&b, f, func); \
+ if (b) { \
+ (void)fputs(b, stdout); \
+ free(b); \
+ } \
+} while (0)
+
+static int asciicode(void);
+static char *doformat(char *, int *);
+static int escape(char *, int, size_t *);
+static int getchr(void);
+static int getfloating(long double *, int);
+static int getint(int *);
+static int getnum(intmax_t *, uintmax_t *, int);
+static const char
+ *getstr(void);
+static char *mknum(char *, int);
+static void usage(void);
+
+static char **gargv;
int
#ifdef BUILTIN
-progprintf(argc, argv)
+progprintf(int argc, char *argv[])
#else
-main(argc, argv)
+main(int argc, char *argv[])
#endif
- int argc;
- char *argv[];
{
- char *fmt, *start;
- int fieldwidth, precision;
- char convch, nextch;
- char *format;
- int ch;
+ size_t len;
+ int ch, chopped, end, rval;
+ char *format, *fmt, *start;
-#if !defined(SHELL) && !defined(BUILTIN)
- (void)setlocale (LC_ALL, "");
+#ifndef BUILTIN
+ (void) setlocale(LC_NUMERIC, "");
#endif
-
- while ((ch = getopt(argc, argv, "")) != -1) {
+ while ((ch = getopt(argc, argv, "")) != -1)
switch (ch) {
case '?':
default:
usage();
return (1);
}
- }
argc -= optind;
argv += optind;
return (1);
}
- format = *argv;
+ /*
+ * Basic algorithm is to scan the format string for conversion
+ * specifications -- once one is found, find out if the field
+ * width or precision is a '*'; if it is, gather up value. Note,
+ * format strings are reused as necessary to use up the provided
+ * arguments, arguments of zero/null string are provided to use
+ * up the format string.
+ */
+ fmt = format = *argv;
+ chopped = escape(fmt, 1, &len); /* backslash interpretation */
+ rval = end = 0;
gargv = ++argv;
-
-#define SKIP1 "#-+ 0"
-#define SKIP2 "*0123456789"
- do {
- /*
- * Basic algorithm is to scan the format string for conversion
- * specifications -- once one is found, find out if the field
- * width or precision is a '*'; if it is, gather up value.
- * Note, format strings are reused as necessary to use up the
- * provided arguments, arguments of zero/null string are
- * provided to use up the format string.
- */
-
- /* find next format specification */
- for (fmt = format; *fmt; fmt++) {
- switch (*fmt) {
- case '%':
- start = fmt++;
-
- if (*fmt == '%') {
- (void)putchar('%');
- break;
- } else if (*fmt == 'b') {
- char *p = getstr();
- if (print_escape_str(p)) {
- return (rval);
- }
- break;
- }
-
- /* skip to field width */
- for (; strchr(SKIP1, *fmt); ++fmt) ;
- fieldwidth = *fmt == '*' ? getint() : 0;
-
- /* skip to possible '.', get following precision */
- for (; strchr(SKIP2, *fmt); ++fmt) ;
- if (*fmt == '.')
- ++fmt;
- precision = *fmt == '*' ? getint() : 0;
-
- for (; strchr(SKIP2, *fmt); ++fmt) ;
- if (!*fmt) {
- warnx ("missing format character");
- return(1);
- }
-
- convch = *fmt;
- nextch = *(fmt + 1);
- *(fmt + 1) = '\0';
- switch(convch) {
- case 'c': {
- char p = getchr();
- PF(start, p);
- break;
- }
- case 's': {
- char *p = getstr();
- PF(start, p);
- break;
- }
- case 'd':
- case 'i': {
- char *f = mklong(start, convch);
- long p = getlong();
- PF(f, p);
- break;
+ for (;;) {
+ start = fmt;
+ while (fmt < format + len) {
+ if (fmt[0] == '%') {
+ fwrite(start, 1, fmt - start, stdout);
+ if (fmt[1] == '%') {
+ /* %% prints a % */
+ putchar('%');
+ fmt += 2;
+ } else {
+ fmt = doformat(fmt, &rval);
+ if (fmt == NULL)
+ return (1);
+ end = 0;
}
- case 'o':
- case 'u':
- case 'x':
- case 'X': {
- char *f = mklong(start, convch);
- unsigned long p = getulong();
- PF(f, p);
- break;
- }
- case 'e':
- case 'E':
- case 'f':
- case 'g':
- case 'G': {
- double p = getdouble();
- PF(start, p);
- break;
- }
- default:
- warnx ("%s: invalid directive", start);
- return(1);
- }
- *(fmt + 1) = nextch;
- break;
-
- case '\\':
- fmt += print_escape(fmt);
- break;
-
- default:
- (void)putchar(*fmt);
- break;
- }
+ start = fmt;
+ } else
+ fmt++;
}
- } while (gargv > argv && *gargv);
- return (rval);
+ if (end == 1) {
+ warnx1("missing format character", NULL, NULL);
+ return (1);
+ }
+ fwrite(start, 1, fmt - start, stdout);
+ if (chopped || !*gargv)
+ return (rval);
+ /* Restart at the beginning of the format string. */
+ fmt = format;
+ end = 1;
+ }
+ /* NOTREACHED */
}
-/*
- * Print SysV echo(1) style escape string
- * Halts processing string and returns 1 if a \c escape is encountered.
- */
-static int
-print_escape_str(str)
- const char *str;
+static char *
+doformat(char *start, int *rval)
{
- int value;
- int c;
-
- while (*str) {
- if (*str == '\\') {
- str++;
- /*
- * %b string octal constants are not like those in C.
- * They start with a \0, and are followed by 0, 1, 2,
- * or 3 octal digits.
- */
- if (*str == '0') {
- str++;
- for (c = 3, value = 0; c-- && isodigit(*str); str++) {
- value <<= 3;
- value += octtobin(*str);
- }
- (void)putchar(value);
- str--;
- } else if (*str == 'c') {
- return 1;
- } else {
- str--;
- str += print_escape(str);
- }
+ static const char skip1[] = "#'-+ 0";
+ static const char skip2[] = "0123456789";
+ char *fmt;
+ int fieldwidth, haveprec, havewidth, mod_ldbl, precision;
+ char convch, nextch;
+
+ fmt = start + 1;
+ /* skip to field width */
+ fmt += strspn(fmt, skip1);
+ if (*fmt == '*') {
+ if (getint(&fieldwidth))
+ return (NULL);
+ havewidth = 1;
+ ++fmt;
+ } else {
+ havewidth = 0;
+
+ /* skip to possible '.', get following precision */
+ fmt += strspn(fmt, skip2);
+ }
+ if (*fmt == '.') {
+ /* precision present? */
+ ++fmt;
+ if (*fmt == '*') {
+ if (getint(&precision))
+ return (NULL);
+ haveprec = 1;
+ ++fmt;
} else {
- (void)putchar(*str);
+ haveprec = 0;
+
+ /* skip to conversion char */
+ fmt += strspn(fmt, skip2);
}
- str++;
+ } else
+ haveprec = 0;
+ if (!*fmt) {
+ warnx1("missing format character", NULL, NULL);
+ return (NULL);
}
- return 0;
-}
-
-/*
- * Print "standard" escape characters
- */
-static size_t
-print_escape(str)
- const char *str;
-{
- const char *start = str;
- int value;
- int c;
-
- str++;
-
- switch (*str) {
- case '0': case '1': case '2': case '3':
- case '4': case '5': case '6': case '7':
- for (c = 3, value = 0; c-- && isodigit(*str); str++) {
- value <<= 3;
- value += octtobin(*str);
+ /*
+ * Look for a length modifier. POSIX doesn't have these, so
+ * we only support them for floating-point conversions, which
+ * are extensions. This is useful because the L modifier can
+ * be used to gain extra range and precision, while omitting
+ * it is more likely to produce consistent results on different
+ * architectures. This is not so important for integers
+ * because overflow is the only bad thing that can happen to
+ * them, but consider the command printf %a 1.1
+ */
+ if (*fmt == 'L') {
+ mod_ldbl = 1;
+ fmt++;
+ if (!strchr("aAeEfFgG", *fmt)) {
+ warnx2("bad modifier L for %%%c", *fmt, NULL);
+ return (NULL);
}
- (void)putchar(value);
- return str - start - 1;
- /* NOTREACHED */
-
- case 'x':
- str++;
- for (value = 0; isxdigit(*str); str++) {
- value <<= 4;
- value += hextobin(*str);
- }
- if (value > UCHAR_MAX) {
- warnx ("escape sequence out of range for character");
- rval = 1;
- }
- (void)putchar (value);
- return str - start - 1;
- /* NOTREACHED */
-
- case '\\': /* backslash */
- (void)putchar('\\');
- break;
-
- case '\'': /* single quote */
- (void)putchar('\'');
- break;
+ } else {
+ mod_ldbl = 0;
+ }
- case '"': /* double quote */
- (void)putchar('"');
- break;
+ convch = *fmt;
+ nextch = *++fmt;
+ *fmt = '\0';
+ switch (convch) {
+ case 'b': {
+ size_t len;
+ char *p;
+ int getout;
- case 'a': /* alert */
-#ifdef __STDC__
- (void)putchar('\a');
+#ifdef SHELL
+ p = savestr(getstr());
#else
- (void)putchar(007);
+ p = strdup(getstr());
#endif
- break;
-
- case 'b': /* backspace */
- (void)putchar('\b');
- break;
-
- case 'e': /* escape */
-#ifdef __GNUC__
- (void)putchar('\e');
+ if (p == NULL) {
+ warnx2("%s", strerror(ENOMEM), NULL);
+ return (NULL);
+ }
+ getout = escape(p, 0, &len);
+ *(fmt - 1) = 's';
+ PF(start, p);
+ *(fmt - 1) = 'b';
+#ifdef SHELL
+ ckfree(p);
#else
- (void)putchar(033);
+ free(p);
#endif
+ if (getout)
+ return (fmt);
break;
+ }
+ case 'c': {
+ char p;
- case 'f': /* form-feed */
- (void)putchar('\f');
- break;
-
- case 'n': /* newline */
- (void)putchar('\n');
+ p = getchr();
+ PF(start, p);
break;
+ }
+ case 's': {
+ const char *p;
- case 'r': /* carriage-return */
- (void)putchar('\r');
+ p = getstr();
+ PF(start, p);
break;
-
- case 't': /* tab */
- (void)putchar('\t');
+ }
+ case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
+ char *f;
+ intmax_t val;
+ uintmax_t uval;
+ int signedconv;
+
+ signedconv = (convch == 'd' || convch == 'i');
+ if ((f = mknum(start, convch)) == NULL)
+ return (NULL);
+ if (getnum(&val, &uval, signedconv))
+ *rval = 1;
+ if (signedconv)
+ PF(f, val);
+ else
+ PF(f, uval);
break;
-
- case 'v': /* vertical-tab */
- (void)putchar('\v');
+ }
+ case 'e': case 'E':
+ case 'f': case 'F':
+ case 'g': case 'G':
+ case 'a': case 'A': {
+ long double p;
+
+ if (getfloating(&p, mod_ldbl))
+ *rval = 1;
+ if (mod_ldbl)
+ PF(start, p);
+ else
+ PF(start, (double)p);
break;
-
+ }
default:
- (void)putchar(*str);
- warnx("unknown escape sequence `\\%c'", *str);
- rval = 1;
- break;
+ warnx2("illegal format character %c", convch, NULL);
+ return (NULL);
}
-
- return 1;
+ *fmt = nextch;
+ return (fmt);
}
static char *
-mklong(str, ch)
- const char *str;
- char ch;
+mknum(char *str, int ch)
{
- static char copy[64];
- size_t len;
+ static char *copy;
+ static size_t copy_size;
+ char *newcopy;
+ size_t len, newlen;
len = strlen(str) + 2;
- (void)memmove(copy, str, len - 3);
- copy[len - 3] = 'l';
+ if (len > copy_size) {
+ newlen = ((len + 1023) >> 10) << 10;
+#ifdef SHELL
+ if ((newcopy = ckrealloc(copy, newlen)) == NULL)
+#else
+ if ((newcopy = realloc(copy, newlen)) == NULL)
+#endif
+ {
+ warnx2("%s", strerror(ENOMEM), NULL);
+ return (NULL);
+ }
+ copy = newcopy;
+ copy_size = newlen;
+ }
+
+ memmove(copy, str, len - 3);
+ copy[len - 3] = 'j';
copy[len - 2] = ch;
copy[len - 1] = '\0';
- return (copy);
+ return (copy);
+}
+
+static int
+escape(char *fmt, int percent, size_t *len)
+{
+ char *save, *store;
+ int value, c;
+
+ for (save = store = fmt; (c = *fmt); ++fmt, ++store) {
+ if (c != '\\') {
+ *store = c;
+ continue;
+ }
+ switch (*++fmt) {
+ case '\0': /* EOS, user error */
+ *store = '\\';
+ *++store = '\0';
+ *len = store - save;
+ return (0);
+ case '\\': /* backslash */
+ case '\'': /* single quote */
+ *store = *fmt;
+ break;
+ case 'a': /* bell/alert */
+ *store = '\a';
+ break;
+ case 'b': /* backspace */
+ *store = '\b';
+ break;
+ case 'c':
+ *store = '\0';
+ *len = store - save;
+ return (1);
+ case 'f': /* form-feed */
+ *store = '\f';
+ break;
+ case 'n': /* newline */
+ *store = '\n';
+ break;
+ case 'r': /* carriage-return */
+ *store = '\r';
+ break;
+ case 't': /* horizontal tab */
+ *store = '\t';
+ break;
+ case 'v': /* vertical tab */
+ *store = '\v';
+ break;
+ /* octal constant */
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ for (c = *fmt == '0' ? 4 : 3, value = 0;
+ c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
+ value <<= 3;
+ value += *fmt - '0';
+ }
+ --fmt;
+ if (percent && value == '%') {
+ *store++ = '%';
+ *store = '%';
+ } else
+ *store = value;
+ break;
+ default:
+ *store = *fmt;
+ break;
+ }
+ }
+ *store = '\0';
+ *len = store - save;
+ return (0);
}
static int
-getchr()
+getchr(void)
{
if (!*gargv)
return ('\0');
return ((int)**gargv++);
}
-static char *
-getstr()
+static const char *
+getstr(void)
{
if (!*gargv)
return ("");
return (*gargv++);
}
-static char *Number = "+-.0123456789";
static int
-getint()
+getint(int *ip)
{
- if (!*gargv)
- return(0);
-
- if (strchr(Number, **gargv))
- return(atoi(*gargv++));
+ intmax_t val;
+ uintmax_t uval;
+ int rval;
- return 0;
+ if (getnum(&val, &uval, 1))
+ return (1);
+ rval = 0;
+ if (val < INT_MIN || val > INT_MAX) {
+ warnx3("%s: %s", *gargv, strerror(ERANGE));
+ rval = 1;
+ }
+ *ip = (int)val;
+ return (rval);
}
-static long
-getlong()
+static int
+getnum(intmax_t *ip, uintmax_t *uip, int signedconv)
{
- long val;
char *ep;
+ int rval;
- if (!*gargv)
- return(0L);
-
- if (**gargv == '\"' || **gargv == '\'')
- return (long) *((*gargv++)+1);
-
+ if (!*gargv) {
+ *ip = 0;
+ return (0);
+ }
+ if (**gargv == '"' || **gargv == '\'') {
+ if (signedconv)
+ *ip = asciicode();
+ else
+ *uip = asciicode();
+ return (0);
+ }
+ rval = 0;
errno = 0;
- val = strtol (*gargv, &ep, 0);
- check_conversion(*gargv++, ep);
- return val;
+ if (signedconv)
+ *ip = strtoimax(*gargv, &ep, 0);
+ else
+ *uip = strtoumax(*gargv, &ep, 0);
+ if (ep == *gargv) {
+ warnx2("%s: expected numeric value", *gargv, NULL);
+ rval = 1;
+ }
+ else if (*ep != '\0') {
+ warnx2("%s: not completely converted", *gargv, NULL);
+ rval = 1;
+ }
+ if (errno == ERANGE) {
+ warnx3("%s: %s", *gargv, strerror(ERANGE));
+ rval = 1;
+ }
+ ++gargv;
+ return (rval);
}
-static unsigned long
-getulong()
+static int
+getfloating(long double *dp, int mod_ldbl)
{
- unsigned long val;
char *ep;
+ int rval;
- if (!*gargv)
- return(0UL);
-
- if (**gargv == '\"' || **gargv == '\'')
- return (unsigned long) *((*gargv++)+1);
-
+ if (!*gargv) {
+ *dp = 0.0;
+ return (0);
+ }
+ if (**gargv == '"' || **gargv == '\'') {
+ *dp = asciicode();
+ return (0);
+ }
+ rval = 0;
errno = 0;
- val = strtoul (*gargv, &ep, 0);
- check_conversion(*gargv++, ep);
- return val;
+ if (mod_ldbl)
+ *dp = strtold(*gargv, &ep);
+ else
+ *dp = strtod(*gargv, &ep);
+ if (ep == *gargv) {
+ warnx2("%s: expected numeric value", *gargv, NULL);
+ rval = 1;
+ } else if (*ep != '\0') {
+ warnx2("%s: not completely converted", *gargv, NULL);
+ rval = 1;
+ }
+ if (errno == ERANGE) {
+ warnx3("%s: %s", *gargv, strerror(ERANGE));
+ rval = 1;
+ }
+ ++gargv;
+ return (rval);
}
-static double
-getdouble()
+static int
+asciicode(void)
{
- double val;
- char *ep;
-
- if (!*gargv)
- return(0.0);
-
- if (**gargv == '\"' || **gargv == '\'')
- return (double) *((*gargv++)+1);
-
- errno = 0;
- val = strtod (*gargv, &ep);
- check_conversion(*gargv++, ep);
- return val;
-}
+ int ch;
-static void
-check_conversion(s, ep)
- const char *s;
- const char *ep;
-{
- if (*ep) {
- if (ep == s)
- warnx ("%s: expected numeric value", s);
- else
- warnx ("%s: not completely converted", s);
- rval = 1;
- } else if (errno == ERANGE) {
- warnx ("%s: %s", s, strerror(ERANGE));
- rval = 1;
- }
+ ch = **gargv;
+ if (ch == '\'' || ch == '"')
+ ch = (*gargv)[1];
+ ++gargv;
+ return (ch);
}
static void
-usage()
+usage(void)
{
- (void)fprintf(stderr, "usage: printf format [arg ...]\n");
+ (void)fprintf(stderr, "usage: printf format [arguments ...]\n");
}
CFILES = sleep.c
MANPAGES = sleep.1
-Extra_CC_Flags = -Wall -mdynamic-no-pic
+Extra_CC_Flags = -Wall -Werror -mdynamic-no-pic
+Extra_CC_Flags += -D__FBSDID=__RCSID
Extra_LD_Flags = -dead_strip
include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make
-.\" $NetBSD: sleep.1,v 1.11 1997/09/14 07:31:56 lukem Exp $
-.\"
+.\"-
.\" Copyright (c) 1990, 1993, 1994
.\" The Regents of the University of California. All rights reserved.
.\"
.\" 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.
.\" SUCH DAMAGE.
.\"
.\" @(#)sleep.1 8.3 (Berkeley) 4/18/94
+.\" $FreeBSD: src/bin/sleep/sleep.1,v 1.22 2005/01/16 16:41:59 ru Exp $
.\"
.Dd April 18, 1994
.Dt SLEEP 1
.Sh DESCRIPTION
The
.Nm
-utility
+command
suspends execution for a minimum of
.Ar seconds .
-It is usually used to schedule the execution of other commands (see
-.Sx EXAMPLES
-below).
.Pp
-The
+If the
.Nm
-utility exits with one of the following values:
-.Bl -tag -width flag
-.It Li \&0
-On successful completion, or if the signal
+command receives a signal, it takes the standard action.
+.Sh IMPLEMENTATION NOTES
+The
.Dv SIGALRM
-was received.
-.It Li \&>\&0
-An error occurred.
-.El
+signal is not handled specially by this implementation.
+.Pp
+The
+.Nm
+command will accept and honor a non-integer number of specified seconds
+(with a
+.Ql .\&
+character as a decimal point).
+.Bf Sy
+This is a non-portable extension, and its use will nearly guarantee that
+a shell script will not execute properly on another system.
+.Ef
+.Sh EXIT STATUS
+.Ex -std
.Sh EXAMPLES
To schedule the execution of a command for
.Va x
-number seconds later:
+number seconds later (with
+.Xr csh 1 ) :
.Pp
.Dl (sleep 1800; sh command_file >& errors)&
.Pp
This incantation would wait a half hour before
-running the script command_file. (See the
+running the script command_file.
+(See the
.Xr at 1
utility.)
.Pp
is done courteously by sleeping for 70 seconds in between each
awk job.
.Sh SEE ALSO
-.Xr setitimer 2 ,
-.Xr sleep 3 ,
-.Xr at 1
+.Xr nanosleep 2 ,
+.Xr sleep 3
.Sh STANDARDS
The
.Nm
command is expected to be
.St -p1003.2
compatible.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v4 .
-/* $NetBSD: sleep.c,v 1.15 1998/07/28 11:41:58 mycroft Exp $ */
-
-/*
+/*-
* Copyright (c) 1988, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* 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.
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
+#if 0
#ifndef lint
-__COPYRIGHT("@(#) Copyright (c) 1988, 1993, 1994\n\
- The Regents of the University of California. All rights reserved.\n");
+static char const copyright[] =
+"@(#) Copyright (c) 1988, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
#ifndef lint
-#if 0
static char sccsid[] = "@(#)sleep.c 8.3 (Berkeley) 4/2/94";
-#else
-__RCSID("$NetBSD: sleep.c,v 1.15 1998/07/28 11:41:58 mycroft Exp $");
-#endif
#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/bin/sleep/sleep.c,v 1.20 2005/08/07 09:11:38 stefanf Exp $");
-#include <time.h>
#include <ctype.h>
-#include <math.h>
-#include <signal.h>
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
+#include <time.h>
#include <unistd.h>
-#include <locale.h>
+#include <string.h>
-void usage __P((void));
-void alarmhandle __P((int));
-int main __P((int, char *[]));
+void usage(void);
int
-main(argc, argv)
- int argc;
- char *argv[];
+main(int argc, char *argv[])
{
- char *arg, *temp;
- double val, ival, fval;
- struct timespec ntime;
- int fracflag;
- int ch;
-
- (void)setlocale(LC_ALL, "");
+ struct timespec time_to_sleep;
+ long l;
+ int neg;
+ char *p;
+
+ if (argc == 2) {
+ p = argv[1];
+ } else if (argc == 3 && !strcmp("--", argv[1])) {
+ // POSIX issue: "sleep -- 3" should be the same as "sleep 3",
+ // normally getopt makes this kind of thing work, but sleep has
+ // no options, so we do it "the easy way"
+ p = argv[2];
+ } else {
+ usage();
+ return(1);
+ }
- (void)signal(SIGALRM, alarmhandle);
+ /* Skip over leading whitespaces. */
+ while (isspace((unsigned char)*p))
+ ++p;
- while ((ch = getopt(argc, argv, "")) != -1)
- switch(ch) {
- case '?':
- default:
+ /* Check for optional `+' or `-' sign. */
+ neg = 0;
+ if (*p == '-') {
+ neg = 1;
+ ++p;
+ if (!isdigit((unsigned char)*p) && *p != '.') {
usage();
+ return(1);
}
- argc -= optind;
- argv += optind;
-
- if (argc != 1)
- usage();
-
- /*
- * Okay, why not just use atof for everything? Why bother
- * checking if there is a fraction in use? Because the old
- * sleep handled the full range of integers, that's why, and a
- * double can't handle a large long. This is fairly useless
- * given how large a number a double can hold on most
- * machines, but now we won't ever have trouble. If you want
- * 1000000000.9 seconds of sleep, well, that's your
- * problem. Why use an isdigit() check instead of checking for
- * a period? Because doing it this way means locales will be
- * handled transparently by the atof code.
- */
- fracflag = 0;
- arg = *argv;
- for (temp = arg; *temp != '\0'; temp++)
- if (!isdigit(*temp))
- fracflag++;
-
- if (fracflag) {
- val = atof(arg);
- if (val <= 0)
- exit(0);
- ival = floor(val);
- fval = (1000000000 * (val-ival));
- ntime.tv_sec = ival;
- ntime.tv_nsec = fval;
}
- else{
- ntime.tv_sec = atol(arg);
- if (ntime.tv_sec <= 0)
- exit(0);
- ntime.tv_nsec = 0;
+ else if (*p == '+')
+ ++p;
+
+ /* Calculate seconds. */
+ if (isdigit((unsigned char)*p)) {
+ l = strtol(p, &p, 10);
+ if (l > INT_MAX) {
+ /*
+ * Avoid overflow when `seconds' is huge. This assumes
+ * that the maximum value for a time_t is <= INT_MAX.
+ */
+ l = INT_MAX;
+ }
+ } else
+ l = 0;
+ time_to_sleep.tv_sec = (time_t)l;
+
+ /* Calculate nanoseconds. */
+ time_to_sleep.tv_nsec = 0;
+
+ if (*p == '.') { /* Decimal point. */
+ l = 100000000L;
+ do {
+ if (isdigit((unsigned char)*++p))
+ time_to_sleep.tv_nsec += (*p - '0') * l;
+ else
+ break;
+ l /= 10;
+ } while (l);
}
- (void)nanosleep(&ntime, NULL);
+ if (!neg && (time_to_sleep.tv_sec > 0 || time_to_sleep.tv_nsec > 0))
+ (void)nanosleep(&time_to_sleep, (struct timespec *)NULL);
- exit(0);
- /* NOTREACHED */
+ return(0);
}
void
-usage()
+usage(void)
{
- (void)fputs("usage: sleep seconds\n", stderr);
- exit(1);
- /* NOTREACHED */
-}
+ const char msg[] = "usage: sleep seconds\n";
-/* ARGSUSED */
-void
-alarmhandle(i)
- int i;
-{
- _exit(0);
- /* NOTREACHED */
+ write(STDERR_FILENO, msg, sizeof(msg) - 1);
}
CFILES = su.c
MANPAGES = su.1
-Extra_CC_Flags = -Wall -mdynamic-no-pic
-Extra_LD_Flags = -dead_strip \
- -lpam -lpam_misc -lbsm
+Extra_CC_Flags = -Wall -mdynamic-no-pic -D__FBSDID=__RCSID
+Extra_LD_Flags = -dead_strip -lpam -lbsm
include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make
Install_Program_Mode = 04555
+
+after_install:
+ $(MKDIR) $(DSTROOT)$(ETCDIR)/pam.d
+ $(INSTALL_FILE) su.pam $(DSTROOT)$(ETCDIR)/pam.d/su
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.\" from: @(#)su.1 8.2 (Berkeley) 4/18/94
-.\" $NetBSD: su.1,v 1.14 1997/10/19 23:31:52 lukem Exp $
+.\" @(#)su.1 8.2 (Berkeley) 4/18/94
+.\" $FreeBSD: src/usr.bin/su/su.1,v 1.40 2007/07/24 06:41:07 delphij Exp $
.\"
-.Dd April 18, 1994
+.Dd September 13, 2006
.Dt SU 1
.Os
.Sh NAME
.Op Fl flm
.Op Ar login Op Ar args
.Sh DESCRIPTION
+The
.Nm
-requests the password for
-.Ar login
-and switches to
-that user and group ID after obtaining proper authentication.
-A shell is then executed, and any additional
-.Ar "shell arguments"
-after the login name
-are passed to the shell.
-If
-.Nm
-is executed by root, no password is requested and a shell
-with the appropriate user ID is executed.
+utility requests appropriate user credentials via PAM
+and switches to that user ID
+(the default user is the superuser).
+A shell is then executed.
+.Pp
+PAM is used to set the policy
+.Xr su 1
+will use.
+In particular, by default only users in the
+.Dq Li admin
+or
+.Dq Li wheel
+groups can switch to UID 0
+.Pq Dq Li root .
+This group requirement may be changed by modifying the
+.Dq Li pam_group
+section of
+.Pa /etc/pam.d/su .
+See
+.Xr pam_group 8
+for details on how to modify this setting.
+.Pp
+By default, the environment is unmodified with the exception of
+.Ev USER ,
+.Ev HOME ,
+and
+.Ev SHELL .
+.Ev HOME
+and
+.Ev SHELL
+are set to the target login's default values.
+.Ev USER
+is set to the target login, unless the target login has a user ID of 0,
+in which case it is unmodified.
+The invoked shell is the one belonging to the target login.
+This is the traditional behavior of
+.Nm .
.Pp
The options are as follows:
.Bl -tag -width Ds
The invoked shell is the target login's, and
.Nm
will change directory to the target login's home directory.
-This option is identical to just passing "-", as in "su -".
+.It Fl
+(no letter) The same as
+.Fl l .
.It Fl m
Leave the environment unmodified.
The invoked shell is your login shell, and no directory changes are made.
.Pp
The
.Fl l
+(or
+.Fl )
and
.Fl m
options are mutually exclusive; the last one specified
overrides any previous ones.
.Pp
-Only users in group
-.Dq wheel
-(normally gid 0)
-or group
-.Dq admin
-(normally gid 20) can
+If the optional
+.Ar args
+are provided on the command line, they are passed to the login shell of
+the target login.
+Note that all command line arguments before the target login name are
+processed by
.Nm
-to
-.Dq root .
+itself, everything after the target login name gets passed to the login
+shell.
.Pp
By default (unless the prompt is reset by a startup file) the super-user
prompt is set to
.Dq Sy \&#
to remind one of its awesome power.
-.Sh SEE ALSO
-.Xr csh 1 ,
-.Xr login 1 ,
-.Xr sh 1 ,
-.Xr skey 1 ,
-.Xr kinit 1 ,
-.Xr kerberos 1 ,
-.Xr passwd 5 ,
-.Xr group 5 ,
-.Xr environ 7
.Sh ENVIRONMENT
Environment variables used by
-.Nm
-:
-.Bl -tag -width "HOME"
+.Nm :
+.Bl -tag -width HOME
.It Ev HOME
Default home directory of real user ID unless modified as
specified above.
.Nm
unless the user ID is 0 (root).
.El
+.Sh FILES
+.Bl -tag -width ".Pa /etc/pam.d/su" -compact
+.It Pa /etc/pam.d/su
+PAM configuration for
+.Nm .
+.El
+.Sh EXAMPLES
+.Bl -tag -width 5n -compact
+.It Li "su man -c catman"
+Runs the command
+.Li catman
+as user
+.Li man .
+You will be asked for man's password unless your real UID is 0.
+.It Li "su man -c 'catman /usr/share/man /usr/local/man'"
+Same as above, but the target command consists of more than a
+single word and hence is quoted for use with the
+.Fl c
+option being passed to the shell.
+(Most shells expect the argument to
+.Fl c
+to be a single word).
+.It Li "su -l foo"
+Simulate a login for user foo.
+.It Li "su - foo"
+Same as above.
+.It Li "su -"
+Simulate a login for root.
+.El
+.Sh SEE ALSO
+.Xr csh 1 ,
+.Xr sh 1 ,
+.Xr group 5 ,
+.Xr passwd 5 ,
+.Xr environ 7 ,
+.Xr pam_group 8
.Sh HISTORY
A
.Nm
command appeared in
-.At v7 .
+.At v1 .
/*
+ * Copyright (c) 2002, 2005 Networks Associates Technologies, Inc.
+ * All rights reserved.
+ *
+ * Portions of this software were developed for the FreeBSD Project by
+ * ThinkSec AS and NAI Labs, the Security Research Division of Network
+ * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+/*-
* Copyright (c) 1988, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
-#ifndef lint
#if 0
+#ifndef lint
static char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94";
-#endif
-static const char rcsid[] =
- "$FreeBSD: src/usr.bin/su/su.c,v 1.48 2002/01/24 16:20:17 des Exp $";
#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/su/su.c,v 1.87 2007/10/18 11:05:30 davidxu Exp $");
#include <sys/param.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
+#ifdef USE_BSM_AUDIT
+#include <bsm/libbsm.h>
+#include <bsm/audit_uevents.h>
+#endif
+
#include <err.h>
#include <errno.h>
#include <grp.h>
+#ifndef __APPLE__
+#include <login_cap.h>
+#endif /* !__APPLE__ */
#include <paths.h>
#include <pwd.h>
#include <signal.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
-
-#include <pam/pam_appl.h>
-#include <pam/pam_misc.h>
-
-#define PAM_END() do { \
- int local_ret; \
- if (pamh != NULL && creds_set) { \
- local_ret = pam_setcred(pamh, PAM_DELETE_CRED); \
- if (local_ret != PAM_SUCCESS) \
- syslog(LOG_ERR, "pam_setcred: %s", \
- pam_strerror(pamh, local_ret)); \
- local_ret = pam_close_session(pamh, 0); \
- local_ret = pam_end(pamh, local_ret); \
- if (local_ret != PAM_SUCCESS) \
- syslog(LOG_ERR, "pam_end: %s", \
- pam_strerror(pamh, local_ret)); \
- } \
+#include <stdarg.h>
+
+#include <security/pam_appl.h>
+#include <security/openpam.h>
+
+#define PAM_END() do { \
+ int local_ret; \
+ if (pamh != NULL) { \
+ local_ret = pam_setcred(pamh, PAM_DELETE_CRED); \
+ if (local_ret != PAM_SUCCESS) \
+ syslog(LOG_ERR, "pam_setcred: %s", \
+ pam_strerror(pamh, local_ret)); \
+ if (asthem) { \
+ local_ret = pam_close_session(pamh, 0); \
+ if (local_ret != PAM_SUCCESS) \
+ syslog(LOG_ERR, "pam_close_session: %s",\
+ pam_strerror(pamh, local_ret)); \
+ } \
+ local_ret = pam_end(pamh, local_ret); \
+ if (local_ret != PAM_SUCCESS) \
+ syslog(LOG_ERR, "pam_end: %s", \
+ pam_strerror(pamh, local_ret)); \
+ } \
} while (0)
-#define PAM_SET_ITEM(what, item) do { \
- int local_ret; \
- local_ret = pam_set_item(pamh, what, item); \
- if (local_ret != PAM_SUCCESS) { \
- syslog(LOG_ERR, "pam_set_item(" #what "): %s", \
- pam_strerror(pamh, local_ret)); \
- errx(1, "pam_set_item(" #what "): %s", \
- pam_strerror(pamh, local_ret)); \
- } \
+#define PAM_SET_ITEM(what, item) do { \
+ int local_ret; \
+ local_ret = pam_set_item(pamh, what, item); \
+ if (local_ret != PAM_SUCCESS) { \
+ syslog(LOG_ERR, "pam_set_item(" #what "): %s", \
+ pam_strerror(pamh, local_ret)); \
+ errx(1, "pam_set_item(" #what "): %s", \
+ pam_strerror(pamh, local_ret)); \
+ /* NOTREACHED */ \
+ } \
} while (0)
enum tristate { UNSET, YES, NO };
static pam_handle_t *pamh = NULL;
-static int creds_set = 0;
static char **environ_pam;
static char *ontty(void);
-static int chshell(char *);
-static void usage(void);
-static int export_pam_environment(void);
+static int chshell(const char *);
+static void usage(void) __dead2;
+static void export_pam_environment(void);
static int ok_to_export(const char *);
extern char **environ;
{
static char *cleanenv;
struct passwd *pwd;
- struct pam_conv conv = {misc_conv, NULL};
+ struct pam_conv conv = { openpam_ttyconv, NULL };
enum tristate iscsh;
+#ifndef __APPLE__
+ login_cap_t *lc;
+#endif /* !__APPLE__ */
union {
const char **a;
char * const *b;
- } np;
+ } np;
uid_t ruid;
- gid_t gid;
- int asme, ch, asthem, fastlogin, prio, i, setwhat, retcode,
- statusp, child_pid, child_pgrp, ret_pid;
+ pid_t child_pid, child_pgrp, pid;
+ int asme, ch, asthem, fastlogin, prio, i, retcode,
+ statusp, setmaclabel;
+#ifndef __APPLE__
+ u_int setwhat;
+#endif /* !__APPLE__ */
char *username, *class, shellbuf[MAXPATHLEN];
const char *p, *user, *shell, *mytty, **nargv;
+ struct sigaction sa, sa_int, sa_quit, sa_pipe;
+ int temp, fds[2];
+#ifdef USE_BSM_AUDIT
+ const char *aerr;
+ au_id_t auid;
+#endif
+#ifdef __APPLE__
+ /* 4043304 */
const char *avshell;
char avshellbuf[MAXPATHLEN];
+#endif /* __APPLE__ */
shell = class = cleanenv = NULL;
asme = asthem = fastlogin = statusp = 0;
user = "root";
iscsh = UNSET;
+ setmaclabel = 0;
#ifdef __APPLE__
while ((ch = getopt(argc, argv, "-flm")) != -1)
#else
- while ((ch = getopt(argc, argv, "-flmc:")) != -1)
+ while ((ch = getopt(argc, argv, "-flmsc:")) != -1)
#endif /* __APPLE__ */
switch ((char)ch) {
case 'f':
asthem = 0;
break;
#ifndef __APPLE__
+ case 's':
+ setmaclabel = 1;
+ break;
case 'c':
class = optarg;
break;
case '?':
default:
usage();
+ /* NOTREACHED */
}
if (optind < argc)
if (user == NULL)
usage();
+ /* NOTREACHED */
- if (strlen(user) > MAXLOGNAME - 1)
+ /*
+ * Try to provide more helpful debugging output if su(1) is running
+ * non-setuid, or was run from a file system not mounted setuid.
+ */
+ if (geteuid() != 0)
+ errx(1, "not running setuid");
+
+#ifdef USE_BSM_AUDIT
+ if (getauid(&auid) < 0 && errno != ENOSYS) {
+ syslog(LOG_AUTH | LOG_ERR, "getauid: %s", strerror(errno));
+ errx(1, "Permission denied");
+ }
+#endif
+ if (strlen(user) > MAXLOGNAME - 1) {
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid,
+ 1, EPERM, "username too long: '%s'", user))
+ errx(1, "Permission denied");
+#endif
errx(1, "username too long");
+ }
- nargv = malloc(sizeof(char *) * (argc + 4));
+ nargv = malloc(sizeof(char *) * (size_t)(argc + 4));
if (nargv == NULL)
errx(1, "malloc failure");
pwd = getpwnam(username);
if (username == NULL || pwd == NULL || pwd->pw_uid != ruid)
pwd = getpwuid(ruid);
- if (pwd == NULL)
+ if (pwd == NULL) {
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid, 1, EPERM,
+ "unable to determine invoking subject: '%s'", username))
+ errx(1, "Permission denied");
+#endif
errx(1, "who are you?");
- gid = pwd->pw_gid;
+ }
username = strdup(pwd->pw_name);
if (username == NULL)
errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
}
- PAM_SET_ITEM(PAM_RUSER, getlogin());
+ PAM_SET_ITEM(PAM_RUSER, username);
mytty = ttyname(STDERR_FILENO);
if (!mytty)
retcode = pam_authenticate(pamh, 0);
if (retcode != PAM_SUCCESS) {
- syslog(LOG_ERR, "pam_authenticate: %s",
- pam_strerror(pamh, retcode));
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid, 1, EPERM, "bad su %s to %s on %s",
+ username, user, mytty))
+ errx(1, "Permission denied");
+#endif
+ syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s",
+ username, user, mytty);
errx(1, "Sorry");
}
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid, 0, 0, "successful authentication"))
+ errx(1, "Permission denied");
+#endif
retcode = pam_get_item(pamh, PAM_USER, (const void **)&p);
if (retcode == PAM_SUCCESS)
user = p;
else
syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
pam_strerror(pamh, retcode));
+ pwd = getpwnam(user);
+ if (pwd == NULL) {
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid, 1, EPERM,
+ "unknown subject: %s", user))
+ errx(1, "Permission denied");
+#endif
+ errx(1, "unknown login: %s", user);
+ }
retcode = pam_acct_mgmt(pamh, 0);
if (retcode == PAM_NEW_AUTHTOK_REQD) {
retcode = pam_chauthtok(pamh,
PAM_CHANGE_EXPIRED_AUTHTOK);
if (retcode != PAM_SUCCESS) {
+#ifdef USE_BSM_AUDIT
+ aerr = pam_strerror(pamh, retcode);
+ if (aerr == NULL)
+ aerr = "Unknown PAM error";
+ if (audit_submit(AUE_su, auid, 1, EPERM,
+ "pam_chauthtok: %s", aerr))
+ errx(1, "Permission denied");
+#endif
syslog(LOG_ERR, "pam_chauthtok: %s",
pam_strerror(pamh, retcode));
errx(1, "Sorry");
}
}
if (retcode != PAM_SUCCESS) {
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid, 1, EPERM, "pam_acct_mgmt: %s",
+ pam_strerror(pamh, retcode)))
+ errx(1, "Permission denied");
+#endif
syslog(LOG_ERR, "pam_acct_mgmt: %s",
pam_strerror(pamh, retcode));
errx(1, "Sorry");
}
- /* get target login information, default to root */
- pwd = getpwnam(user);
- if (pwd == NULL)
- errx(1, "unknown login: %s", user);
+#ifndef __APPLE__
+ /* get target login information */
+ if (class == NULL)
+ lc = login_getpwclass(pwd);
+ else {
+ if (ruid != 0) {
+#ifdef USE_BSM_AUDIT
+ if (audit_submit(AUE_su, auid, 1, EPERM,
+ "only root may use -c"))
+ errx(1, "Permission denied");
+#endif
+ errx(1, "only root may use -c");
+ }
+ lc = login_getclass(class);
+ if (lc == NULL)
+ errx(1, "unknown class: %s", class);
+ }
+#endif /* !__APPLE__ */
/* if asme and non-standard target shell, must be root */
if (asme) {
if (ruid != 0 && !chshell(pwd->pw_shell))
- errx(1, "permission denied (shell).");
+ errx(1, "permission denied (shell)");
}
else if (pwd->pw_shell && *pwd->pw_shell) {
+#ifdef __APPLE__
+ /* 3825554 */
shell = strncpy(shellbuf, pwd->pw_shell, sizeof(shellbuf));
shellbuf[sizeof(shellbuf) - 1] = '\0';
+#else
+ shell = pwd->pw_shell;
+#endif /* __APPLE__ */
iscsh = UNSET;
}
else {
iscsh = NO;
}
- if ((p = strrchr(shell, '/')) != NULL)
- avshell = p+1;
- else
- avshell = shell;
-
/* if we're forking a csh, we want to slightly muck the args */
if (iscsh == UNSET) {
- iscsh = strcmp(avshell, "csh") ? (strcmp(avshell, "tcsh") ? NO : YES) : YES;
+ p = strrchr(shell, '/');
+ if (p)
+ ++p;
+ else
+ p = shell;
+ iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES;
}
setpriority(PRIO_PROCESS, 0, prio);
* PAM modules might add supplementary groups in pam_setcred(), so
* initialize them first.
*/
- if( initgroups(user, pwd->pw_gid) )
- err(1, "initgroups failed");
-
- retcode = pam_open_session(pamh, 0);
- if( retcode != PAM_SUCCESS ) {
- syslog(LOG_ERR, "pam_open_session(pamh, 0): %s",
- pam_strerror(pamh, retcode));
- }
+#ifdef __APPLE__
+ if (initgroups(user, pwd->pw_gid))
+ err(1, "initgroups");
+#else
+ if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
+ err(1, "setusercontext");
+#endif /* __APPLE__ */
retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
- if (retcode != PAM_SUCCESS)
- syslog(LOG_ERR, "pam_setcred(pamh, PAM_ESTABLISH_CRED): %s",
+ if (retcode != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_setcred: %s",
pam_strerror(pamh, retcode));
- else
- creds_set = 1;
+ errx(1, "failed to establish credentials.");
+ }
+ if (asthem) {
+ retcode = pam_open_session(pamh, 0);
+ if (retcode != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_open_session: %s",
+ pam_strerror(pamh, retcode));
+ errx(1, "failed to open session.");
+ }
+ }
/*
* We must fork() before setuid() because we need to call
* pam_setcred(pamh, PAM_DELETE_CRED) as root.
*/
-
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = SIG_IGN;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGINT, &sa, &sa_int);
+ sigaction(SIGQUIT, &sa, &sa_quit);
+ sigaction(SIGPIPE, &sa, &sa_pipe);
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGTSTP, &sa, NULL);
statusp = 1;
+ if (pipe(fds) == -1) {
+ PAM_END();
+ err(1, "pipe");
+ }
child_pid = fork();
switch (child_pid) {
default:
- while ((ret_pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGTTOU, &sa, NULL);
+ close(fds[0]);
+ setpgid(child_pid, child_pid);
+ if (tcgetpgrp(STDERR_FILENO) == getpgrp())
+ tcsetpgrp(STDERR_FILENO, child_pid);
+ close(fds[1]);
+ sigaction(SIGPIPE, &sa_pipe, NULL);
+ while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
if (WIFSTOPPED(statusp)) {
- child_pgrp = tcgetpgrp(1);
+ child_pgrp = getpgid(child_pid);
+ if (tcgetpgrp(STDERR_FILENO) == child_pgrp)
+ tcsetpgrp(STDERR_FILENO, getpgrp());
kill(getpid(), SIGSTOP);
- tcsetpgrp(1, child_pgrp);
- kill(child_pid, SIGCONT);
+ if (tcgetpgrp(STDERR_FILENO) == getpgrp()) {
+ child_pgrp = getpgid(child_pid);
+ tcsetpgrp(STDERR_FILENO, child_pgrp);
+ }
+ kill(child_pid, SIGCONT);
statusp = 1;
continue;
}
break;
}
- if (ret_pid == -1)
+ tcsetpgrp(STDERR_FILENO, getpgrp());
+ if (pid == -1)
err(1, "waitpid");
PAM_END();
exit(WEXITSTATUS(statusp));
case -1:
- err(1, "fork");
PAM_END();
- exit(1);
+ err(1, "fork");
case 0:
- if( setgid(pwd->pw_gid) )
+ close(fds[1]);
+ read(fds[0], &temp, 1);
+ close(fds[0]);
+ sigaction(SIGPIPE, &sa_pipe, NULL);
+ sigaction(SIGINT, &sa_int, NULL);
+ sigaction(SIGQUIT, &sa_quit, NULL);
+
+#ifdef __APPLE__
+ if (setgid(pwd->pw_gid))
err(1, "setgid");
/* Call initgroups(2) after setgid(2) to re-establish memberd */
- if( initgroups(user, pwd->pw_gid) )
+ if (initgroups(user, pwd->pw_gid))
err(1, "initgroups");
- if( setuid(pwd->pw_uid) )
+ if (setuid(pwd->pw_uid))
err(1, "setuid");
+#else
+ /*
+ * Set all user context except for: Environmental variables
+ * Umask Login records (wtmp, etc) Path
+ */
+ setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
+ LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP |
+ LOGIN_SETMAC);
+ /*
+ * If -s is present, also set the MAC label.
+ */
+ if (setmaclabel)
+ setwhat |= LOGIN_SETMAC;
+ /*
+ * Don't touch resource/priority settings if -m has been used
+ * or -l and -c hasn't, and we're not su'ing to root.
+ */
+ if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
+ setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
+ if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
+ err(1, "setusercontext");
+#endif /* __APPLE__ */
if (!asme) {
if (asthem) {
setenv("USER", pwd->pw_name, 1);
setenv("HOME", pwd->pw_dir, 1);
setenv("SHELL", shell, 1);
+#ifdef __APPLE__
+ unsetenv("TMPDIR");
+#endif /* __APPLE__ */
if (asthem) {
/*
setusercontext(lc, pwd, pwd->pw_uid,
LOGIN_SETPATH | LOGIN_SETUMASK |
LOGIN_SETENV);
-#endif
+#endif /* __APPLE__ */
if (p)
setenv("TERM", p, 1);
errx(1, "no directory");
}
}
+#ifndef __APPLE__
+ login_close(lc);
+#endif /* !__APPLE__ */
if (iscsh == YES) {
if (fastlogin)
if (asme)
*np.a-- = "-m";
}
+#ifdef __APPLE__
+ /* 4043304 */
+ if ((p = strrchr(shell, '/')) != NULL)
+ avshell = p + 1;
+ else
+ avshell = shell;
if (asthem) {
avshellbuf[0] = '-';
/* csh *no longer* strips the first character... */
*np.a = avshell;
+#else
+ /* csh strips the first character... */
+ *np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
+#endif /* __APPLE__ */
if (ruid != 0)
syslog(LOG_NOTICE, "%s to %s%s", username, user,
}
}
-static int
+static void
export_pam_environment(void)
{
char **pp;
+ char *p;
for (pp = environ_pam; *pp != NULL; pp++) {
- if (ok_to_export(*pp))
- putenv(*pp);
+ if (ok_to_export(*pp)) {
+ p = strchr(*pp, '=');
+ *p = '\0';
+ setenv(*pp, p + 1, 1);
+ }
free(*pp);
}
- return PAM_SUCCESS;
}
/*
* - Make sure the string doesn't run on too long.
* - Do not export certain variables. This list was taken from the
* Solaris pam_putenv(3) man page.
+ * Note that if the user is chrooted, PAM may have a better idea than we
+ * do of where her home directory is.
*/
static int
ok_to_export(const char *s)
{
static const char *noexport[] = {
- "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
+ "SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
"IFS", "PATH", NULL
};
const char **pp;
#ifdef __APPLE__
fprintf(stderr, "usage: su [-] [-flm] [login [args]]\n");
#else
- fprintf(stderr, "usage: su [-] [-flm] [-c class] [login [args]]\n");
+ fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
#endif /* __APPLE__ */
exit(1);
+ /* NOTREACHED */
}
static int
-chshell(char *sh)
+chshell(const char *sh)
{
int r;
char *cp;
r = 0;
setusershell();
- do {
- cp = getusershell();
- r = strcmp(cp, sh);
- } while (!r && cp != NULL);
+ while ((cp = getusershell()) != NULL && !r)
+ r = (strcmp(cp, sh) == 0);
endusershell();
return r;
}
--- /dev/null
+# su: auth account session
+auth sufficient pam_rootok.so
+auth required pam_opendirectory.so
+account required pam_group.so no_warn group=admin,wheel ruser root_only fail_safe
+account required pam_opendirectory.so no_check_shell
+password required pam_opendirectory.so
+session required pam_launchd.so
#include <stdlib.h>
#include <unistd.h>
#include <err.h>
+#ifdef __APPLE__
+#include <string.h>
+#endif /* __APPLE__ */
#include <sys/sysctl.h>
#include <sys/utsname.h>
&len, NULL, 0) < 0)
err(EXIT_FAILURE, "sysctl");
}
-#else /* __APPLE__ */
+#endif /* !__APPLE__ */
+
+#ifdef __APPLE__
/*
* Let's allow the user to override the output of uname via the shell environment.
* This is a useful feature for cross-compiling (eg. during an OS bringup).
s = getenv ("UNAME_VERSION"); if (s) strncpy (u.version, s, sizeof (u.version));
s = getenv ("UNAME_MACHINE"); if (s) strncpy (u.machine, s, sizeof (u.machine));
}
-#endif /* !__APPLE__ */
+#endif /* __APPLE__ */
if (print_mask & PRINT_SYSNAME) {
space++;
#ifndef __APPLE__
fputs(machine_arch, stdout);
#else
-#if defined(__ppc__)
+#if defined(__ppc__) || defined(__ppc64__)
fputs("powerpc", stdout);
-#elif defined(__i386__)
+#elif defined (__i386__) || defined(__x86_64__)
fputs("i386", stdout);
+#elif defined(__arm__)
+ fputs("arm", stdout);
#else
fputs("unknown", stdout);
#endif
utility does not know about the new conventions for detection of background
jobs.
It will sometimes find a background job instead of the right one.
+.Pp
+Long hostnames and IPv6 addresses may be truncated; however, the
+.Xr who 1
+utility will display full hostnames.
Project = who
Install_Dir = /usr/bin
-CFILES = who.c
+CFILES = who.c utmpentry.c
MANPAGES = who.1
-Extra_CC_Flags = -Wall -mdynamic-no-pic \
- -g -O0
+Extra_CC_Flags = -Wall -Werror -mdynamic-no-pic
+Extra_CC_Flags += -DSUPPORT_UTMPX -D_UTMPX_COMPAT
Extra_LD_Flags = -dead_strip
include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make
--- /dev/null
+/* $NetBSD: utmpentry.c,v 1.15 2008/07/13 20:07:48 dholland Exp $ */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: utmpentry.c,v 1.15 2008/07/13 20:07:48 dholland Exp $");
+#endif
+
+#include <sys/stat.h>
+
+#include <time.h>
+#include <string.h>
+#include <err.h>
+#include <stdlib.h>
+#ifdef __APPLE__
+#include <stdint.h>
+#endif /* __APPLE__ */
+
+#ifdef SUPPORT_UTMP
+#include <utmp.h>
+#endif
+#ifdef SUPPORT_UTMPX
+#include <utmpx.h>
+#endif
+
+#include "utmpentry.h"
+
+#ifdef __APPLE__
+#define timespecclear(tsp) (tsp)->tv_sec = (time_t)((tsp)->tv_nsec = 0L)
+#define timespeccmp(tsp, usp, cmp) \
+ (((tsp)->tv_sec == (usp)->tv_sec) ? \
+ ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \
+ ((tsp)->tv_sec cmp (usp)->tv_sec))
+#endif /* __APPLE__ */
+
+/* Fail the compile if x is not true, by constructing an illegal type. */
+#define COMPILE_ASSERT(x) ((void)sizeof(struct { unsigned : ((x) ? 1 : -1); }))
+
+
+#ifdef SUPPORT_UTMP
+static void getentry(struct utmpentry *, struct utmp *);
+static struct timespec utmptime = {0, 0};
+#endif
+#ifdef SUPPORT_UTMPX
+static void getentryx(struct utmpentry *, struct utmpx *);
+static struct timespec utmpxtime = {0, 0};
+#endif
+#if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
+static int setup(const char *);
+static void adjust_size(struct utmpentry *e);
+#endif
+
+int maxname = 8, maxline = 8, maxhost = 16;
+int etype = 1 << USER_PROCESS;
+static int numutmp = 0;
+static struct utmpentry *ehead;
+
+#if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
+static void
+adjust_size(struct utmpentry *e)
+{
+ int max;
+
+ if ((max = strlen(e->name)) > maxname)
+ maxname = max;
+ if ((max = strlen(e->line)) > maxline)
+ maxline = max;
+ if ((max = strlen(e->host)) > maxhost)
+ maxhost = max;
+}
+
+static int
+setup(const char *fname)
+{
+ int what = 3;
+ struct stat st;
+ const char *sfname;
+
+ if (fname == NULL) {
+#ifdef SUPPORT_UTMPX
+ setutxent();
+#endif
+#ifdef SUPPORT_UTMP
+ setutent();
+#endif
+ } else {
+ size_t len = strlen(fname);
+ if (len == 0)
+ errx(1, "Filename cannot be 0 length.");
+#ifdef __APPLE__
+ what = 1;
+#else /* !__APPLE__ */
+ what = fname[len - 1] == 'x' ? 1 : 2;
+#endif /* __APPLE__ */
+ if (what == 1) {
+#ifdef SUPPORT_UTMPX
+ if (utmpxname(fname) == 0)
+ warnx("Cannot set utmpx file to `%s'",
+ fname);
+#else
+ warnx("utmpx support not compiled in");
+#endif
+ } else {
+#ifdef SUPPORT_UTMP
+ if (utmpname(fname) == 0)
+ warnx("Cannot set utmp file to `%s'",
+ fname);
+#else
+ warnx("utmp support not compiled in");
+#endif
+ }
+ }
+#ifdef SUPPORT_UTMPX
+ if (what & 1) {
+ sfname = fname ? fname : _PATH_UTMPX;
+ if (stat(sfname, &st) == -1) {
+ warn("Cannot stat `%s'", sfname);
+ what &= ~1;
+ } else {
+ if (timespeccmp(&st.st_mtimespec, &utmpxtime, >))
+ utmpxtime = st.st_mtimespec;
+ else
+ what &= ~1;
+ }
+ }
+#endif
+#ifdef SUPPORT_UTMP
+ if (what & 2) {
+ sfname = fname ? fname : _PATH_UTMP;
+ if (stat(sfname, &st) == -1) {
+ warn("Cannot stat `%s'", sfname);
+ what &= ~2;
+ } else {
+ if (timespeccmp(&st.st_mtimespec, &utmptime, >))
+ utmptime = st.st_mtimespec;
+ else
+ what &= ~2;
+ }
+ }
+#endif
+ return what;
+}
+#endif
+
+void
+endutentries(void)
+{
+ struct utmpentry *ep;
+
+#ifdef SUPPORT_UTMP
+ timespecclear(&utmptime);
+#endif
+#ifdef SUPPORT_UTMPX
+ timespecclear(&utmpxtime);
+#endif
+ ep = ehead;
+ while (ep) {
+ struct utmpentry *sep = ep;
+ ep = ep->next;
+ free(sep);
+ }
+ ehead = NULL;
+ numutmp = 0;
+}
+
+int
+getutentries(const char *fname, struct utmpentry **epp)
+{
+#ifdef SUPPORT_UTMPX
+ struct utmpx *utx;
+#endif
+#ifdef SUPPORT_UTMP
+ struct utmp *ut;
+#endif
+#if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
+ struct utmpentry *ep;
+ int what = setup(fname);
+ struct utmpentry **nextp = &ehead;
+ switch (what) {
+ case 0:
+ /* No updates */
+ *epp = ehead;
+ return numutmp;
+ default:
+ /* Need to re-scan */
+ ehead = NULL;
+ numutmp = 0;
+ }
+#endif
+
+#ifdef SUPPORT_UTMPX
+ while ((what & 1) && (utx = getutxent()) != NULL) {
+#ifdef __APPLE__
+ if (((1 << utx->ut_type) & etype) == 0)
+#else /* !__APPLE__ */
+ if (fname == NULL && ((1 << utx->ut_type) & etype) == 0)
+#endif /* __APPLE__ */
+ continue;
+ if ((ep = calloc(1, sizeof(struct utmpentry))) == NULL) {
+ warn(NULL);
+ return 0;
+ }
+ getentryx(ep, utx);
+ *nextp = ep;
+ nextp = &(ep->next);
+ }
+#endif
+
+#ifdef SUPPORT_UTMP
+ if ((etype & (1 << USER_PROCESS)) != 0) {
+ while ((what & 2) && (ut = getutent()) != NULL) {
+ if (fname == NULL && (*ut->ut_name == '\0' ||
+ *ut->ut_line == '\0'))
+ continue;
+ /* Don't process entries that we have utmpx for */
+ for (ep = ehead; ep != NULL; ep = ep->next) {
+ if (strncmp(ep->line, ut->ut_line,
+ sizeof(ut->ut_line)) == 0)
+ break;
+ }
+ if (ep != NULL)
+ continue;
+ if ((ep = calloc(1, sizeof(*ep))) == NULL) {
+ warn(NULL);
+ return 0;
+ }
+ getentry(ep, ut);
+ *nextp = ep;
+ nextp = &(ep->next);
+ }
+ }
+#endif
+ numutmp = 0;
+#if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
+ if (ehead != NULL) {
+ struct utmpentry *from = ehead, *save;
+
+ ehead = NULL;
+ while (from != NULL) {
+ for (nextp = &ehead;
+ (*nextp) && strcmp(from->line, (*nextp)->line) > 0;
+ nextp = &(*nextp)->next)
+ continue;
+ save = from;
+ from = from->next;
+ save->next = *nextp;
+ *nextp = save;
+ numutmp++;
+ }
+ }
+ *epp = ehead;
+ return numutmp;
+#else
+ *epp = NULL;
+ return 0;
+#endif
+}
+
+#ifdef SUPPORT_UTMP
+static void
+getentry(struct utmpentry *e, struct utmp *up)
+{
+ COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name));
+ COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line));
+ COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host));
+
+ /*
+ * e has just been calloc'd. We don't need to clear it or
+ * append null-terminators, because its length is strictly
+ * greater than the source string. Use strncpy to _read_
+ * up->ut_* because they may not be terminated. For this
+ * reason we use the size of the _source_ as the length
+ * argument.
+ */
+ (void)strncpy(e->name, up->ut_name, sizeof(up->ut_name));
+ (void)strncpy(e->line, up->ut_line, sizeof(up->ut_line));
+ (void)strncpy(e->host, up->ut_host, sizeof(up->ut_host));
+
+ e->tv.tv_sec = up->ut_time;
+ e->tv.tv_usec = 0;
+ e->pid = 0;
+ e->term = 0;
+ e->exit = 0;
+ e->sess = 0;
+ e->type = USER_PROCESS;
+ adjust_size(e);
+}
+#endif
+
+#ifdef SUPPORT_UTMPX
+static void
+getentryx(struct utmpentry *e, struct utmpx *up)
+{
+ COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name));
+ COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line));
+ COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host));
+
+ /*
+ * e has just been calloc'd. We don't need to clear it or
+ * append null-terminators, because its length is strictly
+ * greater than the source string. Use strncpy to _read_
+ * up->ut_* because they may not be terminated. For this
+ * reason we use the size of the _source_ as the length
+ * argument.
+ */
+ (void)strncpy(e->name, up->ut_name, sizeof(up->ut_name));
+ (void)strncpy(e->line, up->ut_line, sizeof(up->ut_line));
+ (void)strncpy(e->host, up->ut_host, sizeof(up->ut_host));
+
+ e->tv = up->ut_tv;
+ e->pid = up->ut_pid;
+#ifndef __APPLE__
+ e->term = up->ut_exit.e_termination;
+ e->exit = up->ut_exit.e_exit;
+ e->sess = up->ut_session;
+#endif /* !__APPLE__ */
+ e->type = up->ut_type;
+ adjust_size(e);
+}
+#endif
--- /dev/null
+/* $NetBSD: utmpentry.h,v 1.7 2008/07/13 20:07:49 dholland Exp $ */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if defined(SUPPORT_UTMPX)
+# include <utmpx.h>
+# define WHO_NAME_LEN _UTX_USERSIZE
+# define WHO_LINE_LEN _UTX_LINESIZE
+# define WHO_HOST_LEN _UTX_HOSTSIZE
+#elif defined(SUPPORT_UTMP)
+# include <utmp.h>
+# define WHO_NAME_LEN UT_NAMESIZE
+# define WHO_LINE_LEN UT_LINESIZE
+# define WHO_HOST_LEN UT_HOSTSIZE
+#else
+# error Either SUPPORT_UTMPX or SUPPORT_UTMP must be defined!
+#endif
+
+
+struct utmpentry {
+ char name[WHO_NAME_LEN + 1];
+ char line[WHO_LINE_LEN + 1];
+ char host[WHO_HOST_LEN + 1];
+ struct timeval tv;
+ pid_t pid;
+#ifndef __APPLE__
+ uint16_t term;
+ uint16_t exit;
+ uint16_t sess;
+#endif /* !__APPLE__ */
+ uint16_t type;
+ struct utmpentry *next;
+};
+
+extern int maxname, maxline, maxhost;
+extern int etype;
+
+/*
+ * getutentries provides a linked list of struct utmpentry and returns
+ * the number of entries. The first argument, if not null, names an
+ * alternate utmp(x) file to look in.
+ *
+ * The memory returned by getutentries belongs to getutentries. The
+ * list returned (or elements of it) may be returned again later if
+ * utmp hasn't changed in the meantime.
+ *
+ * endutentries clears and frees the cached data.
+ */
+
+int getutentries(const char *, struct utmpentry **);
+void endutentries(void);
+.\" $NetBSD: who.1,v 1.22 2007/01/18 00:15:05 wiz Exp $
+.\"
.\" Copyright (c) 1986, 1991, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
.\" 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
+.\" 3. 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.
.\"
.\" SUCH DAMAGE.
.\"
.\" @(#)who.1 8.2 (Berkeley) 12/30/93
-.\" $FreeBSD: src/usr.bin/who/who.1,v 1.16 2004/07/02 22:22:34 ru Exp $
.\"
-.Dd May 8, 2002
+.Dd January 17, 2007
.Dt WHO 1
.Os
.Sh NAME
.Nm who
-.Nd display who is on the system
+.Nd display who is logged in
.Sh SYNOPSIS
.Nm
-.Op Fl HmqsTu
-.Op Cm am I
+.Op Fl abdHlmpqrsTtu
.Op Ar file
+.Nm
+.Ar am i
.Sh DESCRIPTION
The
.Nm
-utility displays information about currently logged in users.
-By default, this includes the login name, tty name, date and time of login and
-remote hostname if not local.
+utility displays a list of all users currently logged on, showing for
+each user the login name, tty name, the date and time of login, and
+hostname if not local.
.Pp
-The options are as follows:
-.Bl -tag -width indent
+Available options:
+.Pp
+.Bl -tag -width file
+.It Fl a
+Same as
+.Fl bdlprTtu .
+.It Fl b
+Time of last system boot.
+.It Fl d
+Print dead processes.
.It Fl H
-Write column headings above the output.
+Write column headings above the regular output.
+.It Fl l
+Print system login processes (unsupported).
.It Fl m
-Show information about the terminal attached to standard input only.
+Only print information about the current terminal.
+This is the
+.Tn POSIX
+way of saying
+.Nm
+.Ar am i .
+.It Fl p
+Print active processes spawned by
+.Xr launchd 8
+(unsupported).
.It Fl q
-.Dq "Quick mode" :
-List the names and number of logged in users in columns.
-All other command line options are ignored.
+.Dq Quick mode :
+List only the names and the number of users currently logged on.
+When this option is used, all other options are ignored.
+.It Fl r
+Print the current runlevel.
+This is meaningless on Mac OS X.
.It Fl s
-Show the name, line and time fields only.
+List only the name, line and time fields.
This is the default.
.It Fl T
-Indicate whether each user is accepting messages.
-One of the following characters is written:
-.Pp
-.Bl -tag -width 1n -compact
-.It Li +
-User is accepting messages.
-.It Li \&-
-User is not accepting messages.
-.It Li \&?
-An error occurred.
-.El
+Print a character after the user name indicating the state of the
+terminal line:
+.Sq +
+if the terminal is writable;
+.Sq -
+if it is not;
+and
+.Sq \&?
+if a bad line is encountered.
+.It Fl t
+Print last system clock change (unsupported).
.It Fl u
-Show idle time for each user in hours and minutes as
-.Ar hh Ns : Ns Ar mm ,
-.Ql \&.
-if the user has been idle less than a minute, and
-.Dq Li old
-if the user has been idle more than 24 hours.
-Additionally, display the PID of the user's login process.
-.It Cm am I
-Equivalent to
-.Fl m .
-.El
-.Pp
+Print the idle time for each user, and the associated process ID.
+.It Ar \&am I
+Returns the invoker's real user name.
+.It Ar file
By default,
.Nm
gathers information from the file
.Pa /var/run/utmpx .
-An alternate
+An alternative
.Ar file
may be specified.
-.Sh LEGACY DESCRIPTION
-In legacy mode,
-.Fl u
-does not display the PID of the user's login process.
-.Sh ENVIRONMENT
-The
-.Ev COLUMNS , LANG , LC_ALL
-and
-.Ev LC_TIME
-environment variables affect the execution of
-.Nm
-as described in
-.Xr environ 7 .
+.El
.Sh FILES
-.Bl -tag -width /var/log/utmpx -compact
+.Bl -tag -width /var/run/utmpx -compact
.It Pa /var/run/utmpx
.El
-.Sh DIAGNOSTICS
-.Ex -std
.Sh SEE ALSO
.Xr last 1 ,
+.Xr mesg 1 ,
.Xr users 1 ,
-.Xr w 1 ,
-.Xr compat 5 ,
+.Xr getuid 2 ,
.Xr utmpx 5
.Sh STANDARDS
The
.Sh HISTORY
A
.Nm
-command appeared in
-.At v1 .
+utility appeared in
+.At v6 .
-/*-
- * Copyright (c) 2002 Tim J. Robbins.
- * All rights reserved.
+/* $NetBSD: who.c,v 1.23 2008/07/24 15:35:41 christos Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Fischbein.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 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. 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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * 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 AUTHOR OR CONTRIBUTORS BE LIABLE
+ * 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)
*/
#include <sys/cdefs.h>
-/* commented as FBSDID not needed for Tiger ......
-__FBSDID("$FreeBSD: src/usr.bin/who/who.c,v 1.20 2003/10/26 05:05:48 peter Exp $");
-*/
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1989, 1993\
+ The Regents of the University of California. All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)who.c 8.1 (Berkeley) 6/6/93";
+#endif
+__RCSID("$NetBSD: who.c,v 1.23 2008/07/24 15:35:41 christos Exp $");
+#endif /* not lint */
#include <sys/types.h>
-#include <sys/ioctl.h>
#include <sys/stat.h>
#include <err.h>
-#include <errno.h>
-#include <langinfo.h>
-#include <limits.h>
#include <locale.h>
-#include <paths.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
-#include <timeconv.h>
#include <unistd.h>
+#ifdef SUPPORT_UTMP
+#include <utmp.h>
+#endif
+#ifdef SUPPORT_UTMPX
#include <utmpx.h>
+#endif
+#ifdef __APPLE__
+#include <limits.h>
+#include <paths.h>
+#include <stdint.h>
+#endif /* __APPLE__ */
+
+#include "utmpentry.h"
-/* from utmp.h; used only for print formatting */
-#define UT_NAMESIZE 8
-#define UT_LINESIZE 8
-#define UT_HOSTSIZE 16
-
-static void heading(void);
-static void process_utmp(void);
-static void process_wtmp();
-static void quick(void);
-static void row(const struct utmpx *);
-static int ttywidth(void);
-static void usage(void);
-static void whoami(void);
-
-static int bflag; /* date & time of last reboot */
-static int dflag; /* dead processes */
-static int Hflag; /* Write column headings */
-static int lflag; /* waiting to login */
-static int mflag; /* Show info about current terminal */
-static int pflag; /* Processes active & spawned by init */
-static int qflag; /* "Quick" mode */
-static int rflag; /* run-level of the init process */
-static int sflag; /* Show name, line, time */
-static int tflag; /* time of change to system clock */
-static int Tflag; /* Show terminal state */
-static int uflag; /* Show idle time */
#ifdef __APPLE__
-#include <get_compat.h>
-#else /* !__APPLE__ */
-#define COMPAT_MODE(a,b) (1)
+#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
#endif /* __APPLE__ */
-static int unix2003_std;
+
+static void output_labels(void);
+static void who_am_i(const char *, int);
+static void usage(void) __dead;
+static void process(const char *, int);
+static void eprint(const struct utmpentry *);
+static void print(const char *, const char *, time_t, const char *, pid_t pid,
+ uint16_t term, uint16_t xit, uint16_t sess, uint16_t type);
+static void quick(const char *);
+
+static int show_term; /* show term state */
+static int show_idle; /* show idle time */
+#ifndef __APPLE__
+static int show_details; /* show exit status etc. */
+#endif /* !__APPLE__ */
+
+struct ut_type_names {
+ int type;
+ const char *name;
+} ut_type_names[] = {
+#ifdef SUPPORT_UTMPX
+ { EMPTY, "empty" },
+ { RUN_LVL, "run level" },
+ { BOOT_TIME, "boot time" },
+ { OLD_TIME, "old time" },
+ { NEW_TIME, "new time" },
+ { INIT_PROCESS, "init process" },
+ { LOGIN_PROCESS, "login process" },
+ { USER_PROCESS, "user process" },
+ { DEAD_PROCESS, "dead process" },
+#if defined(_NETBSD_SOURCE)
+ { ACCOUNTING, "accounting" },
+ { SIGNATURE, "signature" },
+ { DOWN_TIME, "down time" },
+#endif /* _NETBSD_SOURCE */
+#endif /* SUPPORT_UTMPX */
+ { -1, "unknown" }
+};
int
main(int argc, char *argv[])
{
- int ch;
+ int c, only_current_term, show_labels, quick_mode, default_mode;
+ int et = 0;
- setlocale(LC_TIME, "");
+ setlocale(LC_ALL, "");
- unix2003_std = COMPAT_MODE("bin/who", "unix2003");
+ only_current_term = show_term = show_idle = show_labels = 0;
+ quick_mode = default_mode = 0;
- while ((ch = getopt(argc, argv, "abdHlmpqrstTu")) != -1) {
- switch (ch) {
-
- case 'a': /* -b, -d, -l, -p, -r, -t, -T and -u */
- bflag = dflag = lflag = pflag = 1;
- rflag = tflag = Tflag = uflag = 1;
+ while ((c = getopt(argc, argv, "abdHlmpqrsTtuv")) != -1) {
+ switch (c) {
+ case 'a':
+ et = -1;
+#ifdef __APPLE__
+ show_idle = 1;
+#else /* !__APPLE__ */
+ show_idle = show_details = 1;
+#endif /* __APPLE__ */
break;
- case 'b': /* date & time of last reboot */
- bflag = 1;
+ case 'b':
+ et |= (1 << BOOT_TIME);
break;
- case 'd': /* dead processes */
- dflag = 1;
+ case 'd':
+ et |= (1 << DEAD_PROCESS);
break;
- case 'H': /* Write column headings */
- Hflag = 1;
+ case 'H':
+ show_labels = 1;
break;
- case 'l': /* waiting to login */
- lflag = 1;
+ case 'l':
+ et |= (1 << LOGIN_PROCESS);
break;
- case 'm': /* Show info about current terminal */
- mflag = 1;
+ case 'm':
+ only_current_term = 1;
break;
- case 'p': /* Processes active & spawned by init */
- pflag = 1;
+ case 'p':
+ et |= (1 << INIT_PROCESS);
break;
- case 'q': /* "Quick" mode */
- qflag = 1;
+ case 'q':
+ quick_mode = 1;
break;
- case 'r': /* run-level of the init process */
- rflag = 1;
+ case 'r':
+ et |= (1 << RUN_LVL);
break;
- case 's': /* Show name, line, time */
- sflag = 1;
+ case 's':
+ default_mode = 1;
break;
- case 't': /* time of change to system clock */
- tflag = 1;
+ case 'T':
+ show_term = 1;
break;
- case 'T': /* Show terminal state */
- Tflag = 1;
+ case 't':
+ et |= (1 << NEW_TIME);
break;
- case 'u': /* Show idle time */
- uflag = 1;
+ case 'u':
+ show_idle = 1;
break;
+#ifndef __APPLE__
+ case 'v':
+ show_details = 1;
+ break;
+#endif /* !__APPLE__ */
default:
usage();
- /*NOTREACHED*/
+ /* NOTREACHED */
}
}
argc -= optind;
argv += optind;
- if (argc >= 2 && strcmp(argv[0], "am") == 0 &&
- (strcmp(argv[1], "i") == 0 || strcmp(argv[1], "I") == 0)) {
- /* "who am i" or "who am I", equivalent to -m */
- mflag = 1;
- argc -= 2;
- argv += 2;
+ if (et != 0)
+ etype = et;
+
+#ifndef __APPLE__
+ if (chdir("/dev")) {
+ err(EXIT_FAILURE, "cannot change directory to /dev");
+ /* NOTREACHED */
}
- if (argc > 1)
+#endif /* !__APPLE__ */
+
+ if (default_mode)
+ only_current_term = show_term = show_idle = 0;
+
+ switch (argc) {
+ case 0: /* who */
+ if (quick_mode) {
+ quick(NULL);
+ } else if (only_current_term) {
+ who_am_i(NULL, show_labels);
+ } else {
+ process(NULL, show_labels);
+ }
+ break;
+ case 1: /* who utmp_file */
+ if (quick_mode) {
+ quick(*argv);
+ } else if (only_current_term) {
+ who_am_i(*argv, show_labels);
+ } else {
+ process(*argv, show_labels);
+ }
+ break;
+ case 2: /* who am i */
+ who_am_i(NULL, show_labels);
+ break;
+ default:
usage();
-
- if (*argv != NULL) {
- if (!utmpxname(*argv) || !wtmpxname(*argv))
- usage();
+ /* NOTREACHED */
}
- if (qflag)
- quick();
- else {
- if (sflag)
- Tflag = uflag = 0;
- if (Hflag)
- heading();
- if (mflag)
- whoami();
- else
- /* read and process utmpx file for relevant options */
- if( Tflag || uflag || !(bflag || dflag || lflag || pflag || rflag) )
- process_utmp();
- }
+ return 0;
+}
- /* read and process wtmp file for relevant options */
- if (bflag || dflag || lflag || pflag || rflag ) {
+static char *
+strrstr(const char *str, const char *pat)
+{
+ const char *estr;
+ size_t len;
+ if (*pat == '\0')
+ return __UNCONST(str);
- process_wtmp();
- }
+ len = strlen(pat);
- endutxent();
- exit(0);
+ for (estr = str + strlen(str); str < estr; estr--)
+ if (strncmp(estr, pat, len) == 0)
+ return __UNCONST(estr);
+ return NULL;
}
static void
-usage(void)
+who_am_i(const char *fname, int show_labels)
{
-
- fprintf(stderr, "usage: who [-abdHlmpqrstTu] [am I] [file]\n");
- exit(1);
+ struct passwd *pw;
+ const char *p;
+ char *t;
+ time_t now;
+ struct utmpentry *ehead, *ep;
+
+ /* search through the utmp and find an entry for this tty */
+ if ((p = ttyname(STDIN_FILENO)) != NULL) {
+
+ /* strip directory prefixes for ttys */
+ if ((t = strrstr(p, "/pts/")) != NULL ||
+ (t = strrchr(p, '/')) != NULL)
+ p = t + 1;
+
+ (void)getutentries(fname, &ehead);
+ for (ep = ehead; ep; ep = ep->next)
+ if (strcmp(ep->line, p) == 0) {
+ if (show_labels)
+ output_labels();
+ eprint(ep);
+ return;
+ }
+ } else
+ p = "tty??";
+
+ (void)time(&now);
+ pw = getpwuid(getuid());
+ if (show_labels)
+ output_labels();
+ print(pw ? pw->pw_name : "?", p, now, "", getpid(), 0, 0, 0, 0);
}
static void
-heading(void)
+process(const char *fname, int show_labels)
{
+ struct utmpentry *ehead, *ep;
+ (void)getutentries(fname, &ehead);
+ if (show_labels)
+ output_labels();
+ for (ep = ehead; ep != NULL; ep = ep->next)
+ eprint(ep);
+#ifdef __APPLE__
+ if ((etype & (1 << RUN_LVL)) != 0) {
+ printf(" . run-level 3\n");
+ }
+#endif /* __APPLE__ */
+}
- printf("%-*s ", UT_NAMESIZE, "NAME");
- if (Tflag)
- printf("S ");
- printf("%-*s ", UT_LINESIZE, "LINE");
- printf("%-*s ", 12, "TIME");
- if (uflag)
- printf("IDLE ");
- if (unix2003_std && uflag && !Tflag)
- printf(" PID ");
- printf("%-*s", UT_HOSTSIZE, "FROM");
- putchar('\n');
+static void
+eprint(const struct utmpentry *ep)
+{
+ print(ep->name, ep->line, (time_t)ep->tv.tv_sec, ep->host, ep->pid,
+#ifdef __APPLE__
+ 0, 0, 0, ep->type);
+#else /* !__APPLE__ */
+ ep->term, ep->exit, ep->sess, ep->type);
+#endif /* __APPLE__ */
}
static void
-row(const struct utmpx *ut)
+print(const char *name, const char *line, time_t t, const char *host,
+ pid_t pid, uint16_t term, uint16_t xit, uint16_t sess, uint16_t type)
{
- char buf[80], tty[sizeof(_PATH_DEV) + _UTX_LINESIZE];
struct stat sb;
- time_t idle, t;
- static int d_first = -1;
- struct tm *tm;
char state;
- char login_pidstr[20];
+ static time_t now = 0;
+ time_t idle;
+ const char *types = NULL;
+ size_t i;
- if (d_first < 0)
- d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
+ state = '?';
+ idle = 0;
- if (Tflag || uflag) {
- snprintf(tty, sizeof(tty), "%s%.*s", _PATH_DEV,
- _UTX_LINESIZE, ut->ut_line);
- state = '?';
- idle = 0;
+ for (i = 0; ut_type_names[i].type >= 0; i++) {
+ types = ut_type_names[i].name;
+ if (ut_type_names[i].type == type)
+ break;
+ }
+
+ if (show_term || show_idle) {
+ if (now == 0)
+ time(&now);
+
+#ifdef __APPLE__
+ char tty[PATH_MAX + 1];
+ snprintf(tty, sizeof(tty), "%s%s", _PATH_DEV, line);
if (stat(tty, &sb) == 0) {
- state = sb.st_mode & (S_IWOTH|S_IWGRP) ?
- '+' : '-';
- idle = time(NULL) - sb.st_mtime;
- }
- if (unix2003_std && !Tflag) {
- /* uflag without Tflag */
- if (ut->ut_pid) {
- snprintf(login_pidstr,sizeof(login_pidstr),
- "%8d",ut->ut_pid);
- } else {
- strcpy(login_pidstr," ?");
- }
+#else /* !__APPLE__ */
+ if (stat(line, &sb) == 0) {
+#endif /* __APPLE__ */
+ state = (sb.st_mode & 020) ? '+' : '-';
+ idle = now - sb.st_atime;
}
+
}
- printf("%-*.*s ", UT_NAMESIZE, _UTX_USERSIZE, ut->ut_user);
- if (Tflag)
- printf("%c ", state);
- printf("%-*.*s ", UT_LINESIZE, _UTX_LINESIZE, ut->ut_line);
- t = _time32_to_time(ut->ut_tv.tv_sec);
- tm = localtime(&t);
- strftime(buf, sizeof(buf), d_first ? "%e %b %R" : "%b %e %R", tm);
- printf("%-*s ", 12, buf);
- if (uflag) {
- if (idle < 60)
- printf(" . ");
- else if (idle < 24 * 60 * 60)
- printf("%02d:%02d ", (int)(idle / 60 / 60),
- (int)(idle / 60 % 60));
- else
- printf(" old ");
- if (unix2003_std && !Tflag) {
- printf("%s ", login_pidstr);
- }
+#ifdef __APPLE__
+ switch (type) {
+ case LOGIN_PROCESS:
+ (void)printf("%-*.*s ", maxname, maxname, "LOGIN");
+ break;
+ case BOOT_TIME:
+ (void)printf("%-*.*s ", maxname, maxname, "reboot");
+ break;
+ default:
+ (void)printf("%-*.*s ", maxname, maxname, name);
+ break;
}
- if (*ut->ut_host != '\0')
- printf("(%.*s)", _UTX_HOSTSIZE, ut->ut_host);
- putchar('\n');
-
-}
+#else /* !__APPLE__ */
+ (void)printf("%-*.*s ", maxname, maxname, name);
+#endif /* __APPLE__ */
-static void
-process_utmp(void)
-{
- struct utmpx *ut;
+ if (show_term)
+ (void)printf("%c ", state);
- while ((ut = getutxent()) != NULL)
- if (*ut->ut_user != '\0' && ut->ut_type == USER_PROCESS) {
- row(ut);
- }
-}
-
-/* For some options, process the wtmp file to generate output */
-static void
-process_wtmp(void)
-{
- struct utmpx *ut;
- struct utmpx lboot_ut;
- int num = 0; /* count of user entries */
-
- setutxent_wtmp(0); /* zero means reverse chronological */
- lboot_ut.ut_type = 0;
- while (!lboot_ut.ut_type && (ut = getutxent_wtmp()) != NULL) {
- switch(ut->ut_type) {
- case BOOT_TIME:
- lboot_ut = *ut;
- strcpy(lboot_ut.ut_user, "reboot");
- strcpy(lboot_ut.ut_line, "~");
- break;
- case INIT_PROCESS:
- case LOGIN_PROCESS:
- case USER_PROCESS:
- case DEAD_PROCESS:
- num++;
- break;
+#ifdef __APPLE__
+ (void)printf("%-*.*s ", maxline, maxline, type == BOOT_TIME ? "~" : line);
+#else /* !__APPLE__ */
+ (void)printf("%-*.*s ", maxline, maxline, line);
+#endif /* __APPLE__ */
+ (void)printf("%.12s ", ctime(&t) + 4);
+
+ if (show_idle) {
+ if (idle < 60)
+ (void)printf(" . ");
+ else if (idle < (24 * 60 * 60))
+ (void)printf("%02ld:%02ld ",
+ (long)(idle / (60 * 60)),
+ (long)(idle % (60 * 60)) / 60);
+ else
+ (void)printf(" old ");
+
+ (void)printf("\t%6d", pid);
+
+#ifndef __APPLE__
+ if (show_details) {
+ if (type == RUN_LVL)
+ (void)printf("\tnew=%c old=%c", term, xit);
+ else
+ (void)printf("\tterm=%d exit=%d", term, xit);
+ (void)printf(" sess=%d", sess);
+ (void)printf(" type=%s ", types);
}
+#endif /* !__APPLE__ */
}
- endutxent_wtmp();
-
- if (bflag && lboot_ut.ut_type)
- row(&lboot_ut);
+
+#ifdef __APPLE__
+ /* 6179576 */
+ if (type == DEAD_PROCESS)
+ (void)printf("\tterm=%d exit=%d", 0, 0);
+#endif /* __APPLE__ */
- /* run level of the init process is unknown in BSD system. If multi
- user, then display the highest run level. Else, no-op.
- */
- if (rflag && (num > 1))
- printf(" . run-level 3\n");
+ if (*host)
+ (void)printf("\t(%.*s)", maxhost, host);
+ (void)putchar('\n');
}
static void
-quick(void)
+output_labels(void)
{
- struct utmpx *ut;
- int col, ncols, num;
-
- ncols = ttywidth();
- col = num = 0;
- while ((ut = getutxent()) != NULL) {
- if (*ut->ut_user == '\0' || ut->ut_type != USER_PROCESS)
- continue;
- printf("%-*.*s", UT_NAMESIZE, _UTX_USERSIZE, ut->ut_user);
- if (++col < ncols / (UT_NAMESIZE + 1))
- putchar(' ');
- else {
- col = 0;
- putchar('\n');
- }
- num++;
- }
- if (col != 0)
- putchar('\n');
-
- printf("# users = %d\n", num);
+ (void)printf("%-*.*s ", maxname, maxname, "USER");
+
+ if (show_term)
+ (void)printf("S ");
+
+ (void)printf("%-*.*s ", maxline, maxline, "LINE");
+ (void)printf("WHEN ");
+
+ if (show_idle) {
+ (void)printf("IDLE ");
+ (void)printf("\t PID");
+
+ (void)printf("\tCOMMENT");
+ }
+
+ (void)putchar('\n');
}
static void
-whoami(void)
+quick(const char *fname)
{
- struct utmpx ut;
- struct utmpx *u;
- struct passwd *pwd;
- const char *name, *p, *tty;
-
- if ((tty = ttyname(STDIN_FILENO)) == NULL)
- tty = "tty??";
- else if ((p = strrchr(tty, '/')) != NULL)
- tty = p + 1;
-
- memset(&ut, 0, sizeof(ut));
- strncpy(ut.ut_line, tty, sizeof(ut.ut_line));
- memcpy(ut.ut_id, tty + (strlen(tty) - sizeof(ut.ut_id)), sizeof(ut.ut_id));
- ut.ut_type = USER_PROCESS;
- /* Search utmp for our tty, dump first matching record. */
- u = getutxid(&ut);
- if (u) {
- row(u);
- return;
+ struct utmpentry *ehead, *ep;
+ int num = 0;
+
+ (void)getutentries(fname, &ehead);
+ for (ep = ehead; ep != NULL; ep = ep->next) {
+ (void)printf("%-*s ", maxname, ep->name);
+ if ((++num % 8) == 0)
+ (void)putchar('\n');
}
+ if (num % 8)
+ (void)putchar('\n');
- /* Not found; fill the utmpx structure with the information we have. */
- if ((pwd = getpwuid(getuid())) != NULL)
- name = pwd->pw_name;
- else
- name = "?";
- strncpy(ut.ut_user, name, _UTX_USERSIZE);
- ut.ut_tv.tv_sec = _time_to_time32(time(NULL));
- row(&ut);
+ (void)printf("# users = %d\n", num);
}
-static int
-ttywidth(void)
+static void
+usage(void)
{
- struct winsize ws;
- long width;
- char *cols, *ep;
-
- if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') {
- errno = 0;
- width = strtol(cols, &ep, 10);
- if (errno || width <= 0 || width > INT_MAX || ep == cols ||
- *ep != '\0')
- warnx("invalid COLUMNS environment variable ignored");
- else
- return (width);
- }
- if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1)
- return (ws.ws_col);
-
- return (80);
+#ifdef __APPLE__
+ (void)fprintf(stderr, "Usage: %s [-abdHlmpqrsTtu] [file]\n\t%s am i\n",
+#else /* !__APPLE__ */
+ (void)fprintf(stderr, "Usage: %s [-abdHlmqrsTtuv] [file]\n\t%s am i\n",
+#endif /* __APPLE__ */
+ getprogname(), getprogname());
+ exit(EXIT_FAILURE);
}
and
.Fl n
options are not mutually-exclusive.
+.Pp
+For more information about legacy mode, see
+.Xr compat 5 .
.Sh EXIT STATUS
The
.Nm
main(int argc, char *argv[])
{
long arg_max;
- int ch, Jflag, nargs, nflag, nline;
+ int ch, Jflag, nflag, nline;
+ size_t nargs;
size_t linelen;
char *endptr;
break;
case 'n':
nflag = 1;
- if ((nargs = atoi(optarg)) <= 0)
+ if ((nargs = strtol(optarg, NULL, 10)) <= 0)
errx(1, "illegal argument count");
if (COMPAT_MODE("bin/xargs", "Unix2003")) {
Lflag = 0; /* Override */
foundeof = *eofstr != '\0' &&
strcmp(argp, eofstr) == 0;
+#ifdef __APPLE__
+ /* 6591323: -I specifies that it processes the entire line,
+ * so only recognize eofstr at the end of a line. */
+ if (Iflag && !last_was_newline)
+ foundeof = 0;
+
+ /* 6591323: Essentially the same as the EOF handling above. */
+ if (foundeof && (p - strlen(eofstr) == bbp)) {
+ waitchildren(*argv, 1);
+ exit(rval);
+ }
+#endif
+
/* Do not make empty args unless they are quoted */
if ((argp != p || wasquoted) && !foundeof) {
*p++ = '\0';