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

View file

@ -148,6 +148,7 @@ include third_party/linenoise/linenoise.mk
include third_party/maxmind/maxmind.mk
include third_party/lua/lua.mk
include third_party/make/make.mk
include third_party/finger/finger.mk
include third_party/argon2/argon2.mk
include third_party/smallz4/smallz4.mk
include third_party/sqlite3/sqlite3.mk

View file

@ -39,7 +39,7 @@
* @param service is the port number as a string
* @param hints may be passed to specialize behavior (optional)
* @param res receives a pointer that must be freed with freeaddrinfo(),
* and won't be modified if -1 is returned
* and won't be modified if non-zero is returned
* @return 0 on success or EAI_xxx value
* @threadsafe
*/

View file

@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
@ -19,8 +19,10 @@
#include "libc/str/str.h"
/**
* Copies memory the legacy way.
* Moves memory the BSD way.
*
* Please use memmove() instead. Note the order of arguments.
*/
void *bcopy(void *dst, const void *src, size_t n) {
return memmove(dst, src, n);
void bcopy(const void *src, void *dest, size_t n) {
memmove(dest, src, n);
}

4
libc/isystem/utmp.h Normal file
View file

@ -0,0 +1,4 @@
#ifndef COSMOPOLITAN_LIBC_ISYSTEM_UTMP_H_
#define COSMOPOLITAN_LIBC_ISYSTEM_UTMP_H_
#include "libc/runtime/utmp.h"
#endif /* COSMOPOLITAN_LIBC_ISYSTEM_UTMP_H_ */

4
libc/isystem/utmpx.h Normal file
View file

@ -0,0 +1,4 @@
#ifndef COSMOPOLITAN_LIBC_ISYSTEM_UTMPX_H_
#define COSMOPOLITAN_LIBC_ISYSTEM_UTMPX_H_
#include "libc/runtime/utmpx.h"
#endif /* COSMOPOLITAN_LIBC_ISYSTEM_UTMPX_H_ */

View file

@ -0,0 +1,28 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
__utmpxname:
.errno
mov ENOTSUP(%rip),%edx
mov %edx,(%rax)
ret
.endfn __utmpxname,globl
.alias __utmpxname,utmpname
.alias __utmpxname,utmpxname

47
libc/runtime/utmp.h Normal file
View file

@ -0,0 +1,47 @@
#ifndef COSMOPOLITAN_LIBC_RUNTIME_UTMP_H_
#define COSMOPOLITAN_LIBC_RUNTIME_UTMP_H_
#include "libc/calls/weirdtypes.h"
#include "libc/runtime/utmpx.h"
#define ACCOUNTING 9
#define UT_NAMESIZE 32
#define UT_HOSTSIZE 256
#define UT_LINESIZE 32
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct lastlog {
time_t ll_time;
char ll_line[UT_LINESIZE];
char ll_host[UT_HOSTSIZE];
};
#define ut_time ut_tv.tv_sec
#define ut_name ut_user
#define ut_addr ut_addr_v6[0]
#define utmp utmpx
#define e_exit __e_exit
#define e_termination __e_termination
int login_tty(int);
int utmpname(const char *);
struct utmp *getutent(void);
struct utmp *getutid(const struct utmp *);
struct utmp *getutline(const struct utmp *);
struct utmp *pututline(const struct utmp *);
void endutent(void);
void setutent(void);
void updwtmp(const char *, const struct utmp *);
#define _PATH_UTMP "/dev/null/utmp"
#define _PATH_WTMP "/dev/null/wtmp"
#define UTMP_FILE _PATH_UTMP
#define WTMP_FILE _PATH_WTMP
#define UTMP_FILENAME _PATH_UTMP
#define WTMP_FILENAME _PATH_WTMP
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_RUNTIME_UTMP_H_ */

51
libc/runtime/utmpx.h Normal file
View file

@ -0,0 +1,51 @@
#ifndef COSMOPOLITAN_LIBC_RUNTIME_UTMPX_H_
#define COSMOPOLITAN_LIBC_RUNTIME_UTMPX_H_
#include "libc/calls/struct/timeval.h"
#include "libc/calls/weirdtypes.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct utmpx {
short ut_type;
pid_t ut_pid;
char ut_line[32];
char ut_id[4];
char ut_user[32];
char ut_host[256];
struct {
short __e_termination;
short __e_exit;
} ut_exit;
long ut_session;
struct timeval ut_tv;
unsigned ut_addr_v6[4];
char __unused[20];
};
void endutxent(void);
struct utmpx *getutxent(void);
struct utmpx *getutxid(const struct utmpx *);
struct utmpx *getutxline(const struct utmpx *);
struct utmpx *pututxline(const struct utmpx *);
void setutxent(void);
#if defined(_BSD_SOURCE) || defined(_GNU_SOURCE)
#define e_exit __e_exit
#define e_termination __e_termination
void updwtmpx(const char *, const struct utmpx *);
int utmpxname(const char *);
#endif
#define EMPTY 0
#define RUN_LVL 1
#define BOOT_TIME 2
#define NEW_TIME 3
#define OLD_TIME 4
#define INIT_PROCESS 5
#define LOGIN_PROCESS 6
#define USER_PROCESS 7
#define DEAD_PROCESS 8
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_RUNTIME_UTMPX_H_ */

