Add finger demo to redbean and fix regression

This change fixes a regression in unix.connect() caused by the recent
addition of UNIX domain sockets. The BSD finger command has been added
to third_party for fun and profit. A new demo has been added to redbean
showing how a protocol as simple as finger can be implemented.
This commit is contained in:
Justine Tunney 2022-06-23 03:39:44 -07:00
parent 2415afab0e
commit 17cbe73411
34 changed files with 2454 additions and 29 deletions

165
third_party/finger/display.c vendored Normal file
View file

@ -0,0 +1,165 @@
/*
* Copyright (c) 1989 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "libc/calls/ioctl.h"
#include "libc/calls/struct/termios.h"
#include "libc/calls/struct/winsize.h"
#include "libc/calls/termios.h"
#include "libc/fmt/fmt.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/termios.h"
#include "third_party/finger/finger.h"
// clang-format off
int
getscreenwidth(void)
{
struct winsize ws;
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0 || ws.ws_col==0) {
return 80;
}
return ws.ws_col;
}
int
is8bit(void)
{
static int cache=-1;
struct termios tios;
if (cache>=0) return cache;
if (tcgetattr(STDIN_FILENO, &tios)<0) {
/* assume 8-bit; it's 1999 now, not 1972 */
cache = 1;
}
else {
cache = (tios.c_cflag & CSIZE)==CS8;
}
return cache;
}
/************/
static int send_crs=0;
void
set_crmode(void)
{
send_crs = 1;
}
static
void
fxputc(FILE *f, int ch)
{
/* drop any sign */
ch = ch&0xff;
/* on 7-bit terminals, strip high bit */
if (!is8bit()) ch &= 0x7f;
/*
* Assume anything that isn't a control character is printable.
* We can't count on locale stuff to tell us what's printable
* because we might be looking at someone who uses different
* locale settings or is on the other side of the planet. So,
* strip 0-31, 127, 128-159, and 255. Note that not stripping
* 128-159 is asking for trouble, as 155 (M-esc) is interpreted
* as esc-[ by most terminals. Hopefully this won't break anyone's
* charset.
*
* It would be nice if we could set the terminal to display in the
* right charset, but we have no way to know what it is. feh.
*/
if (((ch&0x7f) >= 32 && (ch&0x7f) != 0x7f) || ch=='\t') {
putc(ch, f);
return;
}
if (ch=='\n') {
if (send_crs) putc('\r', f);
putc('\n', f);
return;
}
if (ch&0x80) {
putc('M', f);
putc('-', f);
ch &= 0x7f;
}
putc('^', f);
if (ch==0x7f) putc('?', f);
else putc(ch+'@', f);
}
void
xputc(int ch)
{
fxputc(stdout, ch);
}
static void fxputs(FILE *f, const char *buf) {
int i;
for (i=0; buf[i]; i++) fxputc(f, buf[i]);
}
int xprintf(const char *fmt, ...) {
char buf[1024];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
fxputs(stdout, buf);
return strlen(buf);
}
int eprintf(const char *fmt, ...) {
char buf[1024];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
fxputs(stderr, buf);
return strlen(buf);
}

194
third_party/finger/finger.1 vendored Normal file
View file