View file

@ -19,7 +19,9 @@
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/sock/internal.h"
#include "libc/sock/sock.h"
#include "libc/sock/sockdebug.h"
#include "libc/sock/syscall_fd.internal.h"
#include "libc/sysv/errfuns.h"

View file

@ -89,6 +89,7 @@ void *memcpy(void *restrict, const void *restrict, size_t) memcpyesque;
void *mempcpy(void *restrict, const void *restrict, size_t) memcpyesque;
void *memccpy(void *restrict, const void *restrict, int, size_t) memcpyesque;
void *memeqmask(void *, const void *, const void *, size_t) memcpyesque;
void bcopy(const void *, void *, size_t) memcpyesque;
void explicit_bzero(void *, size_t);
int bcmp(const void *, const void *, size_t) strlenesque;

26
libc/stubs/endutxent.S Normal file
View file

@ -0,0 +1,26 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
// Closes user accounting database.
// @note unsupported
endutxent:
ret
.endfn endutxent,globl
.alias endutxent,endutent

27
libc/stubs/getutxent.S Normal file
View file

@ -0,0 +1,27 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
// Reads next entry in user accounting database.
// @note unsupported
getutxent:
xor %eax,%eax
ret
.endfn getutxent,globl
.alias getutxent,getutent

27
libc/stubs/getutxid.S Normal file
View file

@ -0,0 +1,27 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
// Searches forward in the user accounting database.
// @note unsupported
getutxid:
xor %eax,%eax
ret
.endfn getutxid,globl
.alias getutxid,getutid

27
libc/stubs/getutxline.S Normal file
View file

@ -0,0 +1,27 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
// Searches forward in the user accounting database.
// @note unsupported
getutxline:
xor %eax,%eax
ret
.endfn getutxline,globl
.alias getutxline,getutline

27
libc/stubs/pututxline.S Normal file
View file

@ -0,0 +1,27 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
// Puts in the user accounting database.
// @note unsupported
pututxline:
xor %eax,%eax
ret
.endfn pututxline,globl
.alias pututxline,pututline

26
libc/stubs/setutxent.S Normal file
View file

@ -0,0 +1,26 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
// Rewinds the user accounting database.
// @note unsupported
setutxent:
ret
.endfn setutxent,globl
.alias setutxent,setutent

26
libc/stubs/updwtmpx.S Normal file
View file

@ -0,0 +1,26 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
// Does something to the user accounting database.
// @note unsupported
updwtmpx:
ret
.endfn updwtmpx,globl
.alias updwtmpx,updwtmp

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 \

View file

@ -0,0 +1,70 @@
-- UNIX Finger Example
local function WriteForm(host, user)
Write([[<!doctype html>
<title>redbean unix finger demo</title>
<style>
body { padding: 1em; }
h1 a { color: inherit; text-decoration: none; }
h1 img { border: none; vertical-align: middle; }
input { margin: .5em; padding: .25em; }
pre { margin-left: 2em; }
p { word-break: break-word; max-width: 650px; }
dt { font-weight: bold; }
dd { margin-top: 1em; margin-bottom: 1em; }
.hdr { text-indent: -1em; padding-left: 1em; }
</style>
<h1>
<a href="/"><img src="/redbean.png"></a>
<a href="unix-finger.lua">redbean unix finger demo</a>
</h1>
<p>
Your redbean is able to function as an finger client. Lua server
pages can use the <code>unix</code> module to implement protocols
that your redbean wasn't originally intended to support. All it
takes is few lines of code!
</p>
<form action="unix-finger.lua" method="post">
<input type="text" id="host" name="host" size="40"
value="%s" placeholder="host" autofocus>
<label for="host">host</label>
<br>
<input type="text" id="user" name="user" size="40"
value="%s" placeholder="user">
<label for="user">user</label>
<br>
<input type="submit" value="finger">
</form>
]] % {EscapeHtml(host), EscapeHtml(user)})
end
local function main()
if IsPublicIp(GetClientAddr()) then
ServeError(403)
elseif GetMethod() == 'GET' or GetMethod() == 'HEAD' then
WriteForm('graph.no', 'new_york')
elseif GetMethod() == 'POST' then
ip = assert(ResolveIp(GetParam('host')))
fd = assert(unix.socket())
assert(unix.connect(fd, ip, 79))
assert(unix.write(fd, GetParam('user') .. '\r\n'))
response = ''
while true do
data = assert(unix.read(fd))
if data == '' then
break
end
response = response .. data
end
assert(unix.close(fd))
WriteForm(GetParam('host'), GetParam('user'))
Write('<pre>\r\n')
Write(EscapeHtml(VisualizeControlCodes(response)))
Write('</pre>\r\n')
else
ServeError(405)
SetHeader('Allow', 'GET, HEAD, POST')
end
end
main()

View file

@ -1449,6 +1449,28 @@ FUNCTIONS
value will be the `"0b"`-prefixed binary str. The result is
currently modulo 2^64. Negative numbers are converted to unsigned.
ResolveIp(hostname:str)
├─→ ip:uint32
└─→ nil, error:str
Gets IP address associated with hostname.
This function first checks if hostname is already an IP address, in
which case it returns the result of `ParseIp`. Otherwise, it checks
HOSTS.TXT on the local system and returns the first IPv4 address
associated with hostname. If no such entry is found, a DNS lookup is
performed using the system configured (e.g. /etc/resolv.conf) DNS
resolution service. If the service returns multiple IN A records
then only the first one is reutrned.
The returned address is word-encoded in host endian order. For
example, 1.2.3.4 is encoded as 0x01020304. The `FormatIp` function
may be used to turn this value back into a string.
If no IP address could be found, then nil is returned alongside a
string of unspecified format describing the error. Calls to this
function may be wrapped in assert() if an exception is desired.
────────────────────────────────────────────────────────────────────────────────

View file

@ -21,6 +21,7 @@
#include "libc/bits/popcnt.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/rusage.h"
#include "libc/dns/dns.h"
#include "libc/fmt/itoa.h"
#include "libc/fmt/leb128.h"
#include "libc/intrin/kprintf.h"
@ -41,7 +42,9 @@
#include "libc/runtime/sysconf.h"
#include "libc/sock/sock.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/rusage.h"
#include "libc/sysv/consts/sock.h"
#include "libc/time/time.h"
#include "libc/x/x.h"
#include "net/http/escape.h"
@ -379,6 +382,27 @@ int LuaSlurp(lua_State *L) {
}
}
int LuaResolveIp(lua_State *L) {
ssize_t rc;
int64_t ip;
const char *host;
struct addrinfo *ai = NULL;
struct addrinfo hint = {AI_NUMERICSERV, AF_INET, SOCK_STREAM, IPPROTO_TCP};
host = luaL_checkstring(L, 1);
if ((ip = ParseIp(host, -1)) != -1) {
lua_pushinteger(L, ntohl(ai->ai_addr4->sin_addr.s_addr));
return 1;
} else if ((rc = getaddrinfo(host, "0", &hint, &ai)) == EAI_SUCCESS) {
lua_pushinteger(L, ntohl(ai->ai_addr4->sin_addr.s_addr));
freeaddrinfo(ai);
return 1;
} else {
lua_pushnil(L);
lua_pushfstring(L, "%s: DNS lookup failed: EAI_%s", host, gai_strerror(rc));
return 2;
}
}
static int LuaCheckControlFlags(lua_State *L, int idx) {
int f = luaL_checkinteger(L, idx);
if (f & ~(kControlWs | kControlC0 | kControlC1)) {

View file

@ -70,6 +70,7 @@ int LuaRand64(lua_State *);
int LuaRdrand(lua_State *);
int LuaRdseed(lua_State *);
int LuaRdtsc(lua_State *);
int LuaResolveIp(lua_State *);
int LuaSetLogLevel(lua_State *);
int LuaSha1(lua_State *);
int LuaSha224(lua_State *);

View file

@ -173,6 +173,7 @@ o/$(MODE)/tool/net/demo/unix-subprocess.lua.zip.o \
o/$(MODE)/tool/net/demo/unix-webserver.lua.zip.o \
o/$(MODE)/tool/net/demo/unix-dir.lua.zip.o \
o/$(MODE)/tool/net/demo/unix-info.lua.zip.o \
o/$(MODE)/tool/net/demo/unix-finger.lua.zip.o \
o/$(MODE)/tool/net/demo/fetch.lua.zip.o \
o/$(MODE)/tool/net/demo/call-lua-module.lua.zip.o \
o/$(MODE)/tool/net/demo/store-asset.lua.zip.o \
@ -222,6 +223,7 @@ o/$(MODE)/tool/net/redbean-demo.com.dbg: \
o/$(MODE)/tool/net/demo/unix-webserver.lua.zip.o \
o/$(MODE)/tool/net/demo/unix-dir.lua.zip.o \
o/$(MODE)/tool/net/demo/unix-info.lua.zip.o \
o/$(MODE)/tool/net/demo/unix-finger.lua.zip.o \
o/$(MODE)/tool/net/demo/fetch.lua.zip.o \
o/$(MODE)/tool/net/demo/store-asset.lua.zip.o \
o/$(MODE)/tool/net/demo/call-lua-module.lua.zip.o \

View file

@ -5131,6 +5131,7 @@ static const luaL_Reg kLuaFuncs[] = {
{"Rdrand", LuaRdrand}, //
{"Rdseed", LuaRdseed}, //
{"Rdtsc", LuaRdtsc}, //
{"ResolveIp", LuaResolveIp}, //
{"Route", LuaRoute}, //
{"RouteHost", LuaRouteHost}, //
{"RoutePath", LuaRoutePath}, //