@ -0,0 +1,194 @@
.\" Copyright (c) 1989, 1990 The Regents of the University of California.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. All advertising materials mentioning features or use of this software
.\" must display the following acknowledgement:
.\" This product includes software developed by the University of
.\" California, Berkeley and its contributors.
.\" 4. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" from: @(#)finger.1 6.14 (Berkeley) 7/27/91
.\" $Id: finger.1,v 1.18 2000/07/30 23:56:57 dholland Exp $
.\"
.Dd August 15, 1999
.Dt FINGER 1
.Os "Linux NetKit (0.17)"
.Sh NAME
.Nm finger
.Nd user information lookup program
.Sh SYNOPSIS
.Nm finger
.Op Fl lmsp
.Op Ar user ...
.Op Ar user@host ...
.Sh DESCRIPTION
The
.Nm finger
displays information about the system users.
.Pp
Options are:
.Bl -tag -width flag
.It Fl s
.Nm Finger
displays the user's login name, real name, terminal name and write
status (as a ``*'' after the terminal name if write permission is
denied), idle time, login time, office location and office phone
number.
.Pp
Login time is displayed as month, day, hours and minutes, unless
more than six months ago, in which case the year is displayed rather
than the hours and minutes.
.Pp
Unknown devices as well as nonexistent idle and login times are
displayed as single asterisks.
.Pp
.It Fl l
Produces a multi-line format displaying all of the information
described for the
.Fl s
option as well as the user's home directory, home phone number, login
shell, mail status, and the contents of the files
.Dq Pa .plan ,
.Dq Pa .project ,
.Dq Pa .pgpkey
and
.Dq Pa .forward
from the user's home directory.
.Pp
Phone numbers specified as eleven digits are printed as ``+N-NNN-NNN-NNNN''.
Numbers specified as ten or seven digits are printed as the appropriate
subset of that string.
Numbers specified as five digits are printed as ``xN-NNNN''.
Numbers specified as four digits are printed as ``xNNNN''.
.Pp
If write permission is denied to the device, the phrase ``(messages off)''
is appended to the line containing the device name.
One entry per user is displayed with the
.Fl l
option; if a user is logged on multiple times, terminal information
is repeated once per login.
.Pp
Mail status is shown as ``No Mail.'' if there is no mail at all,
``Mail last read DDD MMM ## HH:MM YYYY (TZ)'' if the person has looked
at their mailbox since new mail arriving, or ``New mail received ...'',
`` Unread since ...'' if they have new mail.
.Pp
.It Fl p
Prevents
the
.Fl l
option of
.Nm finger
from displaying the contents of the
.Dq Pa .plan ,
.Dq Pa .project
and
.Dq Pa .pgpkey
files.
.It Fl m
Prevent matching of
.Ar user
names.
.Ar User
is usually a login name; however, matching will also be done on the
users' real names, unless the
.Fl m
option is supplied.
All name matching performed by
.Nm finger
is case insensitive.
.El
.Pp
If no options are specified,
.Nm finger
defaults to the
.Fl l
style output if operands are provided, otherwise to the
.Fl s
style.
Note that some fields may be missing, in either format, if information
is not available for them.
.Pp
If no arguments are specified,
.Nm finger
will print an entry for each user currently logged into the system.
.Pp
.Nm Finger
may be used to look up users on a remote machine.
The format is to specify a
.Ar user
as
.Dq Li user@host ,
or
.Dq Li @host ,
where the default output
format for the former is the
.Fl l
style, and the default output format for the latter is the
.Fl s
style.
The
.Fl l
option is the only option that may be passed to a remote machine.
.Pp
If standard output is a socket,
.Nm finger
will emit a carriage return (^M) before every linefeed (^J). This is
for processing remote finger requests when invoked by
.Xr fingerd 8 .
.Sh FILES
.Bl -tag -width mmmmmmmmmmmmmmm
.It Pa ~/.nofinger
If finger finds this file in a user's home directory, it will, for
finger requests originating outside the local host, firmly deny the
existence of that user. For this to work, the finger program, as
started by
.Xr fingerd 8 ,
must be able to see the
.Pa .nofinger
file. This generally means that the home directory containing the file
must have the other-users-execute bit set (o+w). See
.Xr chmod 1 .
If you use this feature for privacy, please test it with ``finger
@localhost'' before relying on it, just in case.
.It ~/.plan
.It ~/.project
.It ~/.pgp
These files are printed as part of a long-format request. The
.Pa .project
file is limited to one line; the
.Pa .plan
file may be arbitrarily long.
.El
.Sh SEE ALSO
.Xr chfn 1 ,
.Xr passwd 1 ,
.Xr w 1 ,
.Xr who 1
.Sh HISTORY
The
.Nm finger
command appeared in
.Bx 3.0 .

333
third_party/finger/finger.c vendored Normal file
View file

@ -0,0 +1,333 @@
/*
* Copyright (c) 1989 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/weirdtypes.h"
#include "libc/fmt/fmt.h"
#include "libc/nt/struct/msg.h"
#include "libc/runtime/utmp.h"
#include "libc/sock/sock.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/time/time.h"
#include "third_party/finger/finger.h"
#include "third_party/getopt/getopt.h"
#include "third_party/musl/passwd.h"
// clang-format off
/*
* Mail status reporting added 931007 by Luke Mewburn, <zak@rmit.edu.au>.
*/
char copyright[] =
"@(#) Copyright (c) 1989 The Regents of the University of California.\n"
"All rights reserved.\n";
/*
* from: @(#)finger.c 5.22 (Berkeley) 6/29/90
*/
char finger_rcsid[] = \
"$Id: finger.c,v 1.15 1999/12/18 16:41:51 dholland Exp $";
/*
* Finger prints out information about users. It is not portable since
* certain fields (e.g. the full user name, office, and phone numbers) are
* extracted from the gecos field of the passwd file which other UNIXes
* may not have or may use for other things. (This is not really true any
* more, btw.)
*
* There are currently two output formats; the short format is one line
* per user and displays login name, tty, login time, real name, idle time,
* and office location/phone number. The long format gives the same
* information (in a more legible format) as well as home directory, shell,
* mail info, and .plan/.project files.
*/
static void loginlist(void);
static void userlist(int argc, char *argv[]);
int lflag, pplan;
static int sflag, mflag;
static int enable_nofinger;
time_t now;
char tbuf[TBUFLEN];
PERSON *phead, *ptail; /* the linked list of all people */
int entries; /* number of people */
int main(int argc, char *argv[]) {
int ch;
struct sockaddr_in sin;
socklen_t slen = sizeof(sin);
while ((ch = getopt(argc, argv, "lmps")) != EOF) {
switch(ch) {
case 'l':
lflag = 1; /* long format */
break;
case 'm':
mflag = 1; /* do exact match of names */
break;
case 'p':
pplan = 1; /* don't show .plan/.project */
break;
case 's':
sflag = 1; /* short format */
break;
case '?':
case 'h':
default:
eprintf("usage: finger [-lmps] [login ...]\n");
return 1;
}
}
argc -= optind;
argv += optind;
if (getsockname(STDOUT_FILENO, (struct sockaddr *)&sin, &slen)==0) {
/*
* stdout is a socket. must be a network finger request,
* so emit CRs with our LFs at the ends of lines.
*/
set_crmode();
/*
* Also, enable .nofinger processing.
*/
enable_nofinger = 1;
}
/*
* Also check stdin for nofinger processing, because of older
* fingerds that make stdout a pipe for CRLF handling.
*/
if (getsockname(STDIN_FILENO, (struct sockaddr *)&sin, &slen)==0) {
enable_nofinger = 1;
}
time(&now);
setpwent();
if (!*argv) {
/*
* Assign explicit "small" format if no names given and -l
* not selected. Force the -s BEFORE we get names so proper
* screening will be done.
*/
if (!lflag) {
sflag = 1; /* if -l not explicit, force -s */
}
loginlist();
if (entries == 0) {
xprintf("No one logged on.\n");
}
}
else {
userlist(argc, argv);
/*
* Assign explicit "large" format if names given and -s not
* explicitly stated. Force the -l AFTER we get names so any
* remote finger attempts specified won't be mishandled.
*/
if (!sflag)
lflag = 1; /* if -s not explicit, force -l */
}
if (entries != 0) {
if (lflag) lflag_print();
else sflag_print();
}
return 0;
}
/* Returns 1 if .nofinger is found and enable_nofinger is set. */
static
int
check_nofinger(struct passwd *pw)
{
if (enable_nofinger) {
char path[PATH_MAX];
struct stat tripe;
snprintf(path, sizeof(path), "%s/.nofinger", pw->pw_dir);
if (stat(path, &tripe)==0) {
return 1;
}
}
return 0;
}
static void
loginlist(void)
{
PERSON *pn;
struct passwd *pw;
struct utmp *uptr;
char name[UT_NAMESIZE + 1];
name[UT_NAMESIZE] = '\0';
/*
* if (!freopen(_PATH_UTMP, "r", stdin)) {
* fprintf(stderr, "finger: can't read %s.\n", _PATH_UTMP);
* exit(2);
* }
*/
setutent();
while ((uptr = getutent())!=NULL) {
if (!uptr->ut_name[0])
continue;
#ifdef USER_PROCESS
if (uptr->ut_type != USER_PROCESS) continue;
#endif
if ((pn = find_person(uptr->ut_name)) == NULL) {
memcpy(name, uptr->ut_name, UT_NAMESIZE);
if ((pw = getpwnam(name)) == NULL)
continue;
if (check_nofinger(pw))
continue;
pn = enter_person(pw);
}
enter_where(uptr, pn);
}
for (pn = phead; lflag && pn != NULL; pn = pn->next)
enter_lastlog(pn);
endutent();
}
static void do_local(int argc, char *argv[], int *used) {
int i;
struct passwd *pw;
/*
* traverse the list of possible login names and check the login name
* and real name against the name specified by the user.
*/
if (mflag) {
for (i = 0; i < argc; i++)
if (used[i] >= 0 && (pw = getpwnam(argv[i]))) {
if (!check_nofinger(pw)) {
enter_person(pw);
used[i] = 1;
}
}
} else for (pw = getpwent(); pw; pw = getpwent())
for (i = 0; i < argc; i++)
if (used[i] >= 0 &&
(!strcasecmp(pw->pw_name, argv[i]) ||
match(pw, argv[i]))) {
if (!check_nofinger(pw)) {
enter_person(pw);
used[i] = 1;
}
}
/* list errors */
for (i = 0; i < argc; i++)
if (!used[i])
(void)eprintf("finger: %s: no such user.\n", argv[i]);
}
static void
userlist(int argc, char *argv[])
{
int i;
PERSON *pn;
PERSON *nethead, **nettail;
struct utmp *uptr;
int dolocal, *used;
used = calloc(argc, sizeof(int));
if (!used) {
eprintf("finger: out of space.\n");
exit(1);
}
/* pull out all network requests */
for (i = 0, dolocal = 0, nettail = &nethead; i < argc; i++) {
if (!strchr(argv[i], '@')) {
dolocal = 1;
continue;
}
pn = palloc();
*nettail = pn;
nettail = &pn->next;
pn->name = argv[i];
used[i] = -1;
}
*nettail = NULL;
if (dolocal) do_local(argc, argv, used);
/* handle network requests */
for (pn = nethead; pn; pn = pn->next) {
netfinger(pn->name);
if (pn->next || entries)
xputc('\n');
}
if (entries == 0)
return;
/*
* Scan thru the list of users currently logged in, saving
* appropriate data whenever a match occurs.
*/
/*
* if (!freopen(_PATH_UTMP, "r", stdin)) {
* (void)fprintf( stderr, "finger: can't read %s.\n", _PATH_UTMP);
* exit(1);
* }
*/
setutent();
while ((uptr = getutent())!=NULL) {
if (!uptr->ut_name[0])
continue;
#ifdef USER_PROCESS
if (uptr->ut_type != USER_PROCESS) continue;
#endif
if ((pn = find_person(uptr->ut_name)) == NULL) {
continue;
}
enter_where(uptr, pn);
}
for (pn = phead; pn != NULL; pn = pn->next) {
enter_lastlog(pn);
}
endutent();
}

121
third_party/finger/finger.h vendored Normal file
View file

@ -0,0 +1,121 @@
// clang-format off
/*
* Copyright (c) 1989 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from: @(#)finger.h 5.5 (Berkeley) 6/1/90
* $Id: finger.h,v 1.7 1999/09/14 10:51:11 dholland Exp $
*/
#include "third_party/musl/passwd.h"
#include "libc/runtime/utmp.h"
#include "libc/calls/weirdtypes.h"
/*
* All unique persons are linked in a list headed by "head" and linkd
* by the "next" field, as well as kept in a hash table.
*/
typedef struct person {
struct person *next; /* link to next person */
struct person *hlink; /* link to next person in hash bucket */
uid_t uid; /* user id */
char *dir; /* user's home directory */
char *homephone; /* pointer to home phone no. */
char *name; /* login name */
char *office; /* pointer to office name */
char *officephone; /* pointer to office phone no. */
char *realname; /* pointer to full name */
char *shell; /* user's shell */
time_t mailread; /* last time mail was read */
time_t mailrecv; /* last time mail was read */
struct where *whead, *wtail; /* list of where he is or has been */
} PERSON;
enum status { LASTLOG, LOGGEDIN };
typedef struct where {
struct where *next; /* next place he is or has been */
enum status info; /* type/status of request */
short writable; /* tty is writable */
time_t loginat; /* time of (last) login */
time_t idletime; /* how long idle (if logged in) */
char tty[UT_LINESIZE+1]; /* null terminated tty line */
char host[UT_HOSTSIZE+1]; /* null terminated remote host name */
} WHERE;
extern PERSON *phead, *ptail; /* the linked list of all people */
extern int entries; /* number of people */
#define TBUFLEN 1024
extern char tbuf[TBUFLEN]; /* temp buffer for anybody */
extern time_t now;
extern int lflag, pplan;
struct utmp;
PERSON *enter_person(struct passwd *);
PERSON *find_person(const char *name);
PERSON *palloc(void);
WHERE *walloc(PERSON *);
void lflag_print(void);
void sflag_print(void);
void enter_where(struct utmp *ut, PERSON *pn);
void enter_lastlog(PERSON *pn);
int match(struct passwd *pw, const char *user);
void netfinger(const char *name);
const char *prphone(const char *num);
#ifndef DAYSPERNYEAR
#define DAYSPERNYEAR 365
#endif
#ifndef SECSPERDAY
#define SECSPERDAY (60 * 60 * 24)
#endif
/* turn on crnl translation on output */
void set_crmode(void);
/* Display, masking control characters and possibly doing crnl translation */
void xputc(int ch);
void xputs(const char *buf);
int xprintf(const char *fmt, ...);
/* Send to stderr, possibly doing crnl translation */
int eprintf(const char *fmt, ...);
/* terminal inquiries */
int is8bit(void);
int getscreenwidth(void);

68
third_party/finger/finger.mk vendored Normal file
View file

@ -0,0 +1,68 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
PKGS += THIRD_PARTY_FINGER
THIRD_PARTY_FINGER_ARTIFACTS += THIRD_PARTY_FINGER_A
THIRD_PARTY_FINGER = $(THIRD_PARTY_FINGER_A_DEPS) $(THIRD_PARTY_FINGER_A)
THIRD_PARTY_FINGER_A = o/$(MODE)/third_party/finger/finger.a
THIRD_PARTY_FINGER_A_FILES := $(wildcard third_party/finger/*)
THIRD_PARTY_FINGER_A_HDRS = $(filter %.h,$(THIRD_PARTY_FINGER_A_FILES))
THIRD_PARTY_FINGER_A_INCS = $(filter %.inc,$(THIRD_PARTY_FINGER_A_FILES))
THIRD_PARTY_FINGER_A_SRCS = $(filter %.c,$(THIRD_PARTY_FINGER_A_FILES))
THIRD_PARTY_FINGER_A_OBJS = $(THIRD_PARTY_FINGER_A_SRCS:%.c=o/$(MODE)/%.o)
THIRD_PARTY_FINGER_A_DIRECTDEPS = \
LIBC_CALLS \
LIBC_FMT \
LIBC_INTRIN \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_RUNTIME \
LIBC_STDIO \
LIBC_STR \
LIBC_STUBS \
LIBC_SYSV \
LIBC_DNS \
LIBC_SOCK \
LIBC_TIME \
LIBC_UNICODE \
THIRD_PARTY_MUSL \
THIRD_PARTY_GETOPT
THIRD_PARTY_FINGER_A_DEPS := \
$(call uniq,$(foreach x,$(THIRD_PARTY_FINGER_A_DIRECTDEPS),$($(x))))
THIRD_PARTY_FINGER_A_CHECKS = \
$(THIRD_PARTY_FINGER_A).pkg
$(THIRD_PARTY_FINGER_A): \
third_party/finger/ \
$(THIRD_PARTY_FINGER_A).pkg \
$(THIRD_PARTY_FINGER_A_OBJS)
$(THIRD_PARTY_FINGER_A).pkg: \
$(THIRD_PARTY_FINGER_A_OBJS) \
$(foreach x,$(THIRD_PARTY_FINGER_A_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/third_party/finger/finger.com.dbg: \
$(THIRD_PARTY_FINGER) \
o/$(MODE)/third_party/finger/finger.o \
$(CRT) \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
THIRD_PARTY_FINGER_COMS = o/$(MODE)/third_party/finger/finger.com
THIRD_PARTY_FINGER_BINS = $(THIRD_PARTY_FINGER_COMS) $(THIRD_PARTY_FINGER_COMS:%=%.dbg)
THIRD_PARTY_FINGER_LIBS = $(foreach x,$(THIRD_PARTY_FINGER_ARTIFACTS),$($(x)))
THIRD_PARTY_FINGER_SRCS = $(foreach x,$(THIRD_PARTY_FINGER_ARTIFACTS),$($(x)_SRCS))
THIRD_PARTY_FINGER_HDRS = $(foreach x,$(THIRD_PARTY_FINGER_ARTIFACTS),$($(x)_HDRS))
THIRD_PARTY_FINGER_INCS = $(foreach x,$(THIRD_PARTY_FINGER_ARTIFACTS),$($(x)_INCS))
THIRD_PARTY_FINGER_CHECKS = $(foreach x,$(THIRD_PARTY_FINGER_ARTIFACTS),$($(x)_CHECKS))
THIRD_PARTY_FINGER_OBJS = $(foreach x,$(THIRD_PARTY_FINGER_ARTIFACTS),$($(x)_OBJS))
$(THIRD_PARTY_FINGER_OBJS): third_party/finger/finger.mk
.PHONY: o/$(MODE)/third_party/finger
o/$(MODE)/third_party/finger: \
$(THIRD_PARTY_FINGER_BINS) \
$(THIRD_PARTY_FINGER_CHECKS)

361
third_party/finger/lprint.c vendored Normal file
View file

@ -0,0 +1,361 @@
/*
* Copyright (c) 1989 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/fmt/fmt.h"
#include "libc/paths.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/s.h"
#include "libc/time/time.h"
#include "third_party/finger/finger.h"
// clang-format off
/*
* from: @(#)lprint.c 5.13 (Berkeley) 10/31/90
*/
char lprint_rcsid[] =
"$Id: lprint.c,v 1.11 1999/09/14 10:51:11 dholland Exp $";
static void lprint(PERSON *pn);
static int demi_print(char *str, int oddfield);
static int show_text(const char *directory, const char *file_name,
const char *header);
#define LINE_LEN 80
#define TAB_LEN 8 /* 8 spaces between tabs */
#define _PATH_FORWARD ".forward"
#define _PATH_PLAN ".plan"
#define _PATH_PROJECT ".project"
#define _PATH_PGPKEY ".pgpkey"
void
lflag_print(void)
{
register PERSON *pn = phead;
while (1) {
lprint(pn);
if (!pplan) {
show_text(pn->dir, _PATH_PGPKEY, "PGP key:\n");
show_text(pn->dir, _PATH_PROJECT, "Project:\n");
if (!show_text(pn->dir, _PATH_PLAN, "Plan:\n")) {
xprintf("No Plan.\n");
}
}
if (!(pn = pn->next))
break;
xputc('\n');
}
}
static void
lprint(PERSON *pn)
{
struct tm *delta, *tp;
WHERE *w;
int cpr, len, maxlen;
int oddfield;
char timebuf[128];
/*
* long format --
* login name
* real name
* home directory
* shell
* office, office phone, home phone if available
*/
xprintf("Login: %-15s\t\t\tName: %s\nDirectory: %-25s",
pn->name, pn->realname, pn->dir);
xprintf("\tShell: %-s\n", *pn->shell ? pn->shell : _PATH_BSHELL);
/*
* try and print office, office phone, and home phone on one line;
* if that fails, do line filling so it looks nice.
*/
#define OFFICE_TAG "Office"
#define OFFICE_PHONE_TAG "Office Phone"
oddfield = 0;
if (pn->office && pn->officephone &&
strlen(pn->office) + strlen(pn->officephone) +
sizeof(OFFICE_TAG) + 2 <= 5 * TAB_LEN)
{
snprintf(tbuf, TBUFLEN, "%s: %s, %s", OFFICE_TAG, pn->office,
prphone(pn->officephone));
oddfield = demi_print(tbuf, oddfield);
}
else {
if (pn->office) {
snprintf(tbuf, TBUFLEN, "%s: %s", OFFICE_TAG,
pn->office);
oddfield = demi_print(tbuf, oddfield);
}
if (pn->officephone) {
snprintf(tbuf, TBUFLEN, "%s: %s", OFFICE_PHONE_TAG,
prphone(pn->officephone));
oddfield = demi_print(tbuf, oddfield);
}
}
if (pn->homephone) {
snprintf(tbuf, TBUFLEN, "%s: %s", "Home Phone",
prphone(pn->homephone));
oddfield = demi_print(tbuf, oddfield);
}
if (oddfield) xputc('\n');
/*
* long format con't: * if logged in
* terminal
* idle time
* if messages allowed
* where logged in from
* if not logged in
* when last logged in
*/
/* find out longest device name for this user for formatting */
for (w = pn->whead, maxlen = -1; w != NULL; w = w->next)
if ((len = strlen(w->tty)) > maxlen)
maxlen = len;
/* find rest of entries for user */
for (w = pn->whead; w != NULL; w = w->next) {
switch (w->info) {
case LOGGEDIN:
tp = localtime(&w->loginat);
/*
* t = asctime(tp);
* tzset();
* tzn = tzname[daylight];
* cpr = printf("On since %.16s (%s) on %s",
* t, tzn, w->tty);
*/
strftime(timebuf, sizeof(timebuf),
"%a %b %e %R (%Z)", tp);
cpr = xprintf("On since %s on %s", timebuf, w->tty);
if (*w->host) {
cpr += xprintf(" from %s", w->host);
}
/*
* idle time is tough; if have one, print a comma,
* then spaces to pad out the device name, then the
* idle time. Follow with a comma if a remote login.
*/
delta = gmtime(&w->idletime);
if (delta->tm_yday || delta->tm_hour
|| delta->tm_min || delta->tm_sec) {
if (*w->host)
xputc('\n');
cpr += xprintf("%-*s",
(int) (maxlen - strlen(w->tty) + 3), "");
if (delta->tm_yday > 0) {
cpr += xprintf("%d day%s ",
delta->tm_yday,
delta->tm_yday == 1 ? "" : "s");
}
if (delta->tm_hour > 0) {
cpr += xprintf("%d hour%s ",
delta->tm_hour,
delta->tm_hour == 1 ? "" : "s");
}
if ((delta->tm_min > 0) && !delta->tm_yday) {
cpr += xprintf("%d minute%s ",
delta->tm_min,
delta->tm_min == 1 ? "" : "s");
}
if ((delta->tm_sec > 0) && !delta->tm_yday
&& !delta->tm_hour) {
cpr += xprintf("%d second%s ",
delta->tm_sec,
delta->tm_sec == 1 ? "" : "s");
}
cpr += xprintf("idle");
}
if (!w->writable) {
if (delta->tm_yday || delta->tm_hour
|| delta->tm_min || delta->tm_sec)
cpr += xprintf("\n ");
cpr += xprintf(" (messages off)");
}
break;
case LASTLOG:
if (w->loginat == 0) {
(void)xprintf("Never logged in.");
break;
}
tp = localtime(&w->loginat);
/*
* t = asctime(tp);
* tzset();
* tzn = tzname[daylight];
* if(now - w->loginat > SECSPERDAY * DAYSPERNYEAR / 2)
* cpr =
* printf("Last login %.16s %.4s (%s) on %s",
* t, t + 20, tzn, w->tty);
* else
* cpr = printf("Last login %.16s (%s) on %s",
* t, tzn, w->tty);
*/
if (now - w->loginat < SECSPERDAY * DAYSPERNYEAR / 2) {
strftime(timebuf, sizeof(timebuf),
"%a %b %e %R (%Z)", tp);
}
else {
strftime(timebuf, sizeof(timebuf),
"%a %b %e %R %Y (%Z)", tp);
}
cpr = xprintf("Last login %s on %s", timebuf, w->tty);
if (*w->host) {
cpr += xprintf(" from %s", w->host);
}
break;
}
xputc('\n');
}
/* If the user forwards mail elsewhere, tell us about it */
show_text(pn->dir, _PATH_FORWARD, "Mail forwarded to ");
/* Print the standard mailbox information. */
if (pn->mailrecv == -1)
xprintf("No mail.\n");
else if (pn->mailrecv > pn->mailread) {
tp = localtime(&pn->mailrecv);
/*
* t = asctime(tp);
* tzset();
* tzn = tzname[daylight];
* printf("New mail received %.16s %.4s (%s)\n", t,
* t + 20, tzn);
*/
strftime(timebuf, sizeof(timebuf),
"%a %b %e %R %Y (%Z)", tp);
xprintf("New mail received %s\n", timebuf);
tp = localtime(&pn->mailread);
/*
* t = asctime(tp);
* tzset();
* tzn = tzname[daylight];
* printf(" Unread since %.16s %.4s (%s)\n", t,
* t + 20, tzn);
*/
strftime(timebuf, sizeof(timebuf),
"%a %b %e %R %Y (%Z)", tp);
xprintf(" Unread since %s\n", timebuf);
} else {
tp = localtime(&pn->mailread);
/*
* t = asctime(tp);
* tzset();
* tzn = tzname[daylight];
* printf("Mail last read %.16s %.4s (%s)\n", t,
* t + 20, tzn);
*/
strftime(timebuf, sizeof(timebuf),
"%a %b %e %R %Y (%Z)", tp);
xprintf("Mail last read %s\n", timebuf);
}
}
static int
demi_print(char *str, int oddfield)
{
static int lenlast;
int lenthis, maxlen;
lenthis = strlen(str);
if (oddfield) {
/*
* We left off on an odd number of fields. If we haven't
* crossed the midpoint of the screen, and we have room for
* the next field, print it on the same line; otherwise,
* print it on a new line.
*
* Note: we insist on having the right hand fields start
* no less than 5 tabs out.
*/
maxlen = 5 * TAB_LEN;
if (maxlen < lenlast)
maxlen = lenlast;
if (((((maxlen / TAB_LEN) + 1) * TAB_LEN) +
lenthis) <= LINE_LEN) {
while(lenlast < (4 * TAB_LEN)) {
xputc('\t');
lenlast += TAB_LEN;
}
(void)xprintf("\t%s\n", str); /* force one tab */
} else {
(void)xprintf("\n%s", str); /* go to next line */
oddfield = !oddfield; /* this'll be undone below */
}
} else
(void)xprintf("%s", str);
oddfield = !oddfield; /* toggle odd/even marker */
lenlast = lenthis;
return(oddfield);
}
static int
show_text(const char *directory, const char *file_name, const char *header)
{
int ch, lastc = 0, fd;
FILE *fp;
struct stat sbuf1, sbuf2;
snprintf(tbuf, TBUFLEN, "%s/%s", directory, file_name);
if (lstat(tbuf, &sbuf1) || !S_ISREG(sbuf1.st_mode)) return 0;
fd = open(tbuf, O_RDONLY);
if (fd<0) return 0;
if (fstat(fd, &sbuf2)) { close(fd); return 0; }
/* if we didn't get the same file both times, bail */
if (sbuf1.st_dev!=sbuf2.st_dev || sbuf1.st_ino!=sbuf2.st_ino) {
close(fd);
return 0;
}
fp = fdopen(fd, "r");
if (fp == NULL) { close(fd); return 0; }
xprintf("%s", header);
while ((ch = getc(fp)) != EOF) {
xputc(ch);
lastc = ch;
}
if (lastc != '\n') xputc('\n');
fclose(fp);
return 1;
}

153
third_party/finger/net.c vendored Normal file
View file

@ -0,0 +1,153 @@
/*
* Copyright (c) 1989 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "libc/calls/calls.h"
#include "libc/dns/ent.h"
#include "libc/errno.h"
#include "libc/sock/sock.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/sock.h"
#include "third_party/finger/finger.h"
// clang-format off
#ifndef lint
/*static char sccsid[] = "from: @(#)net.c 5.5 (Berkeley) 6/1/90";*/
char net_rcsid[] = "$Id: net.c,v 1.9 1999/09/14 10:51:11 dholland Exp $";
#endif /* not lint */
void netfinger(const char *name) {
register FILE *fp;
struct in_addr defaddr;
register int c, sawret, ateol;
struct hostent *hp, def;
struct servent *sp;
struct sockaddr_in sn;
int s;
char *alist[1], *host;
host = strrchr(name, '@');
if (!host) return;
*host++ = '\0';
memset(&sn, 0, sizeof(sn));
sp = getservbyname("finger", "tcp");
if (!sp) {
eprintf("finger: tcp/finger: unknown service\n");
return;
}
sn.sin_port = sp->s_port;
hp = gethostbyname(host);
if (!hp) {
if (!inet_aton(host, &defaddr)) {
eprintf("finger: unknown host: %s\n", host);
return;
}
def.h_name = host;
def.h_addr_list = alist;
def.h_addr = (char *)&defaddr;
def.h_length = sizeof(struct in_addr);
def.h_addrtype = AF_INET;
def.h_aliases = 0;
hp = &def;
}
sn.sin_family = hp->h_addrtype;
if (hp->h_length > (int)sizeof(sn.sin_addr)) {
hp->h_length = sizeof(sn.sin_addr);
}
memcpy(&sn.sin_addr, hp->h_addr, hp->h_length);
if ((s = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0) {
eprintf("finger: socket: %s\n", strerror(errno));
return;
}
/* print hostname before connecting, in case it takes a while */
xprintf("[%s]\n", hp->h_name);
if (connect(s, (struct sockaddr *)&sn, sizeof(sn)) < 0) {
eprintf("finger: connect: %s\n", strerror(errno));
close(s);
return;
}
/* -l flag for remote fingerd */
if (lflag) write(s, "/W ", 3);
/* send the name followed by <CR><LF> */
write(s, name, strlen(name));
write(s, "\r\n", 2);
/*
* Read from the remote system; once we're connected, we assume some
* data. If none arrives, we hang until the user interrupts.
*
* If we see a <CR> or a <CR> with the high bit set, treat it as
* a newline; if followed by a newline character, only output one
* newline.
*
* Text is sent to xputc() for printability analysis.
*/
fp = fdopen(s, "r");
if (!fp) {
eprintf("finger: fdopen: %s\n", strerror(errno));
close(s);
return;
}
sawret = 0;
ateol = 1;
while ((c = getc(fp)) != EOF) {
c &= 0xff;
if (c == ('\r'|0x80) || c == ('\n'|0x80)) c &= 0x7f;
if (c == '\r') {
sawret = ateol = 1;
xputc('\n');
}
else if (sawret && c == '\n') {
sawret = 0;
/* don't print */
}
else {
if (c == '\n') ateol = 1;
sawret = 0;
xputc(c);
}
}
if (!ateol) xputc('\n');
fclose(fp);
}

172
third_party/finger/sprint.c vendored Normal file
View file

@ -0,0 +1,172 @@
/*
* Copyright (c) 1989 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "libc/alg/alg.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/time/time.h"
#include "third_party/finger/finger.h"
// clang-format off
#ifndef lint
/*static char sccsid[] = "from: @(#)sprint.c 5.8 (Berkeley) 12/4/90";*/
char sprint_rcsid[] = "$Id: sprint.c,v 1.10 1999/12/12 18:59:33 dholland Exp $";
#endif /* not lint */
static void stimeprint(WHERE *w);
static int psort(const void *a, const void *b);
static PERSON **sort(void);
void sflag_print(void) {
register PERSON *pn;
register WHERE *w;
register char *p;
PERSON **list;
int maxlname, maxrname, space, cnt;
list = sort();
/*
* short format --
* login name
* real name
* terminal name
* if terminal writeable (add an '*' to the terminal name
* if not)
* if logged in show idle time and day logged in, else
* show last login date and time. If > 6 moths,
* show year instead of time.
* office location
* office phone
*/
maxlname = maxrname = sizeof("Login ");
for (cnt = 0; cnt < entries; ++cnt) {
int l;
pn = list[cnt];
l = pn->name ? strlen(pn->name) : 1;
if (l > maxlname) maxlname = l;
l = pn->realname ? strlen(pn->realname) : 1;
if (l > maxrname) maxrname = l;
}
/* prevent screen overflow */
space = getscreenwidth() - 50;
if (maxlname + maxrname > space) maxrname = space - maxlname;
/* add a space if there's room */
if (maxlname + maxrname < space-2) { maxlname++; maxrname++; }
(void)xprintf("%-*s %-*s %s\n", maxlname, "Login", maxrname,
"Name", " Tty Idle Login Time Office Office Phone");
for (cnt = 0; cnt < entries; ++cnt) {
pn = list[cnt];
for (w = pn->whead; w != NULL; w = w->next) {
(void)xprintf("%-*.*s %-*.*s ", maxlname, maxlname,
pn->name, maxrname, maxrname,
pn->realname ? pn->realname : "");
if (!w->loginat) {
(void)xprintf(" * * No logins ");
goto office;
}
(void)xputc(w->info == LOGGEDIN && !w->writable ?
'*' : ' ');
if (*w->tty)
(void)xprintf("%-7.7s ", w->tty);
else
(void)xprintf(" ");
if (w->info == LOGGEDIN) {
stimeprint(w);
(void)xprintf(" ");
} else
(void)xprintf(" * ");
p = ctime(&w->loginat);
(void)xprintf("%.6s", p + 4);
if (now - w->loginat >= SECSPERDAY * DAYSPERNYEAR / 2)
(void)xprintf(" %.4s", p + 20);
else
(void)xprintf(" %.5s", p + 11);
office:
if (w->host[0] != '\0') {
xprintf(" (%s)", w->host);
} else {
if (pn->office)
(void)xprintf(" %-10.10s", pn->office);
else if (pn->officephone)
(void)xprintf(" %-10.10s", " ");
if (pn->officephone)
(void)xprintf(" %-.14s",
prphone(pn->officephone));
}
xputc('\n');
}
}
}
static PERSON **sort(void) {
register PERSON *pn, **lp;
PERSON **list;
if (!(list = (PERSON **)malloc((unsigned)(entries * sizeof(PERSON *))))) {
eprintf("finger: Out of space.\n");
exit(1);
}
for (lp = list, pn = phead; pn != NULL; pn = pn->next)
*lp++ = pn;
(void)qsort(list, entries, sizeof(PERSON *), psort);
return(list);
}
static int psort(const void *a, const void *b) {
const PERSON *const *p = (const PERSON *const *)a;
const PERSON *const *t = (const PERSON *const *)b;
return(strcmp((*p)->name, (*t)->name));
}
static void stimeprint(WHERE *w) {
register struct tm *delta;
delta = gmtime(&w->idletime);
if (!delta->tm_yday)
if (!delta->tm_hour)
if (!delta->tm_min)
(void)xprintf(" ");
else
(void)xprintf("%5d", delta->tm_min);
else
(void)xprintf("%2d:%02d",
delta->tm_hour, delta->tm_min);
else
(void)xprintf("%4dd", delta->tm_yday);
}

414
third_party/finger/util.c vendored Normal file
View file

@ -0,0 +1,414 @@
/*
* Copyright (c) 1989 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/stat.macros.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/paths.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "third_party/finger/finger.h"
// clang-format off
#ifndef lint
/*static char sccsid[] = "from: @(#)util.c 5.14 (Berkeley) 1/17/91";*/
char util_rcsid[] = "$Id: util.c,v 1.18 1999/09/28 22:53:58 netbug Exp $";
#endif /* not lint */
#define HBITS 8 /* number of bits in hash code */
#define HSIZE (1 << 8) /* hash table size */
#define HMASK (HSIZE - 1) /* hash code mask */
static PERSON *htab[HSIZE]; /* the buckets */
static int hash(const char *name);
static void find_idle_and_ttywrite(register WHERE *w) {
struct stat sb;
/* No device for X console. Utmp entry by XDM login (":0"). */
if (w->tty[0] == ':') {
w->idletime = 0; /* would be nice to have it emit ??? */
w->writable = 0;
return;
}
snprintf(tbuf, TBUFLEN, "%s/%s", _PATH_DEV, w->tty);
if (stat(tbuf, &sb) < 0) {
eprintf("finger: %s: %s\n", tbuf, strerror(errno));
return;
}
w->idletime = now < sb.st_atime ? 0 : now - sb.st_atime;
#define TALKABLE 0220 /* tty is writable if 220 mode */
w->writable = ((sb.st_mode & TALKABLE) == TALKABLE);
}
static void userinfo(PERSON *pn, struct passwd *pw) {
char *p;
struct stat sb;
char *bp;
char *rname;
int i, j, ct;
char *fields[4];
int nfields;
pn->uid = pw->pw_uid;
pn->name = strdup(pw->pw_name);
pn->dir = strdup(pw->pw_dir);
pn->shell = strdup(pw->pw_shell);
/* make a private copy of gecos to munge */
strncpy(tbuf, pw->pw_gecos, TBUFLEN);
tbuf[TBUFLEN-1] = 0; /* ensure null termination */
bp = tbuf;
/* why do we skip asterisks!?!? */
if (*bp == '*') ++bp;
/*
* fields[0] -> real name
* fields[1] -> office
* fields[2] -> officephone
* fields[3] -> homephone
*/
nfields = 0;
for (p = strtok(bp, ","); p; p = strtok(NULL, ",")) {
if (*p==0) p = NULL; // skip empties
if (nfields < 4) fields[nfields++] = p;
}
while (nfields<4) fields[nfields++] = NULL;
if (fields[0]) {
/*
* Ampersands in gecos get replaced by the capitalized login
* name. This is a major nuisance and whoever thought it up
* should be shot.
*/
p = fields[0];
/* First, count the number of ampersands. */
for (ct=i=0; p[i]; i++) if (p[i]=='&') ct++;
/* This tells us how much space we need to copy the name. */
rname = malloc(strlen(p) + ct*strlen(pw->pw_name) + 1);
if (!rname) {
eprintf("finger: Out of space.\n");
exit(1);
}
/* Now, do it */
for (i=j=0; p[i]; i++) {
if (p[i]=='&') {
strcpy(rname + j, pw->pw_name);
if (islower(rname[j])) {
rname[j] = toupper(rname[j]);
}
j += strlen(pw->pw_name);
}
else {
rname[j++] = p[i];
}
}
rname[j] = 0;
pn->realname = rname;
}
pn->office = fields[1] ? strdup(fields[1]) : NULL;
pn->officephone = fields[2] ? strdup(fields[2]) : NULL;
pn->homephone = fields[3] ? strdup(fields[3]) : NULL;
pn->mailrecv = -1; /* -1 == not_valid */
pn->mailread = -1; /* -1 == not_valid */
snprintf(tbuf, TBUFLEN, "%s/%s", _PATH_MAILDIR, pw->pw_name);
if (stat(tbuf, &sb) < 0) {
if (errno != ENOENT) {
eprintf("finger: %s: %s\n", tbuf, strerror(errno));
return;
}
}
else if (sb.st_size != 0) {
pn->mailrecv = sb.st_mtime;
pn->mailread = sb.st_atime;
}
}
int
match(struct passwd *pw, const char *user)
{
char *p;
int i, j, ct, rv=0;
char *rname;
strncpy(tbuf, pw->pw_gecos, TBUFLEN);
tbuf[TBUFLEN-1] = 0; /* guarantee null termination */
p = tbuf;
/* why do we skip asterisks!?!? */
if (*p == '*') ++p;
/* truncate the uninteresting stuff off the end of gecos */
p = strtok(p, ",");
if (!p) return 0;
/*
* Ampersands get replaced by the login name.
*/
/* First, count the number of ampersands. */
for (ct=i=0; p[i]; i++) if (p[i]=='&') ct++;
/* This tells us how much space we need to copy the name. */
rname = malloc(strlen(p) + ct*strlen(pw->pw_name) + 1);
if (!rname) {
eprintf("finger: Out of space.\n");
exit(1);
}
/* Now, do it */
for (i=j=0; p[i]; i++) {
if (p[i]=='&') {
strcpy(rname + j, pw->pw_name);
if (islower(rname[j])) rname[j] = toupper(rname[j]);
j += strlen(pw->pw_name);
}
else {
rname[j++] = p[i];
}
}
rname[j] = 0;
for (p = strtok(rname, "\t "); p && !rv; p = strtok(NULL, "\t ")) {
if (!strcasecmp(p, user))
rv = 1;
}
free(rname);
return rv;
}
static int get_lastlog(int fd, uid_t uid, struct lastlog *ll) {
loff_t pos;
if (fd == -1) return -1;
pos = (long)uid * sizeof(*ll);
if (lseek(fd, pos, SEEK_SET) != pos) return -1;
if (read(fd, ll, sizeof(*ll)) != sizeof(*ll)) return -1;
return 0;
}
void enter_lastlog(PERSON *pn) {
static int opened = 0, fd = -1;
WHERE *w;
struct lastlog ll;
int doit = 0;
/* some systems may not maintain lastlog, don't report errors. */
if (!opened) {
fd = open(_PATH_LASTLOG, O_RDONLY, 0);
opened = 1;
}
if (get_lastlog(fd, pn->uid, &ll)) {
/* as if never logged in */
ll.ll_line[0] = ll.ll_host[0] = '\0';
ll.ll_time = 0;
}
if ((w = pn->whead) == NULL)
doit = 1;
else if (ll.ll_time != 0) {
/* if last login is earlier than some current login */
for (; !doit && w != NULL; w = w->next)
if (w->info == LOGGEDIN && w->loginat < ll.ll_time)
doit = 1;
/*
* and if it's not any of the current logins
* can't use time comparison because there may be a small
* discrepency since login calls time() twice
*/
for (w = pn->whead; doit && w != NULL; w = w->next)
if (w->info == LOGGEDIN &&
strncmp(w->tty, ll.ll_line, UT_LINESIZE) == 0)
doit = 0;
}
if (doit) {
w = walloc(pn);
w->info = LASTLOG;
bcopy(ll.ll_line, w->tty, UT_LINESIZE);
w->tty[UT_LINESIZE] = 0;
bcopy(ll.ll_host, w->host, UT_HOSTSIZE);
w->host[UT_HOSTSIZE] = 0;
w->loginat = ll.ll_time;
}
}
void enter_where(struct utmp *ut, PERSON *pn) {
register WHERE *w = walloc(pn);
w->info = LOGGEDIN;
bcopy(ut->ut_line, w->tty, UT_LINESIZE);
w->tty[UT_LINESIZE] = 0;
bcopy(ut->ut_host, w->host, UT_HOSTSIZE);
w->host[UT_HOSTSIZE] = 0;
w->loginat = ut->ut_time;
find_idle_and_ttywrite(w);
}
PERSON * enter_person(struct passwd *pw) {
register PERSON *pn, **pp;
for (pp = htab + hash(pw->pw_name);
*pp != NULL && strcmp((*pp)->name, pw->pw_name) != 0;
pp = &(*pp)->hlink)
;
if ((pn = *pp) == NULL) {
pn = palloc();
entries++;
if (phead == NULL)
phead = ptail = pn;
else {
ptail->next = pn;
ptail = pn;
}
pn->next = NULL;
pn->hlink = NULL;
*pp = pn;
userinfo(pn, pw);
pn->whead = NULL;
}
return(pn);
}
PERSON *find_person(const char *name) {
register PERSON *pn;
/* name may be only UT_NAMESIZE long and not terminated */
for (pn = htab[hash(name)];
pn != NULL && strncmp(pn->name, name, UT_NAMESIZE) != 0;
pn = pn->hlink)
;
return(pn);
}
static int hash(const char *name) {
register int h, i;
h = 0;
/* name may be only UT_NAMESIZE long and not terminated */
for (i = UT_NAMESIZE; --i >= 0 && *name;)
h = ((h << 2 | h >> (HBITS - 2)) ^ *name++) & HMASK;
return(h);
}
PERSON *palloc(void) {
PERSON *p;
if ((p = (PERSON *)malloc((unsigned) sizeof(PERSON))) == NULL) {
eprintf("finger: Out of space.\n");
exit(1);
}
return(p);
}
WHERE *
walloc(PERSON *pn)
{
register WHERE *w;
if ((w = (WHERE *)malloc((unsigned) sizeof(WHERE))) == NULL) {
eprintf("finger: Out of space.\n");
exit(1);
}
if (pn->whead == NULL)
pn->whead = pn->wtail = w;
else {
pn->wtail->next = w;
pn->wtail = w;
}
w->next = NULL;
return(w);
}
const char *
prphone(const char *num)
{
char *p;
const char *q;
int len;
static char pbuf[15];
/* don't touch anything if the user has their own formatting */
for (q = num; *q; ++q)
if (!isdigit(*q))
return(num);
len = q - num;
p = pbuf;
switch(len) {
case 11: /* +0-123-456-7890 */
*p++ = '+';
*p++ = *num++;
*p++ = '-';
/* FALLTHROUGH */
case 10: /* 012-345-6789 */
*p++ = *num++;
*p++ = *num++;
*p++ = *num++;
*p++ = '-';
/* FALLTHROUGH */
case 7: /* 012-3456 */
*p++ = *num++;
*p++ = *num++;
*p++ = *num++;
break;
case 5: /* x0-1234 */
case 4: /* x1234 */
*p++ = 'x';
*p++ = *num++;
break;
default:
return num;
}
if (len != 4) {
*p++ = '-';
*p++ = *num++;
}
*p++ = *num++;
*p++ = *num++;
*p++ = *num++;
*p = '\0';
return(pbuf);
}

View file

@ -101,12 +101,6 @@ struct UnixErrno {
const char *call;
};
union SockAddr {
struct sockaddr s;
struct sockaddr_in i;
struct sockaddr_un u;
};
static lua_State *GL;
static void *LuaRealloc(lua_State *L, void *p, size_t n) {
@ -225,23 +219,26 @@ static int SysretInteger(lua_State *L, const char *call, int olderr,
}
}
static int MakeSockaddr(lua_State *L, int i, union SockAddr *sa,
static int MakeSockaddr(lua_State *L, int i, struct sockaddr_storage *ss,
uint32_t *salen) {
bzero(sa, sizeof(*sa));
if (lua_isstring(L, i)) {
sa->u.sun_family = AF_UNIX;
if (!memccpy(sa->u.sun_path, luaL_checkstring(L, i), 0,
sizeof(sa->u.sun_path))) {
bzero(ss, sizeof(*ss));
if (!lua_isinteger(L, i)) {
((struct sockaddr_un *)ss)->sun_family = AF_UNIX;
if (!memccpy(((struct sockaddr_un *)ss)->sun_path, luaL_checkstring(L, i),
0, sizeof(((struct sockaddr_un *)ss)->sun_path))) {
luaL_error(L, "unix path too long");
unreachable;
}
*salen = sizeof(struct sockaddr_un);
kprintf("shit %d\n", ((struct sockaddr_in *)ss)->sin_family);
return i + 1;
} else {
sa->i.sin_family = AF_INET;
sa->i.sin_addr.s_addr = htonl(luaL_optinteger(L, i, 0));
sa->i.sin_port = htons(luaL_optinteger(L, i + 1, 0));
((struct sockaddr_in *)ss)->sin_family = AF_INET;
((struct sockaddr_in *)ss)->sin_addr.s_addr =
htonl(luaL_optinteger(L, i, 0));
((struct sockaddr_in *)ss)->sin_port = htons(luaL_optinteger(L, i + 1, 0));
*salen = sizeof(struct sockaddr_in);
kprintf("wut %d\n", ((struct sockaddr_in *)ss)->sin_family);
return i + 2;
}
}
@ -1236,11 +1233,11 @@ static int LuaUnixSocketpair(lua_State *L) {
// └─→ nil, unix.Errno
static int LuaUnixBind(lua_State *L) {
uint32_t salen;
union SockAddr sa;
struct sockaddr_storage ss;
int olderr = errno;
MakeSockaddr(L, 2, &sa, &salen);
MakeSockaddr(L, 2, &ss, &salen);
return SysretBool(L, "bind", olderr,
bind(luaL_checkinteger(L, 1), &sa.s, salen));
bind(luaL_checkinteger(L, 1), &ss, salen));
}
// unix.connect(fd:int, ip:uint32, port:uint16)
@ -1249,11 +1246,11 @@ static int LuaUnixBind(lua_State *L) {
// └─→ nil, unix.Errno
static int LuaUnixConnect(lua_State *L) {
uint32_t salen;
union SockAddr sa;
struct sockaddr_storage ss;
int olderr = errno;
MakeSockaddr(L, 2, &sa, &salen);
MakeSockaddr(L, 2, &ss, &salen);
return SysretBool(L, "connect", olderr,
connect(luaL_checkinteger(L, 1), &sa.s, salen));
connect(luaL_checkinteger(L, 1), &ss, salen));
}
// unix.listen(fd:int[, backlog:int])
@ -1510,14 +1507,14 @@ static int LuaUnixSendto(lua_State *L) {
char *data;
size_t size;
uint32_t salen;
union SockAddr sa;
struct sockaddr_storage ss;
int i, fd, flags, olderr = errno;
fd = luaL_checkinteger(L, 1);
data = luaL_checklstring(L, 2, &size);
i = MakeSockaddr(L, 3, &sa, &salen);
i = MakeSockaddr(L, 3, &ss, &salen);
flags = luaL_optinteger(L, i, 0);
return SysretInteger(L, "sendto", olderr,
sendto(fd, data, size, flags, &sa.s, salen));
sendto(fd, data, size, flags, &ss, salen));
}
// unix.shutdown(fd:int, how:int)

View file

@ -8,6 +8,7 @@ o/$(MODE)/third_party: \
o/$(MODE)/third_party/chibicc \
o/$(MODE)/third_party/compiler_rt \
o/$(MODE)/third_party/dlmalloc \
o/$(MODE)/third_party/finger \
o/$(MODE)/third_party/gdtoa \
o/$(MODE)/third_party/getopt \
o/$(MODE)/third_party/libcxx \