9front - general discussion about 9front
 help / color / mirror / Atom feed
* [9front] [Patch] APE changes (2019 Lufia patches)
@ 2021-02-22  2:48 bsdsm
  2021-02-22  4:14 ` ori
                   ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: bsdsm @ 2021-02-22  2:48 UTC (permalink / raw)
  To: 9front

Hello,

This patch is a pared down version of the changes that Lufia made back in
2019; the essential changes from 'add funcs and types to APE' and all of
'add #include_next directive'. An unmodified diff of the former pull
request can be found here[1] for comparison.

This has only been tested on an amd64 build. Works cleanly with tree as of
Feb 20, 2021 changes.


[1]: https://patch-diff.githubusercontent.com/raw/lufia/plan9/pull/5.diff



diff -r bfe93397b157 386/include/ape/_apetypes.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/386/include/ape/_apetypes.h	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,7 @@
+#if !defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN)
+#define	__BYTE_ORDER	__LITTLE_ENDIAN
+#endif
+
+#if !defined(BYTE_ORDER) && defined(LITTLE_ENDIAN)
+#define	BYTE_ORDER	LITTLE_ENDIAN
+#endif
diff -r bfe93397b157 amd64/include/ape/_apetypes.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/amd64/include/ape/_apetypes.h	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,11 @@
+#ifndef _BITS64
+#define _BITS64
+#endif
+
+#if !defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN)
+#define	__BYTE_ORDER	__LITTLE_ENDIAN
+#endif
+
+#if !defined(BYTE_ORDER) && defined(LITTLE_ENDIAN)
+#define	BYTE_ORDER	LITTLE_ENDIAN
+#endif
diff -r bfe93397b157 sys/include/ape/bsd.h
--- a/sys/include/ape/bsd.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/bsd.h	Fri Feb 12 10:59:32 2021 -0700
@@ -36,6 +36,12 @@
 extern int	rcmd(char**, int, char*, char*, char*, int*);
 extern int	strcasecmp(char*, char*);
 extern int	strncasecmp(char*, char*,int);
+extern unsigned int	arc4random(void);
+extern void	arc4random_buf(void*, size_t);
+extern int	getentropy(void*, size_t);
+
+extern const char*	getprogname(void);
+extern void	setprogname(const char*);

 extern int	getopt(int, char**, char*);
 extern int	opterr;
@@ -44,9 +50,9 @@
 extern char	*optarg;
 extern char	*mktemp(char *);
 extern char	*sys_errlist[];
-extern int		sys_nerr;
+extern int	sys_nerr;

-extern int	gethostname(char*, int);
+extern int	gethostname(char*, size_t);

 #ifdef __cplusplus
 }
diff -r bfe93397b157 sys/include/ape/errno.h
--- a/sys/include/ape/errno.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/errno.h	Fri Feb 12 10:59:32 2021 -0700
@@ -80,6 +80,16 @@
 #define ECANCELED	61
 #define EINPROGRESS	62

+/* from research unix */
+#define ETXTBSY	63
+
+/* Added in more recent 1003.x versions */
+#define EALREADY	64
+#define ECONNRESET	65
+
+#define EOVERFLOW	66
+#define ELOOP		67
+
 #endif /* _POSIX_SOURCE */

 #endif /* __ERRNO */
diff -r bfe93397b157 sys/include/ape/inttypes.h
--- a/sys/include/ape/inttypes.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/inttypes.h	Fri Feb 12 10:59:32 2021 -0700
@@ -11,21 +11,25 @@
 #define PRId16 "d"
 #define PRId32 "d"
 #define PRId64 "lld"
+#define PRIdMAX "lld"

 #define PRIo8 "o"
 #define PRIo16 "o"
 #define PRIo32 "o"
 #define PRIo64 "llo"
+#define PRIoMAX "llo"

 #define PRIx8 "x"
 #define PRIx16 "x"
 #define PRIx32 "x"
 #define PRIx64 "llx"
+#define PRIxMAX "llx"

 #define PRIu8 "u"
 #define PRIu16 "u"
 #define PRIu32 "u"
 #define PRIu64 "llu"
+#define PRIuMAX "llu"

 extern intmax_t strtoimax(const char *, char **, int);

diff -r bfe93397b157 sys/include/ape/lib9.h
--- a/sys/include/ape/lib9.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/lib9.h	Fri Feb 12 10:59:32 2021 -0700
@@ -74,5 +74,6 @@
 extern	unsigned long	getfsr(void);
 extern	void		setfcr(unsigned long);
 extern	void		setfsr(unsigned long);
+extern	vlong		nsec(void);

 #endif
diff -r bfe93397b157 sys/include/ape/libgen.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/include/ape/libgen.h	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,16 @@
+#ifndef __LIBGEN_H
+#define __LIBGEN_H
+#pragma lib "/$M/lib/ape/libap.a"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern char *dirname(char *path);
+extern char *basename(char *path);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r bfe93397b157 sys/include/ape/machine/endian.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/include/ape/machine/endian.h	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,10 @@
+#ifndef _ENDIAN_H_
+#define _ENDIAN_H_
+
+#define LITTLE_ENDIAN	1234
+#define BIG_ENDIAN	4321
+#define PDP_ENDIAN	3412
+
+#include "_apetypes.h"
+
+#endif
diff -r bfe93397b157 sys/include/ape/netdb.h
--- a/sys/include/ape/netdb.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/netdb.h	Fri Feb 12 10:59:32 2021 -0700
@@ -147,6 +147,8 @@
 #define NI_NUMERICSCOPE	0x0010	/* For IPv6 addresses, the numeric form of
the scope identifier is returned
 				   instead of its name. */
 #define NI_DGRAM	0x0020	/* Indicates that the service is a datagram
service (SOCK_DGRAM). */
+#define NI_MAXHOST	1025
+#define NI_MAXSERV	32

 /* Error values for `getaddrinfo' and `getnameinfo' functions.  */
 #define EAI_BADFLAGS	  -1	/* Invalid value for `ai_flags' field */
diff -r bfe93397b157 sys/include/ape/poll.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/include/ape/poll.h	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,41 @@
+#ifndef __POLL_H
+#define __POLL_H
+#pragma lib "/$M/lib/ape/libap.a"
+
+#ifndef	FD_SETSIZE
+#define	FD_SETSIZE	96
+#endif
+
+struct pollfd {
+	int fd;			/* file descriptor */
+	short events;	/* events to look for */
+	short revents;	/* events returned */
+};
+
+typedef unsigned long nfds_t;
+
+#define	POLLIN		0x001
+#define	POLLPRI		0x002
+#define	POLLOUT		0x004
+#define	POLLERR		0x008
+#define	POLLHUP		0x010
+#define	POLLNVAL	0x020
+
+#define	POLLRDNORM	0x040
+#define	POLLRDBAND	0x080
+#define	POLLWRNORM	POLLOUT
+#define	POLLWRBAND	0x100
+
+#define	INFTIM	-1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int poll(struct pollfd fds[], nfds_t nfds, int timeout);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r bfe93397b157 sys/include/ape/pthread.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/include/ape/pthread.h	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,105 @@
+#ifndef __PTHREAD_H
+#define __PTHREAD_H
+#pragma lib "/$M/lib/ape/libpthread.a"
+
+#define _LOCK_EXTENSION
+#define _QLOCK_EXTENSION
+#include <sys/types.h>
+#include <unistd.h>
+#include <lock.h>
+#include <qlock.h>
+
+typedef struct pthread_once pthread_once_t;
+typedef pid_t pthread_t;
+typedef int pthread_attr_t;
+typedef struct pthread_mutex pthread_mutex_t;
+typedef int pthread_mutexattr_t;
+typedef struct pthread_cond pthread_cond_t;
+typedef int pthread_condattr_t;
+typedef struct pthread_key pthread_key_t;
+
+enum {
+	PTHREAD_THREADS_MAX = 1000,
+
+	PTHREAD_CANCEL_DISABLE = 1,
+};
+
+struct pthread_once {
+	Lock l;
+	int once;
+};
+struct pthread_mutex {
+	QLock l;
+
+	Lock mu;
+	pthread_t pid;
+	int ref;
+	pthread_mutexattr_t attr;
+};
+struct pthread_cond {
+	QLock l;
+	Rendez r;
+};
+struct pthread_key {
+	Lock l;
+	void (*destroy)(void*);
+	struct {
+		pthread_t	pid;
+		const void	*p;
+	} *arenas;
+	int n;
+};
+
+#define PTHREAD_ONCE_INIT		{ 0 }
+#define PTHREAD_MUTEX_INITIALIZER	{ 0 }
+#define PTHREAD_MUTEX_DEFAULT		0
+#define PTHREAD_MUTEX_NORAML		1
+#define PTHREAD_MUTEX_RECURSIVE	2
+#define PTHREAD_COND_INITIALIZER	{ 0 }
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int	pthread_atfork(void (*)(void), void (*)(void), void (*)(void));
+extern int	pthread_once(pthread_once_t*, void (*)(void));
+extern pthread_t	pthread_self(void);
+extern int	pthread_equal(pthread_t, pthread_t);
+extern int	pthread_create(pthread_t*, pthread_attr_t*, void *(*)(void*),
void*);
+extern void	pthread_exit(void*);
+extern int	pthread_join(pthread_t, void**);
+
+extern int	pthread_mutexattr_init(pthread_mutexattr_t*);
+extern int	pthread_mutexattr_destroy(pthread_mutexattr_t*);
+extern int	pthread_mutexattr_settype(pthread_mutexattr_t*, int);
+
+extern int	pthread_mutex_init(pthread_mutex_t*, const pthread_mutexattr_t*);
+extern int	pthread_mutex_lock(pthread_mutex_t*);
+extern int	pthread_mutex_unlock(pthread_mutex_t*);
+extern int	pthread_mutex_trylock(pthread_mutex_t*);
+extern int	pthread_mutex_destroy(pthread_mutex_t*);
+
+extern int	pthread_cond_init(pthread_cond_t*, pthread_condattr_t*);
+extern int	pthread_cond_wait(pthread_cond_t*, pthread_mutex_t*);
+extern int	pthread_cond_signal(pthread_cond_t*);
+extern int	pthread_cond_broadcast(pthread_cond_t*);
+extern int	pthread_cond_destroy(pthread_cond_t*);
+
+extern int	pthread_key_create(pthread_key_t*, void (*)(void*));
+extern int	pthread_key_delete(pthread_key_t);
+extern void	*pthread_getspecific(pthread_key_t);
+extern int	pthread_setspecific(pthread_key_t, const void*);
+
+extern int	pthread_setcancelstate(int, int*);
+
+#ifndef _PTHREAD_SIGMASK
+#define _PTHREAD_SIGMASK
+#include <signal.h>
+extern int	pthread_sigmask(int, const sigset_t*, sigset_t*);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r bfe93397b157 sys/include/ape/qlock.h
--- a/sys/include/ape/qlock.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/qlock.h	Fri Feb 12 10:59:32 2021 -0700
@@ -26,6 +26,14 @@
 	QLp 	*tail;
 } QLock;

+typedef
+struct Rendez
+{
+	QLock	*l;
+	QLp	*head;
+	QLp	*tail;
+} Rendez;
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -34,6 +42,10 @@
 extern	void	qunlock(QLock*);
 extern	int	canqlock(QLock*);

+extern	void	rsleep(Rendez*);
+extern	int	rwakeup(Rendez*);
+extern	int	rwakeupall(Rendez*);
+
 #ifdef __cplusplus
 }
 #endif
diff -r bfe93397b157 sys/include/ape/signal.h
--- a/sys/include/ape/signal.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/signal.h	Fri Feb 12 10:59:32 2021 -0700
@@ -37,11 +37,12 @@
 #define SIGTSTP	18	/* interactive stop */
 #define SIGTTIN	19	/* read from ctl tty by member of background */
 #define SIGTTOU	20	/* write to ctl tty by member of background */
-#define SIGVTALRM 21 /* virtual alarm clock */
-#define SIGPROF 22  /* profiling alarm clock */
+#define SIGWINCH 21	/* window size changes */
+#define SIGVTALRM 22 /* virtual alarm clock */
+#define SIGPROF 23  /* profiling alarm clock */

 #ifdef _BSD_EXTENSION
-#define NSIG 23
+#define NSIG 24
 #endif

 #ifdef __cplusplus
@@ -65,6 +66,10 @@
 };
 /* values for sa_flags */
 #define SA_NOCLDSTOP	1
+#define SA_ONSTACK	2
+#define SA_RESETHAND	3
+#define SA_RESTART	4
+#define SA_RESTORER	5

 /* first argument to sigprocmask */
 #define SIG_BLOCK	1
diff -r bfe93397b157 sys/include/ape/stdint.h
--- a/sys/include/ape/stdint.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/stdint.h	Fri Feb 12 10:59:32 2021 -0700
@@ -10,6 +10,15 @@
 typedef unsigned int _uintptr_t;
 #endif

+typedef char s8;
+typedef short s16;
+typedef long s32;
+typedef long long s64;
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned long u32;
+typedef unsigned long long u64;
+
 typedef char int8_t;
 typedef short int16_t;
 typedef int int32_t;
diff -r bfe93397b157 sys/include/ape/stdlib.h
--- a/sys/include/ape/stdlib.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/stdlib.h	Fri Feb 12 10:59:32 2021 -0700
@@ -49,6 +49,10 @@
 extern size_t mbstowcs(wchar_t *, const char *, size_t);
 extern size_t wcstombs(char *, const wchar_t *, size_t);

+#ifdef _BSD_EXTENSION
+#include <bsd.h>
+#endif
+
 #ifdef _POSIX_C_SOURCE
 extern int mkstemp(char *template);
 #endif
diff -r bfe93397b157 sys/include/ape/sys/resource.h
--- a/sys/include/ape/sys/resource.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/sys/resource.h	Fri Feb 12 10:59:32 2021 -0700
@@ -5,6 +5,17 @@
     This header file is an extension to ANSI/POSIX
 #endif

+#include <sys/time.h>
+
+#pragma lib "/$M/lib/ape/libap.a"
+
+enum {
+	RUSAGE_SELF = 0,
+	RUSAGE_CHILDREN = -1
+};
+
+#define RUSAGE_SELF		RUSAGE_SELF
+#define RUSAGE_CHILDREN		RUSAGE_CHILDREN
 struct rusage {
 	struct timeval ru_utime;	/* user time used */
 	struct timeval ru_stime;	/* system time used */
@@ -26,4 +37,14 @@
 #define	ru_last		ru_nivcsw
 };

+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int getrusage(int, struct rusage *);
+
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* !__RESOURCE_H__ */
diff -r bfe93397b157 sys/include/ape/sys/socket.h
--- a/sys/include/ape/sys/socket.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/sys/socket.h	Fri Feb 12 10:59:32 2021 -0700
@@ -24,6 +24,7 @@
  */
 typedef int socklen_t;
 typedef unsigned short sa_family_t;
+typedef unsigned short in_port_t;

 /*
  * Types
@@ -178,6 +179,15 @@

 #define	MSG_MAXIOVLEN	16

+#define TCP_NODELAY	1
+#define TCP_MAXSEG	2
+
+enum {
+	SHUT_RD,		/* no more receptions */
+	SHUT_WR,		/* no more transmissions */
+	SHUT_RDWR,		/* no more receptions or transmissions */
+};
+
 extern int accept(int, void *, int *);
 extern int bind(int, void *, int);
 extern int connect(int, void *, int);
diff -r bfe93397b157 sys/include/ape/sys/stat.h
--- a/sys/include/ape/sys/stat.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/sys/stat.h	Fri Feb 12 10:59:32 2021 -0700
@@ -27,6 +27,7 @@
 #define	S__MASK		     0170000
 #ifdef _RESEARCH_SOURCE
 #define S_ISLNK(m)	(((m)&S__MASK) == 0120000)
+#define S_ISSOCK(m)	(((m)&S__MASK) == 0010000)
 #endif
 #define S_ISREG(m)	(((m)&S__MASK) == 0100000)
 #define S_ISDIR(m)	(((m)&S__MASK) == 0040000)
diff -r bfe93397b157 sys/include/ape/sys/types.h
--- a/sys/include/ape/sys/types.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/sys/types.h	Fri Feb 12 10:59:32 2021 -0700
@@ -2,6 +2,7 @@
 #define __TYPES_H

 #pragma lib "/$M/lib/ape/libap.a"
+
 typedef	unsigned short	ino_t;
 typedef	unsigned short	dev_t;
 typedef	long long		off_t;
@@ -10,6 +11,11 @@
 typedef short		gid_t;
 typedef short		nlink_t;
 typedef int		pid_t;
+typedef unsigned char	u_char;
+typedef unsigned short	u_short;
+typedef unsigned int	u_int;
+typedef unsigned long	u_long;
+typedef unsigned int	in_addr_t;

 #ifndef _SIZE_T
 #define _SIZE_T
@@ -25,6 +31,11 @@
 typedef long time_t;
 #endif

+#ifndef _CLOCKID_T
+#define _CLOCKID_T
+typedef int clockid_t;
+#endif
+
 #ifdef _BSD_EXTENSION
 #ifndef _CADDR_T
 #define _CADDR_T
diff -r bfe93397b157 sys/include/ape/syslog.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/include/ape/syslog.h	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,57 @@
+#ifndef __SYSLOG_H
+#define __SYSLOG_H
+#pragma lib "/$M/lib/ape/libap.a"
+
+enum {
+	/* facility */
+	LOG_KERN	= 0<<3,
+	LOG_USER	= 1<<3,
+	LOG_DAEMON	= 3<<3,
+	LOG_AUTH	= 4<<3,
+	LOG_SYSLOG	= 5<<3,
+	LOG_CRON	= 9<<3,
+	LOG_AUTHPRIV= 10<<3,
+	LOG_LOCAL0	= 16<<3,
+	LOG_LOCAL1	= 17<<3,
+	LOG_LOCAL2	= 18<<3,
+	LOG_LOCAL3	= 19<<3,
+	LOG_LOCAL4	= 20<<3,
+	LOG_LOCAL5	= 21<<3,
+	LOG_LOCAL6	= 22<<3,
+	LOG_LOCAL7	= 23<<3,
+};
+
+enum {
+	/* priority */
+	LOG_EMERG,
+	LOG_ALERT,
+	LOG_CRIT,
+	LOG_ERR,
+	LOG_WARNING,
+	LOG_NOTICE,
+	LOG_INFO,
+	LOG_DEBUG,
+};
+
+enum {
+	/* option */
+	LOG_PID		= 0x01,
+	LOG_CONS	= 0x02,
+	LOG_ODELAY	= 0x04,
+	LOG_NDELAY	= 0x08,
+	LOG_NOWAIT	= 0x10,
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void openlog(const char *, int, int);
+extern void syslog(int, const char *, ...);
+extern void closelog(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r bfe93397b157 sys/include/ape/time.h
--- a/sys/include/ape/time.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/time.h	Fri Feb 12 10:59:32 2021 -0700
@@ -9,6 +9,9 @@
 /* obsolsecent, but required */
 #define CLK_TCK CLOCKS_PER_SEC

+#define CLOCK_REALTIME 0
+#define CLOCK_MONOTONIC 1
+
 #ifndef _CLOCK_T
 #define _CLOCK_T
 typedef long clock_t;
@@ -17,6 +20,10 @@
 #define _TIME_T
 typedef long time_t;
 #endif
+#ifndef _CLOCKID_T
+#define _CLOCKID_T
+typedef int clockid_t;
+#endif

 struct tm {
 	int	tm_sec;
@@ -43,6 +50,8 @@
 extern struct tm *gmtime(const time_t *);
 extern struct tm *localtime(const time_t *);
 extern size_t strftime(char *, size_t, const char *, const struct tm *);
+extern int clock_gettime(clockid_t, struct timespec *);
+extern int clock_settime(clockid_t, struct timespec *);

 #ifdef _REENTRANT_SOURCE
 extern struct tm *gmtime_r(const time_t *, struct tm *);
diff -r bfe93397b157 sys/include/ape/unistd.h
--- a/sys/include/ape/unistd.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/unistd.h	Fri Feb 12 10:59:32 2021 -0700
@@ -62,6 +62,7 @@
 #define	_SC_SAVED_IDS		9	/* saved suid/sgid per process */
 #define	_SC_VERSION		10	/* this version */
 #define _SC_LOGIN_NAME_MAX	11	/* max length of a login name */
+#define _SC_PAGESIZE		12

 /* pathconf argument */
 #define _PC_LINK_MAX		1
@@ -115,6 +116,7 @@
 extern int setgid(gid_t);
 extern int getgroups(int, gid_t *);
 extern pid_t getpgrp(void);
+extern int getpgid(pid_t);
 extern int setpgid(pid_t, pid_t);
 extern pid_t setsid(void);
 #endif
@@ -130,6 +132,7 @@
 extern int access(const char *, int);
 extern long pathconf(const char *, int);
 extern long fpathconf(int, int);
+extern int fchdir(int);
 #ifdef __TYPES_H
 extern int chown(const char *, uid_t, gid_t);
 #endif
@@ -144,6 +147,9 @@
 #ifdef __TYPES_H
 extern int ftruncate(int, off_t);
 extern off_t lseek(int, off_t, int);
+
+extern ssize_t pread(int, void *, size_t, off_t);
+extern ssize_t pwrite(int, const void *, size_t, off_t);
 #endif

 /* device- and class-specific functions */
@@ -159,8 +165,12 @@
 /* berkeley specific functions */
 #ifdef _BSD_EXTENSION
 #include <bsd.h>
+
+extern int getpagesize(void);
 #endif

+extern int gethostname(char *, size_t);
+
 #ifdef __cplusplus
 }
 #endif
diff -r bfe93397b157 sys/src/ape/lib/9/mkfile
--- a/sys/src/ape/lib/9/mkfile	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/9/mkfile	Fri Feb 12 10:59:32 2021 -0700
@@ -13,6 +13,7 @@
 	getfcr.$O\
 	getfields.$O\
 	mount.$O\
+	nsec.$O\
 	rendezvous.$O\
 	rfork.$O\
 	segattach.$O\
diff -r bfe93397b157 sys/src/ape/lib/9/nsec.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/9/nsec.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,9 @@
+#include <lib9.h>
+
+extern vlong _NSEC(void);
+
+vlong
+nsec(void)
+{
+	return _NSEC();
+}
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/clock_gettime.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/ap/plan9/clock_gettime.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,33 @@
+#include <time.h>
+#include <sys/times.h>
+#include <lib9.h>
+#include <errno.h>
+
+#define NANO_IN_SEC 1000000000L
+
+int
+clock_gettime(clockid_t clock_id, struct timespec *tp)
+{
+	vlong ns;
+
+	switch(clock_id){
+	case CLOCK_REALTIME:
+	case CLOCK_MONOTONIC:
+		ns = nsec();
+		tp->tv_sec = ns/NANO_IN_SEC;
+		tp->tv_nsec = ns%NANO_IN_SEC;
+		return 0;
+	default:
+		errno = EINVAL;
+		return -1;
+	}
+}
+
+int
+clock_settime(clockid_t clock_id, struct timespec *tp)
+{
+	USED(clock_id);
+	USED(tp);
+	errno = EPERM;
+	return -1;
+}
diff -r 02e3059af5bc sys/src/ape/lib/ap/plan9/dirstat.c
--- a/sys/src/ape/lib/ap/plan9/dirstat.c	Thu Feb 11 09:37:36 2021 +0100
+++ b/sys/src/ape/lib/ap/plan9/dirstat.c	Fri Feb 12 10:59:32 2021 -0700
@@ -63,11 +63,11 @@

 	nd = DIRSIZE;
 	for(i=0; i<2; i++){	/* should work by the second try */
-		d = malloc(sizeof(Dir) + nd);
+		d = malloc(sizeof(Dir) + BIT16SZ +nd);
 		if(d == nil)
 			return nil;
 		buf = (uchar*)&d[1];
-		n = _FSTAT(fd, buf, nd);
+		n = _FSTAT(fd, buf, BIT16SZ+nd);
 		if(n < BIT16SZ){
 			free(d);
 			return nil;
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/fchdir.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/ap/plan9/fchdir.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,32 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include "lib.h"
+#include <errno.h>
+
+extern int _FD2PATH(int fd, char *buf, int nbuf);
+extern int _CHDIR(char *dirname);
+
+int
+fchdir(int fd)
+{
+	char buf[_POSIX_PATH_MAX];
+	struct stat s;
+	int n;
+
+	if(fstat(fd, &s) < 0)
+		return -1;
+	if(!S_ISDIR(s.st_mode)){
+		errno = ENOTDIR;
+		return -1;
+	}
+
+	if (_FD2PATH(fd, buf, sizeof buf) < 0){
+		_syserrno();
+		return -1;
+	}
+	n = _CHDIR(buf);
+	if(n < 0)
+		_syserrno();
+	return n;
+}
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/fork.c
--- a/sys/src/ape/lib/ap/plan9/fork.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/plan9/fork.c	Fri Feb 12 10:59:32 2021 -0700
@@ -2,18 +2,58 @@
 #include <errno.h>
 #include <unistd.h>
 #include "sys9.h"
+#include <pthread.h>
+
+enum {
+	NHANDLERS = 100
+};
+
+static void (*preparehdlr[NHANDLERS])(void);
+static void (*parenthdlr[NHANDLERS])(void);
+static void (*childhdlr[NHANDLERS])(void);
+static int nprepare;
+static int nparent;
+static int nchild;
+
+int
+pthread_atfork(void (*prepare)(void), void (*parent)(void), void
(*child)(void))
+{
+	if(prepare != NULL){
+		if(nprepare >= NHANDLERS)
+			return ENOMEM;
+		preparehdlr[nprepare++] = prepare;
+	}
+	if(parent != NULL){
+		if(nparent >= NHANDLERS)
+			return ENOMEM;
+		parenthdlr[nparent++] = parent;
+	}
+	if(child != NULL){
+		if(nchild >= NHANDLERS)
+			return ENOMEM;
+		childhdlr[nchild++] = child;
+	}
+	return 0;
+}

 pid_t
 fork(void)
 {
-	int n;
+	int n, i;

+	for(i = nprepare-1; i >= 0; i--)
+		preparehdlr[i]();
 	n = _RFORK(RFENVG|RFFDG|RFPROC);
 	if(n < 0)
 		_syserrno();
 	if(n == 0) {
 		_detachbuf();
 		_sessleader = 0;
+		for(i = 0; i < nchild; i++)
+			childhdlr[i]();
+		return 0;
 	}
+	for(i = 0; i < nparent; i++)
+		parenthdlr[i]();
 	return n;
 }
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/fsync.c
--- a/sys/src/ape/lib/ap/plan9/fsync.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/plan9/fsync.c	Fri Feb 12 10:59:32 2021 -0700
@@ -5,6 +5,6 @@
 int
 fsync(int)
 {
-	errno = EINVAL;
-	return -1;
+	/* TODO: should fsync return an error? */
+	return 0;
 }
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/getpgid.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/ap/plan9/getpgid.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,31 @@
+#include "lib.h"
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "sys9.h"
+
+int
+getpgid(pid_t pid)
+{
+	int n, f;
+	char buf[50], fname[30];
+
+	if(pid == 0)
+		pid = getpid();
+	sprintf(fname, "/proc/%d/noteid", pid);
+	f = open(fname, 0);
+	if(f < 0) {
+		errno = ESRCH;
+		return -1;
+	}
+	n = read(f, buf, sizeof(buf));
+	if(n < 0)
+		_syserrno();
+	else{
+		buf[n] = '\0';
+		n = atoi(buf);
+	}
+	close(f);
+	return n;
+}
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/getrusage.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/ap/plan9/getrusage.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,12 @@
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <string.h>
+
+int
+getrusage(int who, struct rusage *usage)
+{
+	/* dummy implementation */
+	USED(who);
+	memset(usage, 0, sizeof *usage);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/mkfile
--- a/sys/src/ape/lib/ap/plan9/mkfile	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/plan9/mkfile	Fri Feb 12 10:59:32 2021 -0700
@@ -9,6 +9,7 @@
 	_exit.$O\
 	_fdinfo.$O\
 	_getpw.$O\
 	_nap.$O\
 	9mallocz.$O\
 	9nsec.$O\
@@ -25,6 +26,7 @@
 	chroot.$O\
 	chmod.$O\
 	chown.$O\
+	clock_gettime.$O\
 	close.$O\
 	convM2D.$O\
 	convD2M.$O\
@@ -42,6 +44,7 @@
 	execv.$O\
 	execve.$O\
 	execvp.$O\
+	fchdir.$O\
 	fcntl.$O\
 	fork.$O\
 	frexp.$O\
@@ -54,11 +57,13 @@
 	getgrnam.$O\
 	getgroups.$O\
 	getlogin.$O\
+	getpgid.$O\
 	getpgrp.$O\
 	getpid.$O\
 	getppid.$O\
 	getpwnam.$O\
 	getpwuid.$O\
+	getrusage.$O\
 	getuid.$O\
 	isatty.$O\
 	kill.$O\
@@ -71,6 +76,7 @@
 	opendir.$O\
 	pause.$O\
 	pipe.$O\
+	poll.$O\
 	profile.$O\
 	qlock.$O\
 	read.$O\
@@ -87,6 +93,7 @@
 	sleep.$O\
 	sqrt.$O\
 	stat.$O\
+	syslog.$O\
 	system.$O\
 	tcgetattr.$O\
 	time.$O\
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/poll.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/ap/plan9/poll.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <poll.h>
+
+int
+poll(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+	int n;
+	fd_set rfds, wfds, efds;
+	struct timeval w, *wp;
+	struct pollfd *p, *ep;
+
+	n = -1;
+	FD_ZERO(&rfds);
+	FD_ZERO(&wfds);
+	FD_ZERO(&efds);
+
+	ep = fds + nfds;
+	for(p = fds; p < ep; p++){
+		if(p->fd < 0)
+			continue;
+		if((p->events&(POLLIN|POLLPRI|POLLOUT)) == 0)
+			continue;
+		if(p->events&POLLIN)
+			FD_SET(p->fd, &rfds);
+		if(p->events&POLLOUT)
+			FD_SET(p->fd, &wfds);
+		if(p->events&(POLLPRI))
+			FD_SET(p->fd, &efds);
+		if(p->fd > n)
+			n = p->fd;
+	}
+	wp = NULL;
+	if(timeout >= 0){
+		w.tv_sec = timeout/1000;
+		w.tv_usec = (timeout%1000)*1000;
+		wp = &w;
+	}
+	n = select(n+1, &rfds, &wfds, &efds, wp);
+	if(n < 0)
+		return -1;
+
+	/* POLLHUP means the socket is no longer connected. (FIN) */
+	/* POLLERR means the socket got an asynchronous error. (RST) */
+
+	for(p = fds; p < ep; p++){
+		p->revents = 0;
+		if(FD_ISSET(p->fd, &rfds))
+			p->revents |= POLLIN;
+		if(FD_ISSET(p->fd, &wfds))
+			p->revents |= POLLOUT;
+		if(FD_ISSET(p->fd, &efds))
+			p->revents |= POLLPRI|POLLHUP;
+	}
+	return n;
+}
diff -r 02e3059af5bc -r 5904c2b92376 sys/src/ape/lib/ap/plan9/qlock.c
--- a/sys/src/ape/lib/ap/plan9/qlock.c	Thu Feb 11 09:37:36 2021 +0100
+++ b/sys/src/ape/lib/ap/plan9/qlock.c	Thu Oct 26 02:42:26 2017 +0200
@@ -22,6 +22,9 @@
 enum
 {
 	Queuing,
+	QueuingR,
+	QueuingW,
+	Sleeping,
 };

 /* find a free shared memory location to queue ourselves in */
@@ -108,3 +112,254 @@
 	unlock(&q->lock);
 	return 0;
 }
+
+#if 0
+
+void
+rlock(RWLock *q)
+{
+	QLp *p, *mp;
+
+	lock(&q->lock);
+	if(q->writer == 0 && q->head == nil){
+		/* no writer, go for it */
+		q->readers++;
+		unlock(&q->lock);
+		return;
+	}
+
+	mp = getqlp();
+	p = q->tail;
+	if(p == 0)
+		q->head = mp;
+	else
+		p->next = mp;
+	q->tail = mp;
+	mp->next = nil;
+	mp->state = QueuingR;
+	unlock(&q->lock);
+
+	/* wait in kernel */
+	while((*_rendezvousp)(mp, (void*)1) == (void*)~0)
+		;
+	mp->inuse = 0;
+}
+
+int
+canrlock(RWLock *q)
+{
+	lock(&q->lock);
+	if (q->writer == 0 && q->head == nil) {
+		/* no writer; go for it */
+		q->readers++;
+		unlock(&q->lock);
+		return 1;
+	}
+	unlock(&q->lock);
+	return 0;
+}
+
+void
+runlock(RWLock *q)
+{
+	QLp *p;
+
+	lock(&q->lock);
+	if(q->readers <= 0)
+		abort();
+	p = q->head;
+	if(--(q->readers) > 0 || p == nil){
+		unlock(&q->lock);
+		return;
+	}
+
+	/* start waiting writer */
+	if(p->state != QueuingW)
+		abort();
+	q->head = p->next;
+	if(q->head == 0)
+		q->tail = 0;
+	q->writer = 1;
+	unlock(&q->lock);
+
+	/* wakeup waiter */
+	while((*_rendezvousp)(p, (void*)0) == (void*)~0)
+		;
+}
+
+void
+wlock(RWLock *q)
+{
+	QLp *p, *mp;
+
+	lock(&q->lock);
+	if(q->readers == 0 && q->writer == 0){
+		/* noone waiting, go for it */
+		q->writer = 1;
+		unlock(&q->lock);
+		return;
+	}
+
+	/* wait */
+	p = q->tail;
+	mp = getqlp();
+	if(p == nil)
+		q->head = mp;
+	else
+		p->next = mp;
+	q->tail = mp;
+	mp->next = nil;
+	mp->state = QueuingW;
+	unlock(&q->lock);
+
+	/* wait in kernel */
+	while((*_rendezvousp)(mp, (void*)1) == (void*)~0)
+		;
+	mp->inuse = 0;
+}
+
+int
+canwlock(RWLock *q)
+{
+	lock(&q->lock);
+	if (q->readers == 0 && q->writer == 0) {
+		/* no one waiting; go for it */
+		q->writer = 1;
+		unlock(&q->lock);
+		return 1;
+	}
+	unlock(&q->lock);
+	return 0;
+}
+
+void
+wunlock(RWLock *q)
+{
+	QLp *p;
+
+	lock(&q->lock);
+	if(q->writer == 0)
+		abort();
+	p = q->head;
+	if(p == nil){
+		q->writer = 0;
+		unlock(&q->lock);
+		return;
+	}
+	if(p->state == QueuingW){
+		/* start waiting writer */
+		q->head = p->next;
+		if(q->head == nil)
+			q->tail = nil;
+		unlock(&q->lock);
+		while((*_rendezvousp)(p, (void*)0) == (void*)~0)
+			;
+		return;
+	}
+
+	if(p->state != QueuingR)
+		abort();
+
+	/* wake waiting readers */
+	while(q->head != nil && q->head->state == QueuingR){
+		p = q->head;
+		q->head = p->next;
+		q->readers++;
+		while((*_rendezvousp)(p, (void*)0) == (void*)~0)
+			;
+	}
+	if(q->head == nil)
+		q->tail = nil;
+	q->writer = 0;
+	unlock(&q->lock);
+}
+
+void
+rsleep(Rendez *r)
+{
+	QLp *t, *me;
+
+	if(!r->l)
+		abort();
+	lock(&r->l->lock);
+	/* we should hold the qlock */
+	if(!r->l->locked)
+		abort();
+
+	/* add ourselves to the wait list */
+	me = getqlp();
+	me->state = Sleeping;
+	if(r->head == nil)
+		r->head = me;
+	else
+		r->tail->next = me;
+	me->next = nil;
+	r->tail = me;
+
+	/* pass the qlock to the next guy */
+	t = r->l->head;
+	if(t){
+		r->l->head = t->next;
+		if(r->l->head == nil)
+			r->l->tail = nil;
+		unlock(&r->l->lock);
+		while((*_rendezvousp)(t, (void*)0x12345) == (void*)~0)
+			;
+	}else{
+		r->l->locked = 0;
+		unlock(&r->l->lock);
+	}
+
+	/* wait for a wakeup */
+	while((*_rendezvousp)(me, (void*)1) == (void*)~0)
+		;
+	me->inuse = 0;
+}
+
+int
+rwakeup(Rendez *r)
+{
+	QLp *t;
+
+	/*
+	 * take off wait and put on front of queue
+	 * put on front so guys that have been waiting will not get starved
+	 */
+
+	if(!r->l)
+		abort();
+	lock(&r->l->lock);
+	if(!r->l->locked)
+		abort();
+
+	t = r->head;
+	if(t == nil){
+		unlock(&r->l->lock);
+		return 0;
+	}
+
+	r->head = t->next;
+	if(r->head == nil)
+		r->tail = nil;
+
+	t->next = r->l->head;
+	r->l->head = t;
+	if(r->l->tail == nil)
+		r->l->tail = t;
+
+	t->state = Queuing;
+	unlock(&r->l->lock);
+	return 1;
+}
+
+int
+rwakeupall(Rendez *r)
+{
+	int i;
+
+	for(i=0; rwakeup(r); i++)
+		;
+	return i;
+}
+
+#endif
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/read.c
--- a/sys/src/ape/lib/ap/plan9/read.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/plan9/read.c	Fri Feb 12 10:59:32 2021 -0700
@@ -1,4 +1,5 @@
 #include <errno.h>
+#include <inttypes.h>
 #include <unistd.h>
 #include <string.h>
 #include "lib.h"
@@ -7,7 +8,7 @@
 #include <stdio.h>

 ssize_t
-read(int d, void *buf, size_t nbytes)
+pread(int d, void *buf, size_t nbytes, off_t offset)
 {
 	int n, noblock, isbuf;
 	Fdinfo *f;
@@ -38,9 +39,15 @@
 		}
 		n = _readbuf(d, buf, nbytes, noblock);
 	}else{
-		n = _READ(d, buf, nbytes);
+		n = _PREAD(d, buf, nbytes, offset);
 		if(n < 0)
 			_syserrno();
 	}
 	return n;
 }
+
+ssize_t
+read(int d, void *buf, size_t nbytes)
+{
+	return pread(d, buf, nbytes, -1LL);
+}
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/syslog.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/ap/plan9/syslog.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,104 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <syslog.h>
+
+static struct {
+	const char *ident;
+	char hostname[128];
+	int option;
+	int facility;
+	int fd;
+	int consfd;
+} flags = {
+	.fd = -1,
+	.consfd = -1,
+};
+
+void
+openlog(const char *ident, int option, int facility)
+{
+	char buf[1024];
+	int fd;
+
+	closelog();
+	if(gethostname(flags.hostname, sizeof(flags.hostname)) < 0)
+		return;
+	if(ident){
+		snprintf(buf, sizeof(buf), "/sys/log/%s", ident);
+		fd = open(buf, O_WRONLY);
+		if(fd < 0)
+			return;
+		if(fcntl(fd, F_SETFD, FD_CLOEXEC) < 0){
+			close(fd);
+			return;
+		}
+		flags.fd = fd;
+	}
+	if(option&LOG_CONS){
+		fd = open("/dev/cons", O_WRONLY);
+		if(fd < 0){
+			closelog();
+			return;
+		}
+		if(fcntl(fd, F_SETFD, FD_CLOEXEC) < 0){
+			closelog();
+			return;
+		}
+		flags.consfd = fd;
+	}
+	flags.ident = ident;
+	flags.option = option;
+	flags.facility = facility;
+}
+
+void
+syslog(int priority, const char *format, ...)
+{
+	char buf[128], *s, *p;
+	time_t t;
+	int n;
+	va_list arg;
+
+	/* syslog => Mar 10 01:45:50 $hostname $prog[$pid]: $msg */
+	/* plan9  => $hostname Mar 10 01:45:50 $msg */
+
+	USED(priority);
+
+	/* TODO: lock */
+	t = time(NULL);
+	s = ctime(&t);
+	p = buf + snprintf(buf, sizeof(buf)-1, "%s ", flags.hostname);
+	strncpy(p, s+4, 15);
+	p += 15;
+	*p++ = ' ';
+	va_start(arg, format);
+	p += vsnprintf(p, buf+sizeof(buf)-p-1, format, arg);
+	va_end(arg);
+	*p++ = '\n';
+	n = p - buf;
+	if(flags.fd >= 0){
+		lseek(flags.fd, 0, 2);
+		write(flags.fd, buf, n);
+	}
+	if(flags.consfd >= 0)
+		write(flags.consfd, buf, n);
+}
+
+void
+closelog(void)
+{
+	flags.ident = NULL;
+	flags.hostname[0] = '\0';
+	flags.option = 0;
+	flags.facility = 0;
+	if(flags.fd >= 0)
+		close(flags.fd);
+	flags.fd = -1;
+	if(flags.consfd >= 0)
+		close(flags.consfd);
+	flags.consfd = -1;
+}
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/write.c
--- a/sys/src/ape/lib/ap/plan9/write.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/plan9/write.c	Fri Feb 12 10:59:32 2021 -0700
@@ -1,10 +1,11 @@
 #include <errno.h>
+#include <inttypes.h>
 #include <unistd.h>
 #include "lib.h"
 #include "sys9.h"

 ssize_t
-write(int d, const void *buf, size_t nbytes)
+pwrite(int d, const void *buf, size_t nbytes, off_t offset)
 {
 	int n;

@@ -14,8 +15,14 @@
 	}
 	if(_fdinfo[d].oflags&O_APPEND)
 		_SEEK(d, 0, 2);
-	n = _WRITE(d, buf, nbytes);
+	n = _PWRITE(d, buf, nbytes, offset);
 	if(n < 0)
 		_syserrno();
 	return n;
 }
+
+ssize_t
+write(int d, const void *buf, size_t nbytes)
+{
+	return pwrite(d, buf, nbytes, -1LL);
+}
diff -r bfe93397b157 sys/src/ape/lib/ap/posix/basename.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/ap/posix/basename.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,21 @@
+#include <stdlib.h>
+#include <string.h>
+
+char *
+basename(char *path)
+{
+	int n;
+	char *p;
+
+	if(path == NULL || path[0] == '\0')
+		return ".";
+	n = strlen(path);
+	if(n == 1 && path[0] == '/' && path[1] == '\0')
+		return "/";
+	while(path[n-1] == '/')
+		path[--n] = '\0';
+	p = strrchr(path, '/');
+	if(p == NULL)
+		return path;
+	return p+1;
+}
diff -r bfe93397b157 sys/src/ape/lib/ap/posix/dirname.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/ap/posix/dirname.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,25 @@
+#include <stdlib.h>
+#include <string.h>
+
+char *
+dirname(char *path)
+{
+	int n;
+	char *p;
+
+	if(path == NULL || path[0] == '\0')
+		return ".";
+	n = strlen(path);
+	if(n == 1 && path[0] == '/' && path[1] == '\0')
+		return "/";
+	while(path[n-1] == '/')
+		path[--n] = '\0';
+	p = strrchr(path, '/');
+	if(p == NULL)
+		return ".";
+	while(*p == '/')
+		*p-- = '\0';
+	if(path[0] == '\0')
+		return "/";
+	return path;
+}
diff -r 02e3059af5bc sys/src/ape/lib/ap/posix/getpagesize.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/ap/posix/getpagesize.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,7 @@
+#include <unistd.h>
+
+int
+getpagesize(void)
+{
+	return sysconf(_SC_PAGESIZE);
+}
diff -r bfe93397b157 sys/src/ape/lib/ap/posix/mkfile
--- a/sys/src/ape/lib/ap/posix/mkfile	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/posix/mkfile	Fri Feb 12 10:59:32 2021 -0700
@@ -2,7 +2,10 @@
 <$APE/config
 LIB=/$objtype/lib/ape/libap.a
 OFILES=\
+	basename.$O\
+	dirname.$O\
 	getgrent.$O\
+	getpagesize.$O\
 	getpwent.$O\
 	locale.$O\
 	mkfifo.$O\
diff -r bfe93397b157 sys/src/ape/lib/ap/posix/sysconf.c
--- a/sys/src/ape/lib/ap/posix/sysconf.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/posix/sysconf.c	Fri Feb 12 10:59:32 2021 -0700
@@ -36,6 +36,8 @@
 		return _POSIX_VERSION;
 	case _SC_LOGIN_NAME_MAX:
 		return L_cuserid;
+	case _SC_PAGESIZE:
+		return 4096;
 	}
 	errno = EINVAL;
 	return -1;
diff -r bfe93397b157 sys/src/ape/lib/ap/stdio/_IO_putc.c
--- a/sys/src/ape/lib/ap/stdio/_IO_putc.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/stdio/_IO_putc.c	Fri Feb 12 10:59:32 2021 -0700
@@ -51,8 +51,13 @@
 				f->bufl+=BUFSIZ;
 				f->rp=t+f->bufl;
 			}else{
-				f->state=ERR;
-				return EOF;
+				/*
+				 * [v]snprintf should return number of characters
+				 * which would have written if enough space had enough available.
+				 * however sprintf is not.
+				 */
+				f->state=WR;
+				return c&0xff;
 			}
 		}
 		*f->wp++=c;
diff -r bfe93397b157 sys/src/ape/lib/ap/stdio/sclose.c
--- a/sys/src/ape/lib/ap/stdio/sclose.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/stdio/sclose.c	Fri Feb 12 10:59:32 2021 -0700
@@ -29,6 +29,8 @@
 					goto Error;
 				f->buf=t;
 				f->wp=t+f->bufl;
+			} else if(f->buf==NULL){
+				return NULL;
 			} else {
 				if(f->wp > f->buf)
 					*(f->wp-1) = '\0';
diff -r bfe93397b157 sys/src/ape/lib/ap/stdio/strerror.c
--- a/sys/src/ape/lib/ap/stdio/strerror.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/stdio/strerror.c	Fri Feb 12 10:59:32 2021 -0700
@@ -77,7 +77,17 @@

 	/* These added in 1003.1b-1993 */
 	"Operation canceled",
-	"Operation in progress"
+	"Operation in progress",
+
+	/* from research unix */
+	"Text file is busy",			/* ETXTBSY */
+
+	/* Added in more recent 1003.x versions */
+	"Operation already in progress",	/* EALREADY */
+	"Connection reset by peer",		/* ECONNRESET */
+
+	"Value too large for defined data type", /* EOVERFLOW */
+	"Too many symbolic links encountered",	/* ELOOP */
 };
 #define	_IO_nerr	(sizeof sys_errlist/sizeof sys_errlist[0])
 int sys_nerr = _IO_nerr;
diff -r bfe93397b157 sys/src/ape/lib/ap/stdio/vfprintf.c
--- a/sys/src/ape/lib/ap/stdio/vfprintf.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/stdio/vfprintf.c	Fri Feb 12 10:59:32 2021 -0700
@@ -215,7 +215,7 @@
 		}
 		return -1;
 	}
-	return nprint;
+	return ferror(f) ? -1 : nprint;
 }

 static int
diff -r bfe93397b157 sys/src/ape/lib/bsd/arc4random.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/bsd/arc4random.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,30 @@
+#include <u.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <libsec.h>
+
+void
+arc4random_buf(void *buf, size_t nbytes)
+{
+	genrandom(buf, nbytes);
+}
+
+unsigned int
+arc4random(void)
+{
+	uint v;
+
+	arc4random_buf(&v, sizeof v);
+	return v;
+}
+
+int
+getentropy(void *buf, size_t len)
+{
+	if (len > 256) {
+		errno = EIO;
+		return -1;
+	}
+	genrandom(buf, len);
+	return 0;
+}
diff -r 8b4cfdf43705 sys/src/ape/lib/bsd/gethostname.c
--- a/sys/src/ape/lib/bsd/gethostname.c	Tue Feb 16 22:04:50 2021 +0100
+++ b/sys/src/ape/lib/bsd/gethostname.c	Tue Feb 16 21:43:40 2021 -0700
@@ -6,7 +6,7 @@
 #include <string.h>

 int
-gethostname(char *name, int namelen)
+gethostname(char *name, size_t namelen)
 {
 	int n, fd;
 	char buf[128];
diff -r bfe93397b157 sys/src/ape/lib/bsd/getprogname.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/bsd/getprogname.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,7 @@
+#include <lib9.h>
+
+const char *
+getprogname(void)
+{
+	return argv0;
+}
diff -r bfe93397b157 sys/src/ape/lib/bsd/mkfile
--- a/sys/src/ape/lib/bsd/mkfile	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/bsd/mkfile	Fri Feb 12 10:59:32 2021 -0700
@@ -4,6 +4,7 @@
 LIB=/$objtype/lib/ape/libbsd.a
 OFILES=\
 	accept.$O\
+	arc4random.$O\
 	bcopy.$O\
 	bind.$O\
 	connect.$O\
@@ -19,6 +20,7 @@
 	getopt.$O\
 	getpeername.$O\
 	getprotobyname.$O\
+	getprogname.$O\
 	getservbyaddr.$O\
 	getservbyname.$O\
 	getsockname.$O\
@@ -41,6 +43,7 @@
 	send.$O\
 	sendto.$O\
 	setlinebuf.$O\
+	setprogname.$O\
 	shutdown.$O\
 	_sock_ingetaddr.$O\
 	_sock_ipattr.$O\
diff -r bfe93397b157 sys/src/ape/lib/bsd/setprogname.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/bsd/setprogname.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,7 @@
+#include <lib9.h>
+
+void
+setprogname(const char *progname)
+{
+	argv0 = (char *)progname;
+}
diff -r bfe93397b157 sys/src/ape/lib/bsd/socket.c
--- a/sys/src/ape/lib/bsd/socket.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/bsd/socket.c	Fri Feb 12 10:59:32 2021 -0700
@@ -180,9 +180,23 @@
 /*
  * probably should do better than this
  */
-int getsockopt(int, int, int, void *, int *)
+int getsockopt(int fd, int level, int opt, void *v, int *len)
 {
-	return -1;
+	// should we check what fd is socket?
+	USED(fd, len);
+
+	if(level != SOL_SOCKET){
+		errno = ENOPROTOOPT;
+		return -1;
+	}
+	switch(opt){
+	case SO_ERROR:
+		*(int *)v = 0;
+		return 0;
+	default:
+		errno = EINVAL;
+		return -1;
+	}
 }

 int setsockopt(int, int, int, void *, int)
diff -r bfe93397b157 sys/src/ape/lib/pthread/_pthread.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/_pthread.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,83 @@
+#include <pthread.h>
+#include <string.h>
+#include "lib.h"
+
+static Lock privlock;
+static Thread privileges[PTHREAD_THREADS_MAX];
+
+Thread *
+_pthreadalloc(void)
+{
+	Thread *p, *ep;
+
+	ep = privileges+nelem(privileges);
+	lock(&privlock);
+	for(p = privileges; p < ep; p++){
+		if(!p->inuse){
+			memset(p, 0, sizeof(*p));
+			p->inuse = 1;
+			unlock(&privlock);
+			return p;
+		}
+	}
+	unlock(&privlock);
+	return NULL;
+}
+
+void
+_pthreadsetpid(Thread *priv, pthread_t pid)
+{
+	lock(&privlock);
+	priv->pid = pid;
+	unlock(&privlock);
+}
+
+Thread *
+_pthreadnew(pthread_t pid)
+{
+	Thread *p, *ep, *freep = NULL;
+
+	ep = privileges+nelem(privileges);
+	lock(&privlock);
+	for(p = privileges; p < ep; p++){
+		if(p->inuse && p->pid == pid){
+			unlock(&privlock);
+			return p;
+		}
+		if(freep == NULL && !p->inuse)
+			freep = p;
+	}
+	if(freep == NULL){
+		unlock(&privlock);
+		return NULL;
+	}
+	memset(freep, 0, sizeof(*freep));
+	freep->inuse = 1;
+	freep->pid = pid;
+	unlock(&privlock);
+	return freep;
+}
+
+Thread *
+_pthreadget(pthread_t pid)
+{
+	Thread *p, *ep;
+
+	ep = privileges+nelem(privileges);
+	lock(&privlock);
+	for(p = privileges; p < ep; p++)
+		if(p->inuse && p->pid == pid){
+			unlock(&privlock);
+			return p;
+		}
+	unlock(&privlock);
+	return NULL;
+}
+
+void
+_pthreadfree(Thread *priv)
+{
+	lock(&privlock);
+	priv->inuse = 0;
+	unlock(&privlock);
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/cond_broadcast.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/cond_broadcast.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,10 @@
+#include <pthread.h>
+
+int
+pthread_cond_broadcast(pthread_cond_t *cond)
+{
+	qlock(cond->r.l);
+	rwakeupall(&cond->r);
+	qunlock(cond->r.l);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/cond_destroy.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/cond_destroy.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,8 @@
+#include <pthread.h>
+
+int
+pthread_cond_destroy(pthread_cond_t *)
+{
+	/* TODO: should we check cond is busy? */
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/cond_init.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/cond_init.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,10 @@
+#include <pthread.h>
+#include <string.h>
+
+int
+pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *)
+{
+	memset(cond, 0, sizeof(*cond));
+	cond->r.l = &cond->l;
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/cond_signal.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/cond_signal.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,10 @@
+#include <pthread.h>
+
+int
+pthread_cond_signal(pthread_cond_t *cond)
+{
+	qlock(cond->r.l);
+	rwakeup(&cond->r);
+	qunlock(cond->r.l);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/cond_wait.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/cond_wait.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,12 @@
+#include <pthread.h>
+
+int
+pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
+{
+	pthread_mutex_unlock(mutex);
+	qlock(cond->r.l);
+	rsleep(&cond->r);
+	qunlock(cond->r.l);
+	pthread_mutex_lock(mutex);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/create.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/create.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,37 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "lib.h"
+
+extern int	_RFORK(int);
+extern int	_RENDEZVOUS(unsigned long, unsigned long);
+
+int
+pthread_create(pthread_t *t, pthread_attr_t *attr, void *(*f)(void*),
void *arg)
+{
+	void *p;
+	int pid;
+	Thread *priv;
+	unsigned long tag;
+
+	if(attr != NULL)
+		return EINVAL;
+	priv = _pthreadalloc();
+	if(priv == NULL)
+		return EAGAIN;
+	tag = (unsigned long)priv;
+	pid = _RFORK(RFFDG|RFPROC|RFMEM);
+	switch(pid){
+	case -1:
+		_syserrno();
+		unlock(&priv->l);
+		_pthreadfree(priv);
+		return errno;
+	case 0:
+		_RENDEZVOUS(tag, 0);
+		p = f(arg);
+		pthread_exit(p);
+		abort(); /* can't reach here */
+	}
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/equal.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/equal.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,7 @@
+#include <pthread.h>
+
+int
+pthread_equal(pthread_t t1, pthread_t t2)
+{
+	return t1 == t2;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/exit.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/exit.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,21 @@
+#include <pthread.h>
+#include <assert.h>
+#include "lib.h"
+
+extern void	_EXITS(char *);
+
+void
+pthread_exit(void *retval)
+{
+	Thread *priv;
+	pthread_t pid;
+
+	pid = pthread_self();
+	priv = _pthreadget(pid);
+	assert(priv != NULL);
+	lock(&priv->l);
+	priv->exited = 1;
+	priv->ret = retval;
+	unlock(&priv->l);
+	_EXITS(0);
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/getspecific.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/getspecific.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,21 @@
+#include <pthread.h>
+#include "lib.h"
+
+void *
+pthread_getspecific(pthread_key_t key)
+{
+	int i;
+	pthread_t pid;
+	const void *p;
+
+	pid = pthread_self();
+	lock(&key.l);
+	for(i = 0; i < key.n; i++)
+		if(key.arenas[i].pid == pid){
+			p = key.arenas[i].p;
+			unlock(&key.l);
+			return (void *)p;
+		}
+	unlock(&key.l);
+	return NULL;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/join.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/join.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,100 @@
+#define _RESEARCH_SOURCE
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <libv.h>
+#include "lib.h"
+
+typedef
+struct Waitmsg
+{
+	int	pid;	/* of loved one */
+	ulong	time[3];	/* of loved one & descendants */
+	char	*msg;
+} Waitmsg;
+
+extern int	_AWAIT(char*, int);
+
+static Waitmsg*
+_wait(void)
+{
+	int n, l;
+	char buf[512], *fld[5];
+	Waitmsg *w;
+
+	n = _AWAIT(buf, sizeof buf-1);
+	if(n < 0){
+		_syserrno();
+		return nil;
+	}
+	buf[n] = '\0';
+	if(getfields(buf, fld, nelem(fld)) != nelem(fld)){
+		errno = ENOBUFS;
+		return nil;
+	}
+	l = strlen(fld[4])+1;
+	w = malloc(sizeof(Waitmsg)+l);
+	if(w == nil)
+		return nil;
+	w->pid = atoi(fld[0]);
+	w->time[0] = atoi(fld[1]);
+	w->time[1] = atoi(fld[2]);
+	w->time[2] = atoi(fld[3]);
+	w->msg = (char*)&w[1];
+	memmove(w->msg, fld[4], l);
+	return w;
+}
+
+static void
+emitexits(void **ret, Thread *priv)
+{
+	if(ret == NULL){
+		return;
+	}
+	*ret = priv->ret;
+}
+
+int
+pthread_join(pthread_t t, void **ret)
+{
+	static Lock l;
+	Thread *priv;
+	int pid;
+	Waitmsg *msg;
+
+	lock(&l);
+	priv = _pthreadget(t);
+	if(priv == NULL){
+		unlock(&l);
+		return EINVAL;
+	}
+	lock(&priv->l);
+	if(priv->exited){
+		emitexits(ret, priv);
+		unlock(&priv->l);
+		_pthreadfree(priv);
+		unlock(&l);
+		return 0;
+	}
+	unlock(&priv->l);
+
+	while((msg=_wait()) != NULL){
+		pid = msg->pid;
+		free(msg);
+		if(pid == t)
+			break;
+	}
+	lock(&priv->l);
+	if(priv->exited){
+		emitexits(ret, priv);
+		unlock(&priv->l);
+		_pthreadfree(priv);
+		unlock(&l);
+		return 0;
+	}
+	unlock(&priv->l);
+	unlock(&l);
+	return ESRCH;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/key_create.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/key_create.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,20 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "lib.h"
+
+int
+pthread_key_create(pthread_key_t *key, void (*destr_func)(void*))
+{
+	if(destr_func)
+		return EINVAL; /* don't implement yet */
+	memset(key, 0, sizeof(*key));
+	key->destroy = destr_func;
+	key->n = 32;
+	key->arenas = malloc(sizeof(*key->arenas)*key->n);
+	if(key->arenas == NULL)
+		return ENOMEM;
+	memset(key->arenas, 0, sizeof(*key->arenas)*key->n);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/key_delete.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/key_delete.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,10 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include "lib.h"
+
+int
+pthread_key_delete(pthread_key_t key)
+{
+	free(key.arenas);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/lib.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/lib.h	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,48 @@
+#include <signal.h>
+
+#define nelem(a)	(sizeof(a)/sizeof((a)[0]))
+
+/* rfork */
+enum
+{
+	RFNAMEG		= (1<<0),
+	RFENVG		= (1<<1),
+	RFFDG		= (1<<2),
+	RFNOTEG		= (1<<3),
+	RFPROC		= (1<<4),
+	RFMEM		= (1<<5),
+	RFNOWAIT	= (1<<6),
+	RFCNAMEG	= (1<<10),
+	RFCENVG		= (1<<11),
+	RFCFDG		= (1<<12),
+	RFREND		= (1<<13),
+	RFNOMNT		= (1<<14)
+};
+
+typedef struct Thread Thread;
+struct Thread {
+	int		inuse;
+	pid_t		pid;
+
+	Lock		l;
+	int		exited;
+	void		*ret;
+	sigset_t	sigset;
+	int		state;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void	_syserrno(void);
+
+extern Thread*	_pthreadalloc(void);
+extern void	_pthreadsetpid(Thread*, pthread_t);
+extern Thread*	_pthreadnew(pthread_t);
+extern Thread*	_pthreadget(pthread_t);
+extern void	_pthreadfree(Thread*);
+
+#ifdef __cplusplus
+}
+#endif
diff -r bfe93397b157 sys/src/ape/lib/pthread/mkfile
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/mkfile	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,36 @@
+APE=/sys/src/ape
+<$APE/config
+LIB=/$objtype/lib/ape/libpthread.a
+OFILES=\
+	_pthread.$O\
+	cond_broadcast.$O\
+	cond_destroy.$O\
+	cond_init.$O\
+	cond_signal.$O\
+	cond_wait.$O\
+	create.$O\
+	equal.$O\
+	exit.$O\
+	getspecific.$O\
+	join.$O\
+	key_create.$O\
+	key_delete.$O\
+	mutexattr_destroy.$O\
+	mutexattr_init.$O\
+	mutexattr_settype.$O\
+	mutex_destroy.$O\
+	mutex_init.$O\
+	mutex_lock.$O\
+	mutex_trylock.$O\
+	mutex_unlock.$O\
+	once.$O\
+	self.$O\
+	setcancelstate.$O\
+	setspecific.$O\
+	sigmask.$O\
+
+HFILES=lib.h
+
+</sys/src/cmd/mksyslib
+
+CFLAGS=-c -D_POSIX_SOURCE -FTVw -D_SUSV2_SOURCE -D_PLAN9_SOURCE
diff -r bfe93397b157 sys/src/ape/lib/pthread/mutex_destroy.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/mutex_destroy.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,8 @@
+#include <pthread.h>
+
+int
+pthread_mutex_destroy(pthread_mutex_t *)
+{
+	/* TODO: should we check mutex is busy? */
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/mutex_init.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/mutex_init.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,11 @@
+#include <pthread.h>
+#include <string.h>
+
+int
+pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
+{
+	memset(mutex, 0, sizeof(*mutex));
+	if(attr)
+		mutex->attr = *attr;
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/mutex_lock.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/mutex_lock.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,23 @@
+#include <pthread.h>
+
+int
+pthread_mutex_lock(pthread_mutex_t *mutex)
+{
+	pthread_t pid;
+
+	pid = pthread_self();
+	lock(&mutex->mu);
+	if(mutex->attr == PTHREAD_MUTEX_RECURSIVE && mutex->pid == pid){
+		mutex->ref++;
+		unlock(&mutex->mu);
+		return 0;
+	}
+	unlock(&mutex->mu);
+
+	qlock(&mutex->l);
+	lock(&mutex->mu);
+	mutex->pid = pid;
+	mutex->ref++;
+	unlock(&mutex->mu);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/mutex_trylock.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/mutex_trylock.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,25 @@
+#include <pthread.h>
+#include <errno.h>
+
+int
+pthread_mutex_trylock(pthread_mutex_t *mutex)
+{
+	pthread_t pid;
+
+	pid = pthread_self();
+	lock(&mutex->mu);
+	if(mutex->attr == PTHREAD_MUTEX_RECURSIVE && mutex->pid == pid){
+		mutex->ref++;
+		unlock(&mutex->mu);
+		return 0;
+	}
+	unlock(&mutex->mu);
+
+	if(!canqlock(&mutex->l))
+		return EBUSY;
+	lock(&mutex->mu);
+	mutex->pid = pid;
+	mutex->ref++;
+	unlock(&mutex->mu);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/mutex_unlock.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/mutex_unlock.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,29 @@
+#include <pthread.h>
+#include <errno.h>
+
+int
+pthread_mutex_unlock(pthread_mutex_t *mutex)
+{
+	pthread_t pid;
+
+	pid = pthread_self();
+	lock(&mutex->mu);
+	if(mutex->pid != pid){
+		unlock(&mutex->mu);
+		return EPERM;
+	}
+	if(mutex->attr == PTHREAD_MUTEX_RECURSIVE){
+		mutex->ref--;
+		if(mutex->ref <= 0){
+			mutex->pid = 0;
+			mutex->ref = 0;
+			qunlock(&mutex->l);
+		}
+		unlock(&mutex->mu);
+		return 0;
+	}
+	mutex->ref--;
+	qunlock(&mutex->l);
+	unlock(&mutex->mu);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/mutexattr_destroy.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/mutexattr_destroy.c	Fri Feb 12 10:59:32 2021
-0700
@@ -0,0 +1,7 @@
+#include <pthread.h>
+
+int
+pthread_mutexattr_destroy(pthread_mutexattr_t*)
+{
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/mutexattr_init.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/mutexattr_init.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,8 @@
+#include <pthread.h>
+
+int
+pthread_mutexattr_init(pthread_mutexattr_t *attr)
+{
+	*attr = 0;
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/mutexattr_settype.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/mutexattr_settype.c	Fri Feb 12 10:59:32 2021
-0700
@@ -0,0 +1,8 @@
+#include <pthread.h>
+
+int
+pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind)
+{
+	*attr = kind;
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/once.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/once.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,13 @@
+#include <pthread.h>
+
+int
+pthread_once(pthread_once_t *once_control, void (*init_routine) (void))
+{
+	lock(&once_control->l);
+	if(once_control->once == 0){
+		init_routine();
+		once_control->once++;
+	}
+	unlock(&once_control->l);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/self.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/self.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,8 @@
+#include <pthread.h>
+#include <unistd.h>
+
+pthread_t
+pthread_self(void)
+{
+	return getpid();
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/setcancelstate.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/setcancelstate.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,21 @@
+#include <pthread.h>
+#include <errno.h>
+#include "lib.h"
+
+int
+pthread_setcancelstate(int state, int *oldstate)
+{
+	Thread *priv;
+	pthread_t pid;
+
+	pid = pthread_self();
+	priv = _pthreadnew(pid);
+	if(priv == NULL)
+		return ENOMEM;
+	lock(&priv->l);
+	if(oldstate)
+		*oldstate = priv->state;
+	priv->state = state;
+	unlock(&priv->l);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/setspecific.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/setspecific.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,30 @@
+#include <pthread.h>
+#include <errno.h>
+#include "lib.h"
+
+int
+pthread_setspecific(pthread_key_t key, const void *p)
+{
+	int i;
+	pthread_t pid;
+
+	pid = pthread_self();
+	lock(&key.l);
+	/* exactly match */
+	for(i = 0; i < key.n; i++)
+		if(key.arenas[i].pid == pid){
+			key.arenas[i].p = p;
+			unlock(&key.l);
+			return 0;
+		}
+	/* unused */
+	for(i = 0; i < key.n; i++)
+		if(key.arenas[i].pid == 0){
+			key.arenas[i].pid = pid;
+			key.arenas[i].p = p;
+			unlock(&key.l);
+			return 0;
+		}
+	unlock(&key.l);
+	return ENOMEM;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/sigmask.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/sigmask.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,21 @@
+#include <pthread.h>
+#include <errno.h>
+#include "lib.h"
+
+int
+pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset)
+{
+	Thread *priv;
+	pthread_t pid;
+
+	pid = pthread_self();
+	priv = _pthreadnew(pid);
+	if(priv == NULL)
+		return ENOMEM;
+	lock(&priv->l);
+	if(oldset)
+		*oldset = priv->sigset;
+	priv->sigset = *set;
+	unlock(&priv->l);
+	return 0;
+}
diff -r bfe93397b157 sys/src/cmd/cpp/cpp.c
--- a/sys/src/cmd/cpp/cpp.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/cmd/cpp/cpp.c	Fri Feb 12 10:59:32 2021 -0700
@@ -238,7 +238,12 @@
 		break;

 	case KINCLUDE:
-		doinclude(trp);
+		doinclude(trp, 0);
+		trp->lp = trp->bp;
+		return;
+
+	case KINCLUDE_NEXT:
+		doinclude(trp, 1);
 		trp->lp = trp->bp;
 		return;

diff -r bfe93397b157 sys/src/cmd/cpp/cpp.h
--- a/sys/src/cmd/cpp/cpp.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/cmd/cpp/cpp.h	Fri Feb 12 10:59:32 2021 -0700
@@ -19,8 +19,8 @@
 		ASRSH, ASOR, ASAND, ELLIPS,
 		DSHARP1, NAME1, DEFINED, UMINUS, MAXTOK};

-enum kwtype { KIF, KIFDEF, KIFNDEF, KELIF, KELSE, KENDIF, KINCLUDE, KDEFINE,
-		KUNDEF, KLINE, KERROR, KWARNING, KPRAGMA, KDEFINED,
+enum kwtype { KIF, KIFDEF, KIFNDEF, KELIF, KELSE, KENDIF, KINCLUDE,
KINCLUDE_NEXT,
+		KDEFINE, KUNDEF, KLINE, KERROR, KWARNING, KPRAGMA, KDEFINED,
 		KLINENO, KFILE, KDATE, KTIME, KSTDC, KEVAL };

 #define	ISDEFINED	01	/* has #defined value */
@@ -60,6 +60,7 @@
 	int	fd;		/* input source */
 	int	ifdepth;	/* conditional nesting in include */
 	struct	source *next;	/* stack for #include */
+	int	pos;		/* next position for #include_next */
 } Source;

 typedef struct nlist {
@@ -105,7 +106,7 @@
 void	control(Tokenrow *);
 void	dodefine(Tokenrow *);
 void	doadefine(Tokenrow *, int);
-void	doinclude(Tokenrow *);
+void	doinclude(Tokenrow *, int);
 void	doif(Tokenrow *, enum kwtype);
 void	expand(Tokenrow *, Nlist *);
 void	builtin(Tokenrow *, int);
diff -r bfe93397b157 sys/src/cmd/cpp/include.c
--- a/sys/src/cmd/cpp/include.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/cmd/cpp/include.c	Fri Feb 12 10:59:32 2021 -0700
@@ -6,12 +6,21 @@

 char	*objname;

+static int
+curpos(int next)
+{
+	if (next)
+		return cursource->pos;
+	return NINCLUDE;
+}
+
 void
-doinclude(Tokenrow *trp)
+doinclude(Tokenrow *trp, int next)
 {
 	char fname[256], iname[256], *p;
 	Includelist *ip;
-	int angled, len, fd, i;
+	Source *s;
+	int angled, len, fd, i, pos;

 	trp->tp += 1;
 	if (trp->tp>=trp->lp)
@@ -44,6 +53,7 @@
 	if (trp->tp < trp->lp || len==0)
 		goto syntax;
 	fname[len] = '\0';
+	pos = NINCLUDE;
 	if (fname[0]=='/') {
 		fd = open(fname, 0);
 		strcpy(iname, fname);
@@ -59,7 +69,7 @@
 				fd = open(iname, 0);
 			}
 		}
-		for (i=NINCLUDE-1; fd<0 && i>=0; i--) {
+		for (i=curpos(next)-1; fd<0 && i>=0; i--) {
 			ip = &includelist[i];
 			if (ip->file==NULL || ip->deleted || (angled && ip->always==0))
 				continue;
@@ -68,9 +78,12 @@
 			strcpy(iname, ip->file);
 			strcat(iname, "/");
 			strcat(iname, fname);
-			fd = open(iname, 0);
+			if((fd = open(iname, 0)) >= 0){
+				pos = i;
+				break;
+			}
 		}
-		if (fd<0 && angled) {
+		if (fd<0 && angled && !next) {
 			strcpy(iname, cursource->filename);
 			p = strrchr(iname, '/');
 			if(p != NULL) {
@@ -78,6 +91,7 @@
 				strcat(iname, "/");
 				strcat(iname, fname);
 				fd = open(iname, 0);
+				pos = NINCLUDE-1;
 			}
 		}
 	}
@@ -89,7 +103,8 @@
 	if (fd >= 0) {
 		if (++incdepth > 20)
 			error(FATAL, "#include too deeply nested");
-		setsource((char*)newstring((uchar*)iname, strlen(iname), 0), fd, NULL);
+		s = setsource((char*)newstring((uchar*)iname, strlen(iname), 0), fd,
NULL);
+		s->pos = pos;
 		genline();
 	} else {
 		trp->tp = trp->bp+2;
diff -r bfe93397b157 sys/src/cmd/cpp/lex.c
--- a/sys/src/cmd/cpp/lex.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/cmd/cpp/lex.c	Fri Feb 12 10:59:32 2021 -0700
@@ -538,6 +538,7 @@
 	s->inp = s->inb;
 	s->inl = s->inp+len;
 	s->inl[0] = s->inl[1] = s->inl[2] = s->inl[3] = EOFC;
+	s->pos = NINCLUDE; /* outside of include dirs */
 	return s;
 }

diff -r bfe93397b157 sys/src/cmd/cpp/nlist.c
--- a/sys/src/cmd/cpp/nlist.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/cmd/cpp/nlist.c	Fri Feb 12 10:59:32 2021 -0700
@@ -28,6 +28,7 @@
 	"else",		KELSE,		ISKW,
 	"endif",	KENDIF,		ISKW,
 	"include",	KINCLUDE,	ISKW,
+	"include_next",	KINCLUDE_NEXT,	ISKW,	// extension to ANSI
 	"define",	KDEFINE,	ISKW,
 	"undef",	KUNDEF,		ISKW,
 	"line",		KLINE,		ISKW,
diff -r 3540943c10e8 sys/src/cmd/python/pyconfig.h
--- a/sys/src/cmd/python/pyconfig.h	Thu Feb 18 15:13:25 2021 +0100
+++ b/sys/src/cmd/python/pyconfig.h	Thu Feb 18 15:23:05 2021 -0700
@@ -46,7 +46,7 @@

 #define SIGWINCH 21	/* for curses */

-#define S_ISSOCK S_ISFIFO /* for hg, see /sys/include/ape/sys/stat.h */
+/* #define S_ISSOCK S_ISFIFO for hg, see /sys/include/ape/sys/stat.h */

 /* Define if --enable-ipv6 is specified */
 #define ENABLE_IPV6 1




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] [Patch] APE changes (2019 Lufia patches)
  2021-02-22  2:48 [9front] [Patch] APE changes (2019 Lufia patches) bsdsm
@ 2021-02-22  4:14 ` ori
  2021-02-22  5:20   ` Jens Staal
  2021-02-25  2:15 ` ori
  2021-02-26 19:23 ` [9front] " bsdsm
  2 siblings, 1 reply; 13+ messages in thread
From: ori @ 2021-02-22  4:14 UTC (permalink / raw)
  To: 9front

Quoth bsdsm@sdf.org:
> Hello,
> 
> This patch is a pared down version of the changes that Lufia made back in
> 2019; the essential changes from 'add funcs and types to APE' and all of
> 'add #include_next directive'. An unmodified diff of the former pull
> request can be found here[1] for comparison.
> 
> This has only been tested on an amd64 build. Works cleanly with tree as of
> Feb 20, 2021 changes.

Thanks for taking the time to port it over. Are there any
programs in particular that you wanted this for/that use
the APIs exposed?


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] [Patch] APE changes (2019 Lufia patches)
  2021-02-22  4:14 ` ori
@ 2021-02-22  5:20   ` Jens Staal
  0 siblings, 0 replies; 13+ messages in thread
From: Jens Staal @ 2021-02-22  5:20 UTC (permalink / raw)
  To: 9front

On Sun, Feb 21, 2021 at 08:14:40PM -0800, ori@eigenstate.org wrote:
> Quoth bsdsm@sdf.org:
> > Hello,
> > 
> > This patch is a pared down version of the changes that Lufia made back in
> > 2019; the essential changes from 'add funcs and types to APE' and all of
> > 'add #include_next directive'. An unmodified diff of the former pull
> > request can be found here[1] for comparison.
> > 
> > This has only been tested on an amd64 build. Works cleanly with tree as of
> > Feb 20, 2021 changes.
> 
> Thanks for taking the time to port it over. Are there any
> programs in particular that you wanted this for/that use
> the APIs exposed?
>

wow great! Looking through the patch, it seems to contain a lot of
things that I have been missing before (I do not remember them all, but
for a lot of them I resorted to ugly gnulib hacks). Lufia also got
upstream support for curl. I am not sure, but I think that that upstream
curl support depends on these patches (and a port of libressl that lufia
also made).

One question: The definition of SIGWINCH in pyconfig.h. Considering that
Python (hopefully) will be depreceated in the default 9front when it is
moved from hg to git9, would it not be better to have this at signal.h?

When real life is getting a bit less chaotic I hope to revisit porting
nethack on pdcursesmod (where we now have upstream/official support).
Some of the things here might help there (I don't remember at the
moment).



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] [Patch] APE changes (2019 Lufia patches)
  2021-02-22  2:48 [9front] [Patch] APE changes (2019 Lufia patches) bsdsm
  2021-02-22  4:14 ` ori
@ 2021-02-25  2:15 ` ori
  2021-02-25  2:21   ` ori
  2021-02-26 19:23 ` [9front] " bsdsm
  2 siblings, 1 reply; 13+ messages in thread
From: ori @ 2021-02-25  2:15 UTC (permalink / raw)
  To: 9front

Quoth bsdsm@sdf.org:
> Hello,
> 
> This patch is a pared down version of the changes that Lufia made back in
> 2019; the essential changes from 'add funcs and types to APE' and all of
> 'add #include_next directive'. An unmodified diff of the former pull
> request can be found here[1] for comparison.
> 
> This has only been tested on an amd64 build. Works cleanly with tree as of
> Feb 20, 2021 changes.
> 
> 
> [1]: https://patch-diff.githubusercontent.com/raw/lufia/plan9/pull/5.diff
> 
> 
> 
> diff -r bfe93397b157 386/include/ape/_apetypes.h
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/386/include/ape/_apetypes.h	Fri Feb 12 10:59:32 2021 -0700
> @@ -0,0 +1,7 @@
> +#if !defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN)
> +#define	__BYTE_ORDER	__LITTLE_ENDIAN
> +#endif
> +
> +#if !defined(BYTE_ORDER) && defined(LITTLE_ENDIAN)
> +#define	BYTE_ORDER	LITTLE_ENDIAN
> +#endif
> diff -r bfe93397b157 amd64/include/ape/_apetypes.h
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/amd64/include/ape/_apetypes.h	Fri Feb 12 10:59:32 2021 -0700
> @@ -0,0 +1,11 @@
> +#ifndef _BITS64
> +#define _BITS64
> +#endif
> +
> +#if !defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN)
> +#define	__BYTE_ORDER	__LITTLE_ENDIAN
> +#endif
> +
> +#if !defined(BYTE_ORDER) && defined(LITTLE_ENDIAN)
> +#define	BYTE_ORDER	LITTLE_ENDIAN
> +#endif

We'd need these for all the other archs that we
support, otherwise ape will fail to build on
anything other than 386 and amd64.

> +#endif
> diff -r bfe93397b157 sys/include/ape/pthread.h

Would you be willing to split the pthread changes
out? it's a lot of tricky code, and it's rather
hard to review.

Same for the cpp include_next, which I'd rather
not commit if we can get away with it (ie, what
software is relying on that nonstandard extension,
and is it a big change to fix the software, and
maybe even upstream the change?)


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] [Patch] APE changes (2019 Lufia patches)
  2021-02-25  2:15 ` ori
@ 2021-02-25  2:21   ` ori
  2021-02-25 16:37     ` Aaron
  0 siblings, 1 reply; 13+ messages in thread
From: ori @ 2021-02-25  2:21 UTC (permalink / raw)
  To: 9front

Quoth ori@eigenstate.org:
> Quoth bsdsm@sdf.org:
> > Hello,
> > 
> > This patch is a pared down version of the changes that Lufia made back in
> > 2019; the essential changes from 'add funcs and types to APE' and all of
> > 'add #include_next directive'. An unmodified diff of the former pull
> > request can be found here[1] for comparison.
> > 
> > This has only been tested on an amd64 build. Works cleanly with tree as of
> > Feb 20, 2021 changes.
> > 
> > 
> > [1]: https://patch-diff.githubusercontent.com/raw/lufia/plan9/pull/5.diff
> > 
> > 
> > 
> > diff -r bfe93397b157 386/include/ape/_apetypes.h
> > --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> > +++ b/386/include/ape/_apetypes.h	Fri Feb 12 10:59:32 2021 -0700
> > @@ -0,0 +1,7 @@
> > +#if !defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN)
> > +#define	__BYTE_ORDER	__LITTLE_ENDIAN
> > +#endif
> > +
> > +#if !defined(BYTE_ORDER) && defined(LITTLE_ENDIAN)
> > +#define	BYTE_ORDER	LITTLE_ENDIAN
> > +#endif
> > diff -r bfe93397b157 amd64/include/ape/_apetypes.h
> > --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> > +++ b/amd64/include/ape/_apetypes.h	Fri Feb 12 10:59:32 2021 -0700
> > @@ -0,0 +1,11 @@
> > +#ifndef _BITS64
> > +#define _BITS64
> > +#endif
> > +
> > +#if !defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN)
> > +#define	__BYTE_ORDER	__LITTLE_ENDIAN
> > +#endif
> > +
> > +#if !defined(BYTE_ORDER) && defined(LITTLE_ENDIAN)
> > +#define	BYTE_ORDER	LITTLE_ENDIAN
> > +#endif
> 
> We'd need these for all the other archs that we
> support, otherwise ape will fail to build on
> anything other than 386 and amd64.
> 
> > +#endif
> > diff -r bfe93397b157 sys/include/ape/pthread.h
> 
> Would you be willing to split the pthread changes
> out? it's a lot of tricky code, and it's rather
> hard to review.
> 
> Same for the cpp include_next, which I'd rather
> not commit if we can get away with it (ie, what
> software is relying on that nonstandard extension,
> and is it a big change to fix the software, and
> maybe even upstream the change?)
> 

And, again -- can you let me know what programs this
enables, and whether they've been tested with this
patch on 9front?


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] [Patch] APE changes (2019 Lufia patches)
  2021-02-25  2:21   ` ori
@ 2021-02-25 16:37     ` Aaron
  2021-02-25 18:06       ` ori
  0 siblings, 1 reply; 13+ messages in thread
From: Aaron @ 2021-02-25 16:37 UTC (permalink / raw)
  To: 9front



On 2/24/21 7:21 PM, ori@eigenstate.org wrote:
> Quoth ori@eigenstate.org:
>> We'd need these for all the other archs that we
>> support, otherwise ape will fail to build on
>> anything other than 386 and amd64.
>>

If I could do do things over again I would simply remove those changes. 
I removed most of the architecture specific changes, and in doing so 
removed the context for the inclusion of the `_apetypes' file. A mistake 
on my part.

>>> +#endif
>>> diff -r bfe93397b157 sys/include/ape/pthread.h
>>
>> Would you be willing to split the pthread changes
>> out? it's a lot of tricky code, and it's rather
>> hard to review.
I can split out the pthread changes and supply that diff later today.

>>
>> Same for the cpp include_next, which I'd rather
>> not commit if we can get away with it (ie, what
>> software is relying on that nonstandard extension,
>> and is it a big change to fix the software, and
>> maybe even upstream the change?)
>>
I am more than willing to supply the include_next changes separately, 
but if no one wants them imported that is fine as well.
> 
> And, again -- can you let me know what programs this
> enables, and whether they've been tested with this
> patch on 9front?
> I never went as far as the original intent, being Torvalds git, instead 
focusing on the dependencies: curl, expat, zlib, and libressl. There are 
separate patches specifically for libressl to build, but those patches 
were insufficient for me on 9front. I did manage to get libressl to 
build but curl still had issues with TLS addresses. At the time I chose 
to see the failure as isolated to the libressl patches. The other 
packages worked without modification.

As it stands it is my intention to submit separate diffs for pthread, 
include_next, and a third for the remainder (excluding _apetypes). If 
this proposal is unacceptable please let me know. If anyone would rather 
see further testing then that is perfectly fine as well.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] [Patch] APE changes (2019 Lufia patches)
  2021-02-25 16:37     ` Aaron
@ 2021-02-25 18:06       ` ori
  2021-02-26 10:28         ` cinap_lenrek
  0 siblings, 1 reply; 13+ messages in thread
From: ori @ 2021-02-25 18:06 UTC (permalink / raw)
  To: 9front

Quoth Aaron <bsdsm@sdf.org>:
> 
> I never went as far as the original intent, being Torvalds git, instead 
> focusing on the dependencies: curl, expat, zlib, and libressl. There are 
> separate patches specifically for libressl to build, but those patches 
> were insufficient for me on 9front. I did manage to get libressl to 
> build but curl still had issues with TLS addresses. At the time I chose 
> to see the failure as isolated to the libressl patches. The other 
> packages worked without modification.
>
> As it stands it is my intention to submit separate diffs for pthread, 
> include_next, and a third for the remainder (excluding _apetypes). If 
> this proposal is unacceptable please let me know. If anyone would rather 
> see further testing then that is perfectly fine as well.

I'm not trying to ask about testing. The question is
"what will you use this for?"

In my opinion, the goal of ape is to accumulate necessary
cruft for running programs, and not much more. Adding to
ape is a cost we pay to port more tools.

	http://fqa.9front.org/linuxburden.jpg

The plan for getting the code reviewed is fine, and there
are APIs that are widely used in the patches, as well as
bug fixes for existing APIs that we provide. So I'm ok
with going through these.

But I'd be far more excited if I heard something like "it
makes torvalds git work" or "it allows threads in the ocaml
port" or other user visible improvements, rather than "it
ticks checkboxes in the posix spec"


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] [Patch] APE changes (2019 Lufia patches)
  2021-02-25 18:06       ` ori
@ 2021-02-26 10:28         ` cinap_lenrek
  2021-02-26 15:38           ` Lucas Francesco
  0 siblings, 1 reply; 13+ messages in thread
From: cinap_lenrek @ 2021-02-26 10:28 UTC (permalink / raw)
  To: 9front

Bringing back in libressl/openssl is a bad idea imho.
(note, we used to have it in the base system for mercurial)

These are unmaintainable monsters that take forever to compile
and have so much code churn that we cannot keep up with
constantly updating it.

We can use libsec from ape programs and it supports the
kernels tls device. it imlements all the modern ciphers
and tls support you need with a fraction of the codesize.
And libsec *is* maintained and in active use so receives
testing and porting/arch specific support (compiler work,
optimized assembly versions).

This was done for example for the python hash module to
use libsec instead of the openssl monster.

Theres also not really a reason for git to implement its
own dns, tls and https and ssh cients. All this is solved
i plan9 using webfs and ssh. If git calls curl, you can
probaly just write a wrapper script around it to use the
native plan9 infrastructure.

Dont try to emulate the broken way unix does networking
onto plan9. It is a waste of time. If you must, attempt
a proper port and *question* the dependencies that it
clams to be needing.

--
cinap

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] [Patch] APE changes (2019 Lufia patches)
  2021-02-26 10:28         ` cinap_lenrek
@ 2021-02-26 15:38           ` Lucas Francesco
  0 siblings, 0 replies; 13+ messages in thread
From: Lucas Francesco @ 2021-02-26 15:38 UTC (permalink / raw)
  To: 9front

Hi Aaron, thanks for the effort!!!

I am personally against #include_next and pthreads, the pthread
implementation is funky and has LOTS of TODOs, and since it involves
locking I'd like to see them in a separate patchset and with more
testing. (I'm also not sure we should have pthreads on the front,
since it may be ok to just to singlethread or port those programs to
our libraries)

But just so you know, I really appreciate the effort of porting those
patches!!!! I think we should nitpick what we need for things that
make our systems more approachable and easier to use, personally I
already have some use for some of it and have tested pthreads with a
new Equis port. (due to the Erlang port I was working, and some X11
tooling), and think endian, errno, intttypes and getpgid changes might
be fine.

Em sex., 26 de fev. de 2021 às 07:33, <cinap_lenrek@felloff.net> escreveu:
>
> Bringing back in libressl/openssl is a bad idea imho.
> (note, we used to have it in the base system for mercurial)
>
> These are unmaintainable monsters that take forever to compile
> and have so much code churn that we cannot keep up with
> constantly updating it.
>
> We can use libsec from ape programs and it supports the
> kernels tls device. it imlements all the modern ciphers
> and tls support you need with a fraction of the codesize.
> And libsec *is* maintained and in active use so receives
> testing and porting/arch specific support (compiler work,
> optimized assembly versions).
>
> This was done for example for the python hash module to
> use libsec instead of the openssl monster.
>
> Theres also not really a reason for git to implement its
> own dns, tls and https and ssh cients. All this is solved
> i plan9 using webfs and ssh. If git calls curl, you can
> probaly just write a wrapper script around it to use the
> native plan9 infrastructure.
>
> Dont try to emulate the broken way unix does networking
> onto plan9. It is a waste of time. If you must, attempt
> a proper port and *question* the dependencies that it
> clams to be needing.
>
> --
> cinap

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [9front] Re: [Patch] APE changes (2019 Lufia patches)
  2021-02-22  2:48 [9front] [Patch] APE changes (2019 Lufia patches) bsdsm
  2021-02-22  4:14 ` ori
  2021-02-25  2:15 ` ori
@ 2021-02-26 19:23 ` bsdsm
  2021-03-01  3:41   ` ori
                     ` (2 more replies)
  2 siblings, 3 replies; 13+ messages in thread
From: bsdsm @ 2021-02-26 19:23 UTC (permalink / raw)
  To: 9front

[-- Attachment #1: Type: text/plain, Size: 996 bytes --]


Sorry for the delay. Attached are the changes, minus architecture
specific code, with interests separated as previously discussed.

Please allow me a moment to address my intentions. My goals are more
human than software related. I began working on a port of Lufia's
changes not long after the pull requests. I stopped for some of the
reasons already discussed: git9, libressl isn't desired, etc. I
decided to put something together only after finding out that those
original patches are still in limbo after two years through 9fans. My
intention being that if even a single line presented was thought
worthy of import then I could reach out to Lufia and say, 'hey check
this out, and maybe your next patch can come straight to 9front.'

Perhaps my goals were misguided. It is not my intention to denigrate
anyone, 9fan or otherwise.

As a final note I agree with the comments made previously about
nitpicking the changes. Please nitpick everything. I just wanted to
make my intentions clearer.

[-- Attachment #2.1: Type: text/plain, Size: 332 bytes --]

from postmaster@1ess:
The following attachment had content that we can't
prove to be harmless.  To avoid possible automatic
execution, we changed the content headers.
The original header was:

	Content-Type: text/x-patch; name="pthread.diff"
	Content-Transfer-Encoding: 8bit
	Content-Disposition: attachment; filename="pthread.diff"

[-- Attachment #2.2: pthread.diff.suspect --]
[-- Type: application/octet-stream, Size: 25669 bytes --]

diff -r bfe93397b157 sys/include/ape/pthread.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/include/ape/pthread.h	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,105 @@
+#ifndef __PTHREAD_H
+#define __PTHREAD_H
+#pragma lib "/$M/lib/ape/libpthread.a"
+
+#define _LOCK_EXTENSION
+#define _QLOCK_EXTENSION
+#include <sys/types.h>
+#include <unistd.h>
+#include <lock.h>
+#include <qlock.h>
+
+typedef struct pthread_once pthread_once_t;
+typedef pid_t pthread_t;
+typedef int pthread_attr_t;
+typedef struct pthread_mutex pthread_mutex_t;
+typedef int pthread_mutexattr_t;
+typedef struct pthread_cond pthread_cond_t;
+typedef int pthread_condattr_t;
+typedef struct pthread_key pthread_key_t;
+
+enum {
+	PTHREAD_THREADS_MAX = 1000,
+
+	PTHREAD_CANCEL_DISABLE = 1,
+};
+
+struct pthread_once {
+	Lock l;
+	int once;
+};
+struct pthread_mutex {
+	QLock l;
+
+	Lock mu;
+	pthread_t pid;
+	int ref;
+	pthread_mutexattr_t attr;
+};
+struct pthread_cond {
+	QLock l;
+	Rendez r;
+};
+struct pthread_key {
+	Lock l;
+	void (*destroy)(void*);
+	struct {
+		pthread_t	pid;
+		const void	*p;
+	} *arenas;
+	int n;
+};
+
+#define PTHREAD_ONCE_INIT		{ 0 }
+#define PTHREAD_MUTEX_INITIALIZER	{ 0 }
+#define PTHREAD_MUTEX_DEFAULT		0
+#define PTHREAD_MUTEX_NORAML		1
+#define PTHREAD_MUTEX_RECURSIVE	2
+#define PTHREAD_COND_INITIALIZER	{ 0 }
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int	pthread_atfork(void (*)(void), void (*)(void), void (*)(void));
+extern int	pthread_once(pthread_once_t*, void (*)(void));
+extern pthread_t	pthread_self(void);
+extern int	pthread_equal(pthread_t, pthread_t);
+extern int	pthread_create(pthread_t*, pthread_attr_t*, void *(*)(void*), void*);
+extern void	pthread_exit(void*);
+extern int	pthread_join(pthread_t, void**);
+
+extern int	pthread_mutexattr_init(pthread_mutexattr_t*);
+extern int	pthread_mutexattr_destroy(pthread_mutexattr_t*);
+extern int	pthread_mutexattr_settype(pthread_mutexattr_t*, int);
+
+extern int	pthread_mutex_init(pthread_mutex_t*, const pthread_mutexattr_t*);
+extern int	pthread_mutex_lock(pthread_mutex_t*);
+extern int	pthread_mutex_unlock(pthread_mutex_t*);
+extern int	pthread_mutex_trylock(pthread_mutex_t*);
+extern int	pthread_mutex_destroy(pthread_mutex_t*);
+
+extern int	pthread_cond_init(pthread_cond_t*, pthread_condattr_t*);
+extern int	pthread_cond_wait(pthread_cond_t*, pthread_mutex_t*);
+extern int	pthread_cond_signal(pthread_cond_t*);
+extern int	pthread_cond_broadcast(pthread_cond_t*);
+extern int	pthread_cond_destroy(pthread_cond_t*);
+
+extern int	pthread_key_create(pthread_key_t*, void (*)(void*));
+extern int	pthread_key_delete(pthread_key_t);
+extern void	*pthread_getspecific(pthread_key_t);
+extern int	pthread_setspecific(pthread_key_t, const void*);
+
+extern int	pthread_setcancelstate(int, int*);
+
+#ifndef _PTHREAD_SIGMASK
+#define _PTHREAD_SIGMASK
+#include <signal.h>
+extern int	pthread_sigmask(int, const sigset_t*, sigset_t*);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r bfe93397b157 sys/include/ape/qlock.h
--- a/sys/include/ape/qlock.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/qlock.h	Fri Feb 12 10:59:32 2021 -0700
@@ -26,6 +26,14 @@
 	QLp 	*tail;
 } QLock;
 
+typedef
+struct Rendez
+{
+	QLock	*l;
+	QLp	*head;
+	QLp	*tail;
+} Rendez;
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -34,6 +42,10 @@
 extern	void	qunlock(QLock*);
 extern	int	canqlock(QLock*);
 
+extern	void	rsleep(Rendez*);
+extern	int	rwakeup(Rendez*);
+extern	int	rwakeupall(Rendez*);
+
 #ifdef __cplusplus
 }
 #endif
diff -r 02e3059af5bc -r 5904c2b92376 sys/src/ape/lib/ap/plan9/qlock.c
--- a/sys/src/ape/lib/ap/plan9/qlock.c	Thu Feb 11 09:37:36 2021 +0100
+++ b/sys/src/ape/lib/ap/plan9/qlock.c	Thu Oct 26 02:42:26 2017 +0200
@@ -22,6 +22,9 @@
 enum
 {
 	Queuing,
+	QueuingR,
+	QueuingW,
+	Sleeping,
 };
 
 /* find a free shared memory location to queue ourselves in */
@@ -108,3 +112,254 @@
 	unlock(&q->lock);
 	return 0;
 }
+
+#if 0
+
+void
+rlock(RWLock *q)
+{
+	QLp *p, *mp;
+
+	lock(&q->lock);
+	if(q->writer == 0 && q->head == nil){
+		/* no writer, go for it */
+		q->readers++;
+		unlock(&q->lock);
+		return;
+	}
+
+	mp = getqlp();
+	p = q->tail;
+	if(p == 0)
+		q->head = mp;
+	else
+		p->next = mp;
+	q->tail = mp;
+	mp->next = nil;
+	mp->state = QueuingR;
+	unlock(&q->lock);
+
+	/* wait in kernel */
+	while((*_rendezvousp)(mp, (void*)1) == (void*)~0)
+		;
+	mp->inuse = 0;
+}
+
+int
+canrlock(RWLock *q)
+{
+	lock(&q->lock);
+	if (q->writer == 0 && q->head == nil) {
+		/* no writer; go for it */
+		q->readers++;
+		unlock(&q->lock);
+		return 1;
+	}
+	unlock(&q->lock);
+	return 0;
+}
+
+void
+runlock(RWLock *q)
+{
+	QLp *p;
+
+	lock(&q->lock);
+	if(q->readers <= 0)
+		abort();
+	p = q->head;
+	if(--(q->readers) > 0 || p == nil){
+		unlock(&q->lock);
+		return;
+	}
+
+	/* start waiting writer */
+	if(p->state != QueuingW)
+		abort();
+	q->head = p->next;
+	if(q->head == 0)
+		q->tail = 0;
+	q->writer = 1;
+	unlock(&q->lock);
+
+	/* wakeup waiter */
+	while((*_rendezvousp)(p, (void*)0) == (void*)~0)
+		;
+}
+
+void
+wlock(RWLock *q)
+{
+	QLp *p, *mp;
+
+	lock(&q->lock);
+	if(q->readers == 0 && q->writer == 0){
+		/* noone waiting, go for it */
+		q->writer = 1;
+		unlock(&q->lock);
+		return;
+	}
+
+	/* wait */
+	p = q->tail;
+	mp = getqlp();
+	if(p == nil)
+		q->head = mp;
+	else
+		p->next = mp;
+	q->tail = mp;
+	mp->next = nil;
+	mp->state = QueuingW;
+	unlock(&q->lock);
+
+	/* wait in kernel */
+	while((*_rendezvousp)(mp, (void*)1) == (void*)~0)
+		;
+	mp->inuse = 0;
+}
+
+int
+canwlock(RWLock *q)
+{
+	lock(&q->lock);
+	if (q->readers == 0 && q->writer == 0) {
+		/* no one waiting; go for it */
+		q->writer = 1;
+		unlock(&q->lock);
+		return 1;
+	}
+	unlock(&q->lock);
+	return 0;
+}
+
+void
+wunlock(RWLock *q)
+{
+	QLp *p;
+
+	lock(&q->lock);
+	if(q->writer == 0)
+		abort();
+	p = q->head;
+	if(p == nil){
+		q->writer = 0;
+		unlock(&q->lock);
+		return;
+	}
+	if(p->state == QueuingW){
+		/* start waiting writer */
+		q->head = p->next;
+		if(q->head == nil)
+			q->tail = nil;
+		unlock(&q->lock);
+		while((*_rendezvousp)(p, (void*)0) == (void*)~0)
+			;
+		return;
+	}
+
+	if(p->state != QueuingR)
+		abort();
+
+	/* wake waiting readers */
+	while(q->head != nil && q->head->state == QueuingR){
+		p = q->head;
+		q->head = p->next;
+		q->readers++;
+		while((*_rendezvousp)(p, (void*)0) == (void*)~0)
+			;
+	}
+	if(q->head == nil)
+		q->tail = nil;
+	q->writer = 0;
+	unlock(&q->lock);
+}
+
+void
+rsleep(Rendez *r)
+{
+	QLp *t, *me;
+
+	if(!r->l)
+		abort();
+	lock(&r->l->lock);
+	/* we should hold the qlock */
+	if(!r->l->locked)
+		abort();
+
+	/* add ourselves to the wait list */
+	me = getqlp();
+	me->state = Sleeping;
+	if(r->head == nil)
+		r->head = me;
+	else
+		r->tail->next = me;
+	me->next = nil;
+	r->tail = me;
+
+	/* pass the qlock to the next guy */
+	t = r->l->head;
+	if(t){
+		r->l->head = t->next;
+		if(r->l->head == nil)
+			r->l->tail = nil;
+		unlock(&r->l->lock);
+		while((*_rendezvousp)(t, (void*)0x12345) == (void*)~0)
+			;
+	}else{
+		r->l->locked = 0;
+		unlock(&r->l->lock);
+	}
+
+	/* wait for a wakeup */
+	while((*_rendezvousp)(me, (void*)1) == (void*)~0)
+		;
+	me->inuse = 0;
+}
+
+int
+rwakeup(Rendez *r)
+{
+	QLp *t;
+
+	/*
+	 * take off wait and put on front of queue
+	 * put on front so guys that have been waiting will not get starved
+	 */
+	
+	if(!r->l)
+		abort();
+	lock(&r->l->lock);
+	if(!r->l->locked)
+		abort();
+
+	t = r->head;
+	if(t == nil){
+		unlock(&r->l->lock);
+		return 0;
+	}
+
+	r->head = t->next;
+	if(r->head == nil)
+		r->tail = nil;
+
+	t->next = r->l->head;
+	r->l->head = t;
+	if(r->l->tail == nil)
+		r->l->tail = t;
+
+	t->state = Queuing;
+	unlock(&r->l->lock);
+	return 1;
+}
+
+int
+rwakeupall(Rendez *r)
+{
+	int i;
+
+	for(i=0; rwakeup(r); i++)
+		;
+	return i;
+}
+
+#endif
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/fork.c
--- a/sys/src/ape/lib/ap/plan9/fork.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/plan9/fork.c	Fri Feb 12 10:59:32 2021 -0700
@@ -2,18 +2,58 @@
 #include <errno.h>
 #include <unistd.h>
 #include "sys9.h"
+#include <pthread.h>
+
+enum {
+	NHANDLERS = 100
+};
+
+static void (*preparehdlr[NHANDLERS])(void);
+static void (*parenthdlr[NHANDLERS])(void);
+static void (*childhdlr[NHANDLERS])(void);
+static int nprepare;
+static int nparent;
+static int nchild;
+
+int
+pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
+{
+	if(prepare != NULL){
+		if(nprepare >= NHANDLERS)
+			return ENOMEM;
+		preparehdlr[nprepare++] = prepare;
+	}
+	if(parent != NULL){
+		if(nparent >= NHANDLERS)
+			return ENOMEM;
+		parenthdlr[nparent++] = parent;
+	}
+	if(child != NULL){
+		if(nchild >= NHANDLERS)
+			return ENOMEM;
+		childhdlr[nchild++] = child;
+	}
+	return 0;
+}
 
 pid_t
 fork(void)
 {
-	int n;
+	int n, i;
 
+	for(i = nprepare-1; i >= 0; i--)
+		preparehdlr[i]();
 	n = _RFORK(RFENVG|RFFDG|RFPROC);
 	if(n < 0)
 		_syserrno();
 	if(n == 0) {
 		_detachbuf();
 		_sessleader = 0;
+		for(i = 0; i < nchild; i++)
+			childhdlr[i]();
+		return 0;
 	}
+	for(i = 0; i < nparent; i++)
+		parenthdlr[i]();
 	return n;
 }
diff -r bfe93397b157 sys/src/ape/lib/pthread/_pthread.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/_pthread.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,83 @@
+#include <pthread.h>
+#include <string.h>
+#include "lib.h"
+
+static Lock privlock;
+static Thread privileges[PTHREAD_THREADS_MAX];
+
+Thread *
+_pthreadalloc(void)
+{
+	Thread *p, *ep;
+
+	ep = privileges+nelem(privileges);
+	lock(&privlock);
+	for(p = privileges; p < ep; p++){
+		if(!p->inuse){
+			memset(p, 0, sizeof(*p));
+			p->inuse = 1;
+			unlock(&privlock);
+			return p;
+		}
+	}
+	unlock(&privlock);
+	return NULL;
+}
+
+void
+_pthreadsetpid(Thread *priv, pthread_t pid)
+{
+	lock(&privlock);
+	priv->pid = pid;
+	unlock(&privlock);
+}
+
+Thread *
+_pthreadnew(pthread_t pid)
+{
+	Thread *p, *ep, *freep = NULL;
+
+	ep = privileges+nelem(privileges);
+	lock(&privlock);
+	for(p = privileges; p < ep; p++){
+		if(p->inuse && p->pid == pid){
+			unlock(&privlock);
+			return p;
+		}
+		if(freep == NULL && !p->inuse)
+			freep = p;
+	}
+	if(freep == NULL){
+		unlock(&privlock);
+		return NULL;
+	}
+	memset(freep, 0, sizeof(*freep));
+	freep->inuse = 1;
+	freep->pid = pid;
+	unlock(&privlock);
+	return freep;
+}
+
+Thread *
+_pthreadget(pthread_t pid)
+{
+	Thread *p, *ep;
+
+	ep = privileges+nelem(privileges);
+	lock(&privlock);
+	for(p = privileges; p < ep; p++)
+		if(p->inuse && p->pid == pid){
+			unlock(&privlock);
+			return p;
+		}
+	unlock(&privlock);
+	return NULL;
+}
+
+void
+_pthreadfree(Thread *priv)
+{
+	lock(&privlock);
+	priv->inuse = 0;
+	unlock(&privlock);
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/cond_broadcast.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/cond_broadcast.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,10 @@
+#include <pthread.h>
+
+int
+pthread_cond_broadcast(pthread_cond_t *cond)
+{
+	qlock(cond->r.l);
+	rwakeupall(&cond->r);
+	qunlock(cond->r.l);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/cond_destroy.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/cond_destroy.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,8 @@
+#include <pthread.h>
+
+int
+pthread_cond_destroy(pthread_cond_t *)
+{
+	/* TODO: should we check cond is busy? */
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/cond_init.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/cond_init.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,10 @@
+#include <pthread.h>
+#include <string.h>
+
+int
+pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *)
+{
+	memset(cond, 0, sizeof(*cond));
+	cond->r.l = &cond->l;
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/cond_signal.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/cond_signal.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,10 @@
+#include <pthread.h>
+
+int
+pthread_cond_signal(pthread_cond_t *cond)
+{
+	qlock(cond->r.l);
+	rwakeup(&cond->r);
+	qunlock(cond->r.l);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/cond_wait.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/cond_wait.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,12 @@
+#include <pthread.h>
+
+int
+pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
+{
+	pthread_mutex_unlock(mutex);
+	qlock(cond->r.l);
+	rsleep(&cond->r);
+	qunlock(cond->r.l);
+	pthread_mutex_lock(mutex);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/create.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/create.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,37 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "lib.h"
+
+extern int	_RFORK(int);
+extern int	_RENDEZVOUS(unsigned long, unsigned long);
+
+int
+pthread_create(pthread_t *t, pthread_attr_t *attr, void *(*f)(void*), void *arg)
+{
+	void *p;
+	int pid;
+	Thread *priv;
+	unsigned long tag;
+
+	if(attr != NULL)
+		return EINVAL;
+	priv = _pthreadalloc();
+	if(priv == NULL)
+		return EAGAIN;
+	tag = (unsigned long)priv;
+	pid = _RFORK(RFFDG|RFPROC|RFMEM);
+	switch(pid){
+	case -1:
+		_syserrno();
+		unlock(&priv->l);
+		_pthreadfree(priv);
+		return errno;
+	case 0:
+		_RENDEZVOUS(tag, 0);
+		p = f(arg);
+		pthread_exit(p);
+		abort(); /* can't reach here */
+	}
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/equal.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/equal.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,7 @@
+#include <pthread.h>
+
+int
+pthread_equal(pthread_t t1, pthread_t t2)
+{
+	return t1 == t2;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/exit.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/exit.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,21 @@
+#include <pthread.h>
+#include <assert.h>
+#include "lib.h"
+
+extern void	_EXITS(char *);
+
+void
+pthread_exit(void *retval)
+{
+	Thread *priv;
+	pthread_t pid;
+
+	pid = pthread_self();
+	priv = _pthreadget(pid);
+	assert(priv != NULL);
+	lock(&priv->l);
+	priv->exited = 1;
+	priv->ret = retval;
+	unlock(&priv->l);
+	_EXITS(0);
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/getspecific.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/getspecific.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,21 @@
+#include <pthread.h>
+#include "lib.h"
+
+void *
+pthread_getspecific(pthread_key_t key)
+{
+	int i;
+	pthread_t pid;
+	const void *p;
+
+	pid = pthread_self();
+	lock(&key.l);
+	for(i = 0; i < key.n; i++)
+		if(key.arenas[i].pid == pid){
+			p = key.arenas[i].p;
+			unlock(&key.l);
+			return (void *)p;
+		}
+	unlock(&key.l);
+	return NULL;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/join.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/join.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,100 @@
+#define _RESEARCH_SOURCE
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <libv.h>
+#include "lib.h"
+
+typedef
+struct Waitmsg
+{
+	int	pid;	/* of loved one */
+	ulong	time[3];	/* of loved one & descendants */
+	char	*msg;
+} Waitmsg;
+
+extern int	_AWAIT(char*, int);
+
+static Waitmsg*
+_wait(void)
+{
+	int n, l;
+	char buf[512], *fld[5];
+	Waitmsg *w;
+
+	n = _AWAIT(buf, sizeof buf-1);
+	if(n < 0){
+		_syserrno();
+		return nil;
+	}
+	buf[n] = '\0';
+	if(getfields(buf, fld, nelem(fld)) != nelem(fld)){
+		errno = ENOBUFS;
+		return nil;
+	}
+	l = strlen(fld[4])+1;
+	w = malloc(sizeof(Waitmsg)+l);
+	if(w == nil)
+		return nil;
+	w->pid = atoi(fld[0]);
+	w->time[0] = atoi(fld[1]);
+	w->time[1] = atoi(fld[2]);
+	w->time[2] = atoi(fld[3]);
+	w->msg = (char*)&w[1];
+	memmove(w->msg, fld[4], l);
+	return w;
+}
+
+static void
+emitexits(void **ret, Thread *priv)
+{
+	if(ret == NULL){
+		return;
+	}
+	*ret = priv->ret;
+}
+
+int
+pthread_join(pthread_t t, void **ret)
+{
+	static Lock l;
+	Thread *priv;
+	int pid;
+	Waitmsg *msg;
+
+	lock(&l);
+	priv = _pthreadget(t);
+	if(priv == NULL){
+		unlock(&l);
+		return EINVAL;
+	}
+	lock(&priv->l);
+	if(priv->exited){
+		emitexits(ret, priv);
+		unlock(&priv->l);
+		_pthreadfree(priv);
+		unlock(&l);
+		return 0;
+	}
+	unlock(&priv->l);
+
+	while((msg=_wait()) != NULL){
+		pid = msg->pid;
+		free(msg);
+		if(pid == t)
+			break;
+	}
+	lock(&priv->l);
+	if(priv->exited){
+		emitexits(ret, priv);
+		unlock(&priv->l);
+		_pthreadfree(priv);
+		unlock(&l);
+		return 0;
+	}
+	unlock(&priv->l);
+	unlock(&l);
+	return ESRCH;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/key_create.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/key_create.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,20 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "lib.h"
+
+int
+pthread_key_create(pthread_key_t *key, void (*destr_func)(void*))
+{
+	if(destr_func)
+		return EINVAL; /* don't implement yet */
+	memset(key, 0, sizeof(*key));
+	key->destroy = destr_func;
+	key->n = 32;
+	key->arenas = malloc(sizeof(*key->arenas)*key->n);
+	if(key->arenas == NULL)
+		return ENOMEM;
+	memset(key->arenas, 0, sizeof(*key->arenas)*key->n);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/key_delete.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/key_delete.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,10 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include "lib.h"
+
+int
+pthread_key_delete(pthread_key_t key)
+{
+	free(key.arenas);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/lib.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/lib.h	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,48 @@
+#include <signal.h>
+
+#define nelem(a)	(sizeof(a)/sizeof((a)[0]))
+
+/* rfork */
+enum
+{
+	RFNAMEG		= (1<<0),
+	RFENVG		= (1<<1),
+	RFFDG		= (1<<2),
+	RFNOTEG		= (1<<3),
+	RFPROC		= (1<<4),
+	RFMEM		= (1<<5),
+	RFNOWAIT	= (1<<6),
+	RFCNAMEG	= (1<<10),
+	RFCENVG		= (1<<11),
+	RFCFDG		= (1<<12),
+	RFREND		= (1<<13),
+	RFNOMNT		= (1<<14)
+};
+
+typedef struct Thread Thread;
+struct Thread {
+	int		inuse;
+	pid_t		pid;
+
+	Lock		l;
+	int		exited;
+	void		*ret;
+	sigset_t	sigset;
+	int		state;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void	_syserrno(void);
+
+extern Thread*	_pthreadalloc(void);
+extern void	_pthreadsetpid(Thread*, pthread_t);
+extern Thread*	_pthreadnew(pthread_t);
+extern Thread*	_pthreadget(pthread_t);
+extern void	_pthreadfree(Thread*);
+
+#ifdef __cplusplus
+}
+#endif
diff -r bfe93397b157 sys/src/ape/lib/pthread/mkfile
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/mkfile	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,36 @@
+APE=/sys/src/ape
+<$APE/config
+LIB=/$objtype/lib/ape/libpthread.a
+OFILES=\
+	_pthread.$O\
+	cond_broadcast.$O\
+	cond_destroy.$O\
+	cond_init.$O\
+	cond_signal.$O\
+	cond_wait.$O\
+	create.$O\
+	equal.$O\
+	exit.$O\
+	getspecific.$O\
+	join.$O\
+	key_create.$O\
+	key_delete.$O\
+	mutexattr_destroy.$O\
+	mutexattr_init.$O\
+	mutexattr_settype.$O\
+	mutex_destroy.$O\
+	mutex_init.$O\
+	mutex_lock.$O\
+	mutex_trylock.$O\
+	mutex_unlock.$O\
+	once.$O\
+	self.$O\
+	setcancelstate.$O\
+	setspecific.$O\
+	sigmask.$O\
+
+HFILES=lib.h
+
+</sys/src/cmd/mksyslib
+
+CFLAGS=-c -D_POSIX_SOURCE -FTVw -D_SUSV2_SOURCE -D_PLAN9_SOURCE
diff -r bfe93397b157 sys/src/ape/lib/pthread/mutex_destroy.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/mutex_destroy.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,8 @@
+#include <pthread.h>
+
+int
+pthread_mutex_destroy(pthread_mutex_t *)
+{
+	/* TODO: should we check mutex is busy? */
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/mutex_init.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/mutex_init.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,11 @@
+#include <pthread.h>
+#include <string.h>
+
+int
+pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
+{
+	memset(mutex, 0, sizeof(*mutex));
+	if(attr)
+		mutex->attr = *attr;
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/mutex_lock.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/mutex_lock.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,23 @@
+#include <pthread.h>
+
+int
+pthread_mutex_lock(pthread_mutex_t *mutex)
+{
+	pthread_t pid;
+
+	pid = pthread_self();
+	lock(&mutex->mu);
+	if(mutex->attr == PTHREAD_MUTEX_RECURSIVE && mutex->pid == pid){
+		mutex->ref++;
+		unlock(&mutex->mu);
+		return 0;
+	}
+	unlock(&mutex->mu);
+
+	qlock(&mutex->l);
+	lock(&mutex->mu);
+	mutex->pid = pid;
+	mutex->ref++;
+	unlock(&mutex->mu);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/mutex_trylock.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/mutex_trylock.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,25 @@
+#include <pthread.h>
+#include <errno.h>
+
+int
+pthread_mutex_trylock(pthread_mutex_t *mutex)
+{
+	pthread_t pid;
+
+	pid = pthread_self();
+	lock(&mutex->mu);
+	if(mutex->attr == PTHREAD_MUTEX_RECURSIVE && mutex->pid == pid){
+		mutex->ref++;
+		unlock(&mutex->mu);
+		return 0;
+	}
+	unlock(&mutex->mu);
+
+	if(!canqlock(&mutex->l))
+		return EBUSY;
+	lock(&mutex->mu);
+	mutex->pid = pid;
+	mutex->ref++;
+	unlock(&mutex->mu);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/mutex_unlock.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/mutex_unlock.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,29 @@
+#include <pthread.h>
+#include <errno.h>
+
+int
+pthread_mutex_unlock(pthread_mutex_t *mutex)
+{
+	pthread_t pid;
+
+	pid = pthread_self();
+	lock(&mutex->mu);
+	if(mutex->pid != pid){
+		unlock(&mutex->mu);
+		return EPERM;
+	}
+	if(mutex->attr == PTHREAD_MUTEX_RECURSIVE){
+		mutex->ref--;
+		if(mutex->ref <= 0){
+			mutex->pid = 0;
+			mutex->ref = 0;
+			qunlock(&mutex->l);
+		}
+		unlock(&mutex->mu);
+		return 0;
+	}
+	mutex->ref--;
+	qunlock(&mutex->l);
+	unlock(&mutex->mu);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/mutexattr_destroy.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/mutexattr_destroy.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,7 @@
+#include <pthread.h>
+
+int
+pthread_mutexattr_destroy(pthread_mutexattr_t*)
+{
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/mutexattr_init.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/mutexattr_init.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,8 @@
+#include <pthread.h>
+
+int
+pthread_mutexattr_init(pthread_mutexattr_t *attr)
+{
+	*attr = 0;
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/mutexattr_settype.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/mutexattr_settype.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,8 @@
+#include <pthread.h>
+
+int
+pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind)
+{
+	*attr = kind;
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/once.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/once.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,13 @@
+#include <pthread.h>
+
+int
+pthread_once(pthread_once_t *once_control, void (*init_routine) (void))
+{
+	lock(&once_control->l);
+	if(once_control->once == 0){
+		init_routine();
+		once_control->once++;
+	}
+	unlock(&once_control->l);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/self.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/self.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,8 @@
+#include <pthread.h>
+#include <unistd.h>
+
+pthread_t
+pthread_self(void)
+{
+	return getpid();
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/setcancelstate.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/setcancelstate.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,21 @@
+#include <pthread.h>
+#include <errno.h>
+#include "lib.h"
+
+int
+pthread_setcancelstate(int state, int *oldstate)
+{
+	Thread *priv;
+	pthread_t pid;
+
+	pid = pthread_self();
+	priv = _pthreadnew(pid);
+	if(priv == NULL)
+		return ENOMEM;
+	lock(&priv->l);
+	if(oldstate)
+		*oldstate = priv->state;
+	priv->state = state;
+	unlock(&priv->l);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/setspecific.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/setspecific.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,30 @@
+#include <pthread.h>
+#include <errno.h>
+#include "lib.h"
+
+int
+pthread_setspecific(pthread_key_t key, const void *p)
+{
+	int i;
+	pthread_t pid;
+
+	pid = pthread_self();
+	lock(&key.l);
+	/* exactly match */
+	for(i = 0; i < key.n; i++)
+		if(key.arenas[i].pid == pid){
+			key.arenas[i].p = p;
+			unlock(&key.l);
+			return 0;
+		}
+	/* unused */
+	for(i = 0; i < key.n; i++)
+		if(key.arenas[i].pid == 0){
+			key.arenas[i].pid = pid;
+			key.arenas[i].p = p;
+			unlock(&key.l);
+			return 0;
+		}
+	unlock(&key.l);
+	return ENOMEM;
+}
diff -r bfe93397b157 sys/src/ape/lib/pthread/sigmask.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/pthread/sigmask.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,21 @@
+#include <pthread.h>
+#include <errno.h>
+#include "lib.h"
+
+int
+pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset)
+{
+	Thread *priv;
+	pthread_t pid;
+
+	pid = pthread_self();
+	priv = _pthreadnew(pid);
+	if(priv == NULL)
+		return ENOMEM;
+	lock(&priv->l);
+	if(oldset)
+		*oldset = priv->sigset;
+	priv->sigset = *set;
+	unlock(&priv->l);
+	return 0;
+}

[-- Attachment #3.1: Type: text/plain, Size: 342 bytes --]

from postmaster@1ess:
The following attachment had content that we can't
prove to be harmless.  To avoid possible automatic
execution, we changed the content headers.
The original header was:

	Content-Type: text/x-patch; name="include_next.diff"
	Content-Transfer-Encoding: 8bit
	Content-Disposition: attachment; filename="include_next.diff"

[-- Attachment #3.2: include_next.diff.suspect --]
[-- Type: application/octet-stream, Size: 4046 bytes --]

diff -r bfe93397b157 sys/src/cmd/cpp/cpp.c
--- a/sys/src/cmd/cpp/cpp.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/cmd/cpp/cpp.c	Fri Feb 12 10:59:32 2021 -0700
@@ -238,7 +238,12 @@
 		break;
 
 	case KINCLUDE:
-		doinclude(trp);
+		doinclude(trp, 0);
+		trp->lp = trp->bp;
+		return;
+
+	case KINCLUDE_NEXT:
+		doinclude(trp, 1);
 		trp->lp = trp->bp;
 		return;
 
diff -r bfe93397b157 sys/src/cmd/cpp/cpp.h
--- a/sys/src/cmd/cpp/cpp.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/cmd/cpp/cpp.h	Fri Feb 12 10:59:32 2021 -0700
@@ -19,8 +19,8 @@
 		ASRSH, ASOR, ASAND, ELLIPS,
 		DSHARP1, NAME1, DEFINED, UMINUS, MAXTOK};
 
-enum kwtype { KIF, KIFDEF, KIFNDEF, KELIF, KELSE, KENDIF, KINCLUDE, KDEFINE,
-		KUNDEF, KLINE, KERROR, KWARNING, KPRAGMA, KDEFINED,
+enum kwtype { KIF, KIFDEF, KIFNDEF, KELIF, KELSE, KENDIF, KINCLUDE, KINCLUDE_NEXT,
+		KDEFINE, KUNDEF, KLINE, KERROR, KWARNING, KPRAGMA, KDEFINED,
 		KLINENO, KFILE, KDATE, KTIME, KSTDC, KEVAL };
 
 #define	ISDEFINED	01	/* has #defined value */
@@ -60,6 +60,7 @@
 	int	fd;		/* input source */
 	int	ifdepth;	/* conditional nesting in include */
 	struct	source *next;	/* stack for #include */
+	int	pos;		/* next position for #include_next */
 } Source;
 
 typedef struct nlist {
@@ -105,7 +106,7 @@
 void	control(Tokenrow *);
 void	dodefine(Tokenrow *);
 void	doadefine(Tokenrow *, int);
-void	doinclude(Tokenrow *);
+void	doinclude(Tokenrow *, int);
 void	doif(Tokenrow *, enum kwtype);
 void	expand(Tokenrow *, Nlist *);
 void	builtin(Tokenrow *, int);
diff -r bfe93397b157 sys/src/cmd/cpp/include.c
--- a/sys/src/cmd/cpp/include.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/cmd/cpp/include.c	Fri Feb 12 10:59:32 2021 -0700
@@ -6,12 +6,21 @@
 
 char	*objname;
 
+static int
+curpos(int next)
+{
+	if (next)
+		return cursource->pos;
+	return NINCLUDE;
+}
+
 void
-doinclude(Tokenrow *trp)
+doinclude(Tokenrow *trp, int next)
 {
 	char fname[256], iname[256], *p;
 	Includelist *ip;
-	int angled, len, fd, i;
+	Source *s;
+	int angled, len, fd, i, pos;
 
 	trp->tp += 1;
 	if (trp->tp>=trp->lp)
@@ -44,6 +53,7 @@
 	if (trp->tp < trp->lp || len==0)
 		goto syntax;
 	fname[len] = '\0';
+	pos = NINCLUDE;
 	if (fname[0]=='/') {
 		fd = open(fname, 0);
 		strcpy(iname, fname);
@@ -59,7 +69,7 @@
 				fd = open(iname, 0);
 			}
 		}
-		for (i=NINCLUDE-1; fd<0 && i>=0; i--) {
+		for (i=curpos(next)-1; fd<0 && i>=0; i--) {
 			ip = &includelist[i];
 			if (ip->file==NULL || ip->deleted || (angled && ip->always==0))
 				continue;
@@ -68,9 +78,12 @@
 			strcpy(iname, ip->file);
 			strcat(iname, "/");
 			strcat(iname, fname);
-			fd = open(iname, 0);
+			if((fd = open(iname, 0)) >= 0){
+				pos = i;
+				break;
+			}
 		}
-		if (fd<0 && angled) {
+		if (fd<0 && angled && !next) {
 			strcpy(iname, cursource->filename);
 			p = strrchr(iname, '/');
 			if(p != NULL) {
@@ -78,6 +91,7 @@
 				strcat(iname, "/");
 				strcat(iname, fname);
 				fd = open(iname, 0);
+				pos = NINCLUDE-1;
 			}
 		}
 	}
@@ -89,7 +103,8 @@
 	if (fd >= 0) {
 		if (++incdepth > 20)
 			error(FATAL, "#include too deeply nested");
-		setsource((char*)newstring((uchar*)iname, strlen(iname), 0), fd, NULL);
+		s = setsource((char*)newstring((uchar*)iname, strlen(iname), 0), fd, NULL);
+		s->pos = pos;
 		genline();
 	} else {
 		trp->tp = trp->bp+2;
diff -r bfe93397b157 sys/src/cmd/cpp/lex.c
--- a/sys/src/cmd/cpp/lex.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/cmd/cpp/lex.c	Fri Feb 12 10:59:32 2021 -0700
@@ -538,6 +538,7 @@
 	s->inp = s->inb;
 	s->inl = s->inp+len;
 	s->inl[0] = s->inl[1] = s->inl[2] = s->inl[3] = EOFC;
+	s->pos = NINCLUDE; /* outside of include dirs */
 	return s;
 }
 
diff -r bfe93397b157 sys/src/cmd/cpp/nlist.c
--- a/sys/src/cmd/cpp/nlist.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/cmd/cpp/nlist.c	Fri Feb 12 10:59:32 2021 -0700
@@ -28,6 +28,7 @@
 	"else",		KELSE,		ISKW,
 	"endif",	KENDIF,		ISKW,
 	"include",	KINCLUDE,	ISKW,
+	"include_next",	KINCLUDE_NEXT,	ISKW,	// extension to ANSI
 	"define",	KDEFINE,	ISKW,
 	"undef",	KUNDEF,		ISKW,
 	"line",		KLINE,		ISKW,

[-- Attachment #4.1: Type: text/plain, Size: 348 bytes --]

from postmaster@1ess:
The following attachment had content that we can't
prove to be harmless.  To avoid possible automatic
execution, we changed the content headers.
The original header was:

	Content-Type: text/x-patch; name="excluded_middle.diff"
	Content-Transfer-Encoding: 8bit
	Content-Disposition: attachment; filename="excluded_middle.diff"

[-- Attachment #4.2: excluded_middle.diff.suspect --]
[-- Type: application/octet-stream, Size: 29223 bytes --]

diff -r bfe93397b157 sys/include/ape/bsd.h
--- a/sys/include/ape/bsd.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/bsd.h	Fri Feb 12 10:59:32 2021 -0700
@@ -36,6 +36,12 @@
 extern int	rcmd(char**, int, char*, char*, char*, int*);
 extern int	strcasecmp(char*, char*);
 extern int	strncasecmp(char*, char*,int);
+extern unsigned int	arc4random(void);
+extern void	arc4random_buf(void*, size_t);
+extern int	getentropy(void*, size_t);
+
+extern const char*	getprogname(void);
+extern void	setprogname(const char*);
 
 extern int	getopt(int, char**, char*);
 extern int	opterr;
@@ -44,9 +50,9 @@
 extern char	*optarg;
 extern char	*mktemp(char *);
 extern char	*sys_errlist[];
-extern int		sys_nerr;
+extern int	sys_nerr;
 
-extern int	gethostname(char*, int);
+extern int	gethostname(char*, size_t);
 
 #ifdef __cplusplus
 }
diff -r bfe93397b157 sys/include/ape/errno.h
--- a/sys/include/ape/errno.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/errno.h	Fri Feb 12 10:59:32 2021 -0700
@@ -80,6 +80,16 @@
 #define ECANCELED	61
 #define EINPROGRESS	62
 
+/* from research unix */
+#define ETXTBSY	63
+
+/* Added in more recent 1003.x versions */
+#define EALREADY	64
+#define ECONNRESET	65
+
+#define EOVERFLOW	66
+#define ELOOP		67
+
 #endif /* _POSIX_SOURCE */
 
 #endif /* __ERRNO */
diff -r bfe93397b157 sys/include/ape/inttypes.h
--- a/sys/include/ape/inttypes.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/inttypes.h	Fri Feb 12 10:59:32 2021 -0700
@@ -11,21 +11,25 @@
 #define PRId16 "d"
 #define PRId32 "d"
 #define PRId64 "lld"
+#define PRIdMAX "lld"
 
 #define PRIo8 "o"
 #define PRIo16 "o"
 #define PRIo32 "o"
 #define PRIo64 "llo"
+#define PRIoMAX "llo"
 
 #define PRIx8 "x"
 #define PRIx16 "x"
 #define PRIx32 "x"
 #define PRIx64 "llx"
+#define PRIxMAX "llx"
 
 #define PRIu8 "u"
 #define PRIu16 "u"
 #define PRIu32 "u"
 #define PRIu64 "llu"
+#define PRIuMAX "llu"
 
 extern intmax_t strtoimax(const char *, char **, int);
 
diff -r bfe93397b157 sys/include/ape/lib9.h
--- a/sys/include/ape/lib9.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/lib9.h	Fri Feb 12 10:59:32 2021 -0700
@@ -75,5 +75,6 @@
 extern	void		setfcr(unsigned long);
 extern	void		setfsr(unsigned long);
 extern	int 	fd2path(int, char*, int);
+extern	vlong		nsec(void);
 
 #endif
diff -r bfe93397b157 sys/include/ape/libgen.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/include/ape/libgen.h	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,16 @@
+#ifndef __LIBGEN_H
+#define __LIBGEN_H
+#pragma lib "/$M/lib/ape/libap.a"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern char *dirname(char *path);
+extern char *basename(char *path);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r bfe93397b157 sys/include/ape/netdb.h
--- a/sys/include/ape/netdb.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/netdb.h	Fri Feb 12 10:59:32 2021 -0700
@@ -147,6 +147,8 @@
 #define NI_NUMERICSCOPE	0x0010	/* For IPv6 addresses, the numeric form of the scope identifier is returned
 				   instead of its name. */
 #define NI_DGRAM	0x0020	/* Indicates that the service is a datagram service (SOCK_DGRAM). */
+#define NI_MAXHOST	1025
+#define NI_MAXSERV	32
 
 /* Error values for `getaddrinfo' and `getnameinfo' functions.  */
 #define EAI_BADFLAGS	  -1	/* Invalid value for `ai_flags' field */
diff -r bfe93397b157 sys/include/ape/poll.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/include/ape/poll.h	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,41 @@
+#ifndef __POLL_H
+#define __POLL_H
+#pragma lib "/$M/lib/ape/libap.a"
+
+#ifndef	FD_SETSIZE
+#define	FD_SETSIZE	96
+#endif
+
+struct pollfd {
+	int fd;			/* file descriptor */
+	short events;	/* events to look for */
+	short revents;	/* events returned */
+};
+
+typedef unsigned long nfds_t;
+
+#define	POLLIN		0x001
+#define	POLLPRI		0x002
+#define	POLLOUT		0x004
+#define	POLLERR		0x008
+#define	POLLHUP		0x010
+#define	POLLNVAL	0x020
+
+#define	POLLRDNORM	0x040
+#define	POLLRDBAND	0x080
+#define	POLLWRNORM	POLLOUT
+#define	POLLWRBAND	0x100
+
+#define	INFTIM	-1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int poll(struct pollfd fds[], nfds_t nfds, int timeout);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r bfe93397b157 sys/include/ape/signal.h
--- a/sys/include/ape/signal.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/signal.h	Fri Feb 12 10:59:32 2021 -0700
@@ -37,11 +37,12 @@
 #define SIGTSTP	18	/* interactive stop */
 #define SIGTTIN	19	/* read from ctl tty by member of background */
 #define SIGTTOU	20	/* write to ctl tty by member of background */
-#define SIGVTALRM 21 /* virtual alarm clock */
-#define SIGPROF 22  /* profiling alarm clock */
+#define SIGWINCH 21	/* window size changes */
+#define SIGVTALRM 22 /* virtual alarm clock */
+#define SIGPROF 23  /* profiling alarm clock */
 
 #ifdef _BSD_EXTENSION
-#define NSIG 23
+#define NSIG 24
 #endif
 
 #ifdef __cplusplus
@@ -65,6 +66,10 @@
 };
 /* values for sa_flags */
 #define SA_NOCLDSTOP	1
+#define SA_ONSTACK	2
+#define SA_RESETHAND	3
+#define SA_RESTART	4
+#define SA_RESTORER	5
 
 /* first argument to sigprocmask */
 #define SIG_BLOCK	1
diff -r bfe93397b157 sys/include/ape/stdint.h
--- a/sys/include/ape/stdint.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/stdint.h	Fri Feb 12 10:59:32 2021 -0700
@@ -10,6 +10,15 @@
 typedef unsigned int _uintptr_t;
 #endif
 
+typedef char s8;
+typedef short s16;
+typedef long s32;
+typedef long long s64;
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned long u32;
+typedef unsigned long long u64;
+
 typedef char int8_t;
 typedef short int16_t;
 typedef int int32_t;
diff -r bfe93397b157 sys/include/ape/stdlib.h
--- a/sys/include/ape/stdlib.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/stdlib.h	Fri Feb 12 10:59:32 2021 -0700
@@ -49,6 +49,10 @@
 extern size_t mbstowcs(wchar_t *, const char *, size_t);
 extern size_t wcstombs(char *, const wchar_t *, size_t);
 
+#ifdef _BSD_EXTENSION
+#include <bsd.h>
+#endif
+
 #ifdef _POSIX_C_SOURCE
 extern int mkstemp(char *template);
 #endif
diff -r bfe93397b157 sys/include/ape/sys/resource.h
--- a/sys/include/ape/sys/resource.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/sys/resource.h	Fri Feb 12 10:59:32 2021 -0700
@@ -5,6 +5,17 @@
     This header file is an extension to ANSI/POSIX
 #endif
 
+#include <sys/time.h>
+
+#pragma lib "/$M/lib/ape/libap.a"
+
+enum {
+	RUSAGE_SELF = 0,
+	RUSAGE_CHILDREN = -1
+};
+
+#define RUSAGE_SELF		RUSAGE_SELF
+#define RUSAGE_CHILDREN		RUSAGE_CHILDREN
 struct rusage {
 	struct timeval ru_utime;	/* user time used */
 	struct timeval ru_stime;	/* system time used */
@@ -26,4 +37,14 @@
 #define	ru_last		ru_nivcsw
 };
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int getrusage(int, struct rusage *);
+
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* !__RESOURCE_H__ */
diff -r bfe93397b157 sys/include/ape/sys/socket.h
--- a/sys/include/ape/sys/socket.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/sys/socket.h	Fri Feb 12 10:59:32 2021 -0700
@@ -24,6 +24,7 @@
  */
 typedef int socklen_t;
 typedef unsigned short sa_family_t;
+typedef unsigned short in_port_t;
 
 /*
  * Types
@@ -178,6 +179,15 @@
 
 #define	MSG_MAXIOVLEN	16
 
+#define TCP_NODELAY	1
+#define TCP_MAXSEG	2
+
+enum {
+	SHUT_RD,		/* no more receptions */
+	SHUT_WR,		/* no more transmissions */
+	SHUT_RDWR,		/* no more receptions or transmissions */
+};
+
 extern int accept(int, void *, int *);
 extern int bind(int, void *, int);
 extern int connect(int, void *, int);
diff -r bfe93397b157 sys/include/ape/sys/stat.h
--- a/sys/include/ape/sys/stat.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/sys/stat.h	Fri Feb 12 10:59:32 2021 -0700
@@ -27,6 +27,7 @@
 #define	S__MASK		     0170000
 #ifdef _RESEARCH_SOURCE
 #define S_ISLNK(m)	(((m)&S__MASK) == 0120000)
+#define S_ISSOCK(m)	(((m)&S__MASK) == 0010000)
 #endif
 #define S_ISREG(m)	(((m)&S__MASK) == 0100000)
 #define S_ISDIR(m)	(((m)&S__MASK) == 0040000)
diff -r bfe93397b157 sys/include/ape/sys/types.h
--- a/sys/include/ape/sys/types.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/sys/types.h	Fri Feb 12 10:59:32 2021 -0700
@@ -2,6 +2,7 @@
 #define __TYPES_H
 
 #pragma lib "/$M/lib/ape/libap.a"
+
 typedef	unsigned short	ino_t;
 typedef	unsigned short	dev_t;
 typedef	long long		off_t;
@@ -10,6 +11,11 @@
 typedef short		gid_t;
 typedef short		nlink_t;
 typedef int		pid_t;
+typedef unsigned char	u_char;
+typedef unsigned short	u_short;
+typedef unsigned int	u_int;
+typedef unsigned long	u_long;
+typedef unsigned int	in_addr_t;
 
 #ifndef _SIZE_T
 #define _SIZE_T
@@ -25,6 +31,11 @@
 typedef long time_t;
 #endif
 
+#ifndef _CLOCKID_T
+#define _CLOCKID_T
+typedef int clockid_t;
+#endif
+
 #ifdef _BSD_EXTENSION
 #ifndef _CADDR_T
 #define _CADDR_T
diff -r bfe93397b157 sys/include/ape/syslog.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/include/ape/syslog.h	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,57 @@
+#ifndef __SYSLOG_H
+#define __SYSLOG_H
+#pragma lib "/$M/lib/ape/libap.a"
+
+enum {
+	/* facility */
+	LOG_KERN	= 0<<3,
+	LOG_USER	= 1<<3,
+	LOG_DAEMON	= 3<<3,
+	LOG_AUTH	= 4<<3,
+	LOG_SYSLOG	= 5<<3,
+	LOG_CRON	= 9<<3,
+	LOG_AUTHPRIV= 10<<3,
+	LOG_LOCAL0	= 16<<3,
+	LOG_LOCAL1	= 17<<3,
+	LOG_LOCAL2	= 18<<3,
+	LOG_LOCAL3	= 19<<3,
+	LOG_LOCAL4	= 20<<3,
+	LOG_LOCAL5	= 21<<3,
+	LOG_LOCAL6	= 22<<3,
+	LOG_LOCAL7	= 23<<3,
+};
+
+enum {
+	/* priority */
+	LOG_EMERG,
+	LOG_ALERT,
+	LOG_CRIT,
+	LOG_ERR,
+	LOG_WARNING,
+	LOG_NOTICE,
+	LOG_INFO,
+	LOG_DEBUG,
+};
+
+enum {
+	/* option */
+	LOG_PID		= 0x01,
+	LOG_CONS	= 0x02,
+	LOG_ODELAY	= 0x04,
+	LOG_NDELAY	= 0x08,
+	LOG_NOWAIT	= 0x10,
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void openlog(const char *, int, int);
+extern void syslog(int, const char *, ...);
+extern void closelog(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r bfe93397b157 sys/include/ape/time.h
--- a/sys/include/ape/time.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/time.h	Fri Feb 12 10:59:32 2021 -0700
@@ -9,6 +9,9 @@
 /* obsolsecent, but required */
 #define CLK_TCK CLOCKS_PER_SEC
 
+#define CLOCK_REALTIME 0
+#define CLOCK_MONOTONIC 1
+
 #ifndef _CLOCK_T
 #define _CLOCK_T
 typedef long clock_t;
@@ -17,6 +20,10 @@
 #define _TIME_T
 typedef long time_t;
 #endif
+#ifndef _CLOCKID_T
+#define _CLOCKID_T
+typedef int clockid_t;
+#endif
 
 struct tm {
 	int	tm_sec;
@@ -43,6 +50,8 @@
 extern struct tm *gmtime(const time_t *);
 extern struct tm *localtime(const time_t *);
 extern size_t strftime(char *, size_t, const char *, const struct tm *);
+extern int clock_gettime(clockid_t, struct timespec *);
+extern int clock_settime(clockid_t, struct timespec *);
 
 #ifdef _REENTRANT_SOURCE
 extern struct tm *gmtime_r(const time_t *, struct tm *);
diff -r bfe93397b157 sys/include/ape/unistd.h
--- a/sys/include/ape/unistd.h	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/include/ape/unistd.h	Fri Feb 12 10:59:32 2021 -0700
@@ -62,6 +62,7 @@
 #define	_SC_SAVED_IDS		9	/* saved suid/sgid per process */
 #define	_SC_VERSION		10	/* this version */
 #define _SC_LOGIN_NAME_MAX	11	/* max length of a login name */
+#define _SC_PAGESIZE		12
 
 /* pathconf argument */
 #define _PC_LINK_MAX		1
@@ -115,6 +116,7 @@
 extern int setgid(gid_t);
 extern int getgroups(int, gid_t *);
 extern pid_t getpgrp(void);
+extern int getpgid(pid_t);
 extern int setpgid(pid_t, pid_t);
 extern pid_t setsid(void);
 #endif
@@ -130,6 +132,7 @@
 extern int access(const char *, int);
 extern long pathconf(const char *, int);
 extern long fpathconf(int, int);
+extern int fchdir(int);
 #ifdef __TYPES_H
 extern int chown(const char *, uid_t, gid_t);
 #endif
@@ -144,6 +147,9 @@
 #ifdef __TYPES_H
 extern int ftruncate(int, off_t);
 extern off_t lseek(int, off_t, int);
+
+extern ssize_t pread(int, void *, size_t, off_t);
+extern ssize_t pwrite(int, const void *, size_t, off_t);
 #endif
 
 /* device- and class-specific functions */
@@ -159,8 +165,12 @@
 /* berkeley specific functions */
 #ifdef _BSD_EXTENSION
 #include <bsd.h>
+
+extern int getpagesize(void);
 #endif
 
+extern int gethostname(char *, size_t);
+
 #ifdef __cplusplus
 }
 #endif
diff -r bfe93397b157 sys/src/ape/lib/9/mkfile
--- a/sys/src/ape/lib/9/mkfile	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/9/mkfile	Fri Feb 12 10:59:32 2021 -0700
@@ -13,6 +13,7 @@
 	getfcr.$O\
 	getfields.$O\
 	mount.$O\
+	nsec.$O\
 	rendezvous.$O\
 	rfork.$O\
 	segattach.$O\
diff -r bfe93397b157 sys/src/ape/lib/9/nsec.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/9/nsec.c	Fri Feb 26 11:05:08 2021 -0700
@@ -0,0 +1,9 @@
+#include <lib9.h>
+
+extern vlong _NSEC(void);
+
+vlong
+nsec(void)
+{
+	return _NSEC();
+}
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/clock_gettime.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/ap/plan9/clock_gettime.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,33 @@
+#include <time.h>
+#include <sys/times.h>
+#include <lib9.h>
+#include <errno.h>
+
+#define NANO_IN_SEC 1000000000L
+
+int
+clock_gettime(clockid_t clock_id, struct timespec *tp)
+{
+	vlong ns;
+
+	switch(clock_id){
+	case CLOCK_REALTIME:
+	case CLOCK_MONOTONIC:
+		ns = nsec();
+		tp->tv_sec = ns/NANO_IN_SEC;
+		tp->tv_nsec = ns%NANO_IN_SEC;
+		return 0;
+	default:
+		errno = EINVAL;
+		return -1;
+	}
+}
+
+int
+clock_settime(clockid_t clock_id, struct timespec *tp)
+{
+	USED(clock_id);
+	USED(tp);
+	errno = EPERM;
+	return -1;
+}
diff -r 02e3059af5bc sys/src/ape/lib/ap/plan9/dirstat.c
--- a/sys/src/ape/lib/ap/plan9/dirstat.c	Thu Feb 11 09:37:36 2021 +0100
+++ b/sys/src/ape/lib/ap/plan9/dirstat.c	Fri Feb 12 10:59:32 2021 -0700
@@ -63,11 +63,11 @@
 
 	nd = DIRSIZE;
 	for(i=0; i<2; i++){	/* should work by the second try */
-		d = malloc(sizeof(Dir) + nd);
+		d = malloc(sizeof(Dir) + BIT16SZ +nd);
 		if(d == nil)
 			return nil;
 		buf = (uchar*)&d[1];
-		n = _FSTAT(fd, buf, nd);
+		n = _FSTAT(fd, buf, BIT16SZ+nd);
 		if(n < BIT16SZ){
 			free(d);
 			return nil;
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/fchdir.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/ap/plan9/fchdir.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,32 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include "lib.h"
+#include <errno.h>
+
+extern int _FD2PATH(int fd, char *buf, int nbuf);
+extern int _CHDIR(char *dirname);
+
+int
+fchdir(int fd)
+{
+	char buf[_POSIX_PATH_MAX];
+	struct stat s;
+	int n;
+
+	if(fstat(fd, &s) < 0)
+		return -1;
+	if(!S_ISDIR(s.st_mode)){
+		errno = ENOTDIR;
+		return -1;
+	}
+
+	if (_FD2PATH(fd, buf, sizeof buf) < 0){
+		_syserrno();
+		return -1;
+	}
+	n = _CHDIR(buf);
+	if(n < 0)
+		_syserrno();
+	return n;
+}
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/fsync.c
--- a/sys/src/ape/lib/ap/plan9/fsync.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/plan9/fsync.c	Fri Feb 12 10:59:32 2021 -0700
@@ -5,6 +5,6 @@
 int
 fsync(int)
 {
-	errno = EINVAL;
-	return -1;
+	/* TODO: should fsync return an error? */
+	return 0;
 }
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/getpgid.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/ap/plan9/getpgid.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,31 @@
+#include "lib.h"
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "sys9.h"
+
+int
+getpgid(pid_t pid)
+{
+	int n, f;
+	char buf[50], fname[30];
+
+	if(pid == 0)
+		pid = getpid();
+	sprintf(fname, "/proc/%d/noteid", pid);
+	f = open(fname, 0);
+	if(f < 0) {
+		errno = ESRCH;
+		return -1;
+	}
+	n = read(f, buf, sizeof(buf));
+	if(n < 0)
+		_syserrno();
+	else{
+		buf[n] = '\0';
+		n = atoi(buf);
+	}
+	close(f);
+	return n;
+}
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/getrusage.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/ap/plan9/getrusage.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,12 @@
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <string.h>
+
+int
+getrusage(int who, struct rusage *usage)
+{
+	/* dummy implementation */
+	USED(who);
+	memset(usage, 0, sizeof *usage);
+	return 0;
+}
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/mkfile
--- a/sys/src/ape/lib/ap/plan9/mkfile	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/plan9/mkfile	Fri Feb 12 10:59:32 2021 -0700
@@ -25,6 +25,7 @@
 	chroot.$O\
 	chmod.$O\
 	chown.$O\
+	clock_gettime.$O\
 	close.$O\
 	convM2D.$O\
 	convD2M.$O\
@@ -42,6 +43,7 @@
 	execv.$O\
 	execve.$O\
 	execvp.$O\
+	fchdir.$O\
 	fcntl.$O\
 	fork.$O\
 	frexp.$O\
@@ -54,11 +56,13 @@
 	getgrnam.$O\
 	getgroups.$O\
 	getlogin.$O\
+	getpgid.$O\
 	getpgrp.$O\
 	getpid.$O\
 	getppid.$O\
 	getpwnam.$O\
 	getpwuid.$O\
+	getrusage.$O\
 	getuid.$O\
 	isatty.$O\
 	kill.$O\
@@ -71,6 +75,7 @@
 	opendir.$O\
 	pause.$O\
 	pipe.$O\
+	poll.$O\
 	profile.$O\
 	qlock.$O\
 	read.$O\
@@ -87,6 +92,7 @@
 	sleep.$O\
 	sqrt.$O\
 	stat.$O\
+	syslog.$O\
 	system.$O\
 	tcgetattr.$O\
 	time.$O\
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/poll.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/ap/plan9/poll.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <poll.h>
+
+int
+poll(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+	int n;
+	fd_set rfds, wfds, efds;
+	struct timeval w, *wp;
+	struct pollfd *p, *ep;
+
+	n = -1;
+	FD_ZERO(&rfds);
+	FD_ZERO(&wfds);
+	FD_ZERO(&efds);
+
+	ep = fds + nfds;
+	for(p = fds; p < ep; p++){
+		if(p->fd < 0)
+			continue;
+		if((p->events&(POLLIN|POLLPRI|POLLOUT)) == 0)
+			continue;
+		if(p->events&POLLIN)
+			FD_SET(p->fd, &rfds);
+		if(p->events&POLLOUT)
+			FD_SET(p->fd, &wfds);
+		if(p->events&(POLLPRI))
+			FD_SET(p->fd, &efds);
+		if(p->fd > n)
+			n = p->fd;
+	}
+	wp = NULL;
+	if(timeout >= 0){
+		w.tv_sec = timeout/1000;
+		w.tv_usec = (timeout%1000)*1000;
+		wp = &w;
+	}
+	n = select(n+1, &rfds, &wfds, &efds, wp);
+	if(n < 0)
+		return -1;
+
+	/* POLLHUP means the socket is no longer connected. (FIN) */
+	/* POLLERR means the socket got an asynchronous error. (RST) */
+
+	for(p = fds; p < ep; p++){
+		p->revents = 0;
+		if(FD_ISSET(p->fd, &rfds))
+			p->revents |= POLLIN;
+		if(FD_ISSET(p->fd, &wfds))
+			p->revents |= POLLOUT;
+		if(FD_ISSET(p->fd, &efds))
+			p->revents |= POLLPRI|POLLHUP;
+	}
+	return n;
+}
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/read.c
--- a/sys/src/ape/lib/ap/plan9/read.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/plan9/read.c	Fri Feb 12 10:59:32 2021 -0700
@@ -1,4 +1,5 @@
 #include <errno.h>
+#include <inttypes.h>
 #include <unistd.h>
 #include <string.h>
 #include "lib.h"
@@ -7,7 +8,7 @@
 #include <stdio.h>
 
 ssize_t
-read(int d, void *buf, size_t nbytes)
+pread(int d, void *buf, size_t nbytes, off_t offset)
 {
 	int n, noblock, isbuf;
 	Fdinfo *f;
@@ -38,9 +39,15 @@
 		}
 		n = _readbuf(d, buf, nbytes, noblock);
 	}else{
-		n = _READ(d, buf, nbytes);
+		n = _PREAD(d, buf, nbytes, offset);
 		if(n < 0)
 			_syserrno();
 	}
 	return n;
 }
+
+ssize_t
+read(int d, void *buf, size_t nbytes)
+{
+	return pread(d, buf, nbytes, -1LL);
+}
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/syslog.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/ap/plan9/syslog.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,104 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <syslog.h>
+
+static struct {
+	const char *ident;
+	char hostname[128];
+	int option;
+	int facility;
+	int fd;
+	int consfd;
+} flags = {
+	.fd = -1,
+	.consfd = -1,
+};
+
+void
+openlog(const char *ident, int option, int facility)
+{
+	char buf[1024];
+	int fd;
+
+	closelog();
+	if(gethostname(flags.hostname, sizeof(flags.hostname)) < 0)
+		return;
+	if(ident){
+		snprintf(buf, sizeof(buf), "/sys/log/%s", ident);
+		fd = open(buf, O_WRONLY);
+		if(fd < 0)
+			return;
+		if(fcntl(fd, F_SETFD, FD_CLOEXEC) < 0){
+			close(fd);
+			return;
+		}
+		flags.fd = fd;
+	}
+	if(option&LOG_CONS){
+		fd = open("/dev/cons", O_WRONLY);
+		if(fd < 0){
+			closelog();
+			return;
+		}
+		if(fcntl(fd, F_SETFD, FD_CLOEXEC) < 0){
+			closelog();
+			return;
+		}
+		flags.consfd = fd;
+	}
+	flags.ident = ident;
+	flags.option = option;
+	flags.facility = facility;
+}
+
+void
+syslog(int priority, const char *format, ...)
+{
+	char buf[128], *s, *p;
+	time_t t;
+	int n;
+	va_list arg;
+
+	/* syslog => Mar 10 01:45:50 $hostname $prog[$pid]: $msg */
+	/* plan9  => $hostname Mar 10 01:45:50 $msg */
+
+	USED(priority);
+
+	/* TODO: lock */
+	t = time(NULL);
+	s = ctime(&t);
+	p = buf + snprintf(buf, sizeof(buf)-1, "%s ", flags.hostname);
+	strncpy(p, s+4, 15);
+	p += 15;
+	*p++ = ' ';
+	va_start(arg, format);
+	p += vsnprintf(p, buf+sizeof(buf)-p-1, format, arg);
+	va_end(arg);
+	*p++ = '\n';
+	n = p - buf;
+	if(flags.fd >= 0){
+		lseek(flags.fd, 0, 2);
+		write(flags.fd, buf, n);
+	}
+	if(flags.consfd >= 0)
+		write(flags.consfd, buf, n);
+}
+
+void
+closelog(void)
+{
+	flags.ident = NULL;
+	flags.hostname[0] = '\0';
+	flags.option = 0;
+	flags.facility = 0;
+	if(flags.fd >= 0)
+		close(flags.fd);
+	flags.fd = -1;
+	if(flags.consfd >= 0)
+		close(flags.consfd);
+	flags.consfd = -1;
+}
diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/write.c
--- a/sys/src/ape/lib/ap/plan9/write.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/plan9/write.c	Fri Feb 12 10:59:32 2021 -0700
@@ -1,10 +1,11 @@
 #include <errno.h>
+#include <inttypes.h>
 #include <unistd.h>
 #include "lib.h"
 #include "sys9.h"
 
 ssize_t
-write(int d, const void *buf, size_t nbytes)
+pwrite(int d, const void *buf, size_t nbytes, off_t offset)
 {
 	int n;
 
@@ -14,8 +15,14 @@
 	}
 	if(_fdinfo[d].oflags&O_APPEND)
 		_SEEK(d, 0, 2);
-	n = _WRITE(d, buf, nbytes);
+	n = _PWRITE(d, buf, nbytes, offset);
 	if(n < 0)
 		_syserrno();
 	return n;
 }
+
+ssize_t
+write(int d, const void *buf, size_t nbytes)
+{
+	return pwrite(d, buf, nbytes, -1LL);
+}
diff -r bfe93397b157 sys/src/ape/lib/ap/posix/basename.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/ap/posix/basename.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,21 @@
+#include <stdlib.h>
+#include <string.h>
+
+char *
+basename(char *path)
+{
+	int n;
+	char *p;
+
+	if(path == NULL || path[0] == '\0')
+		return ".";
+	n = strlen(path);
+	if(n == 1 && path[0] == '/' && path[1] == '\0')
+		return "/";
+	while(path[n-1] == '/')
+		path[--n] = '\0';
+	p = strrchr(path, '/');
+	if(p == NULL)
+		return path;
+	return p+1;
+}
diff -r bfe93397b157 sys/src/ape/lib/ap/posix/dirname.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/ap/posix/dirname.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,25 @@
+#include <stdlib.h>
+#include <string.h>
+
+char *
+dirname(char *path)
+{
+	int n;
+	char *p;
+
+	if(path == NULL || path[0] == '\0')
+		return ".";
+	n = strlen(path);
+	if(n == 1 && path[0] == '/' && path[1] == '\0')
+		return "/";
+	while(path[n-1] == '/')
+		path[--n] = '\0';
+	p = strrchr(path, '/');
+	if(p == NULL)
+		return ".";
+	while(*p == '/')
+		*p-- = '\0';
+	if(path[0] == '\0')
+		return "/";
+	return path;
+}
diff -r 02e3059af5bc sys/src/ape/lib/ap/posix/getpagesize.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/ap/posix/getpagesize.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,7 @@
+#include <unistd.h>
+
+int
+getpagesize(void)
+{
+	return sysconf(_SC_PAGESIZE);
+}
diff -r bfe93397b157 sys/src/ape/lib/ap/posix/mkfile
--- a/sys/src/ape/lib/ap/posix/mkfile	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/posix/mkfile	Fri Feb 12 10:59:32 2021 -0700
@@ -2,7 +2,10 @@
 <$APE/config
 LIB=/$objtype/lib/ape/libap.a
 OFILES=\
+	basename.$O\
+	dirname.$O\
 	getgrent.$O\
+	getpagesize.$O\
 	getpwent.$O\
 	locale.$O\
 	mkfifo.$O\
diff -r bfe93397b157 sys/src/ape/lib/ap/posix/sysconf.c
--- a/sys/src/ape/lib/ap/posix/sysconf.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/posix/sysconf.c	Fri Feb 12 10:59:32 2021 -0700
@@ -36,6 +36,8 @@
 		return _POSIX_VERSION;
 	case _SC_LOGIN_NAME_MAX:
 		return L_cuserid;
+	case _SC_PAGESIZE:
+		return 4096;
 	}
 	errno = EINVAL;
 	return -1;
diff -r bfe93397b157 sys/src/ape/lib/ap/stdio/_IO_putc.c
--- a/sys/src/ape/lib/ap/stdio/_IO_putc.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/stdio/_IO_putc.c	Fri Feb 12 10:59:32 2021 -0700
@@ -51,8 +51,13 @@
 				f->bufl+=BUFSIZ;
 				f->rp=t+f->bufl;
 			}else{
-				f->state=ERR;
-				return EOF;
+				/*
+				 * [v]snprintf should return number of characters
+				 * which would have written if enough space had enough available.
+				 * however sprintf is not.
+				 */
+				f->state=WR;
+				return c&0xff;
 			}
 		}
 		*f->wp++=c;
diff -r bfe93397b157 sys/src/ape/lib/ap/stdio/sclose.c
--- a/sys/src/ape/lib/ap/stdio/sclose.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/stdio/sclose.c	Fri Feb 12 10:59:32 2021 -0700
@@ -29,6 +29,8 @@
 					goto Error;
 				f->buf=t;
 				f->wp=t+f->bufl;
+			} else if(f->buf==NULL){
+				return NULL;
 			} else {
 				if(f->wp > f->buf)
 					*(f->wp-1) = '\0';
diff -r bfe93397b157 sys/src/ape/lib/ap/stdio/strerror.c
--- a/sys/src/ape/lib/ap/stdio/strerror.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/stdio/strerror.c	Fri Feb 12 10:59:32 2021 -0700
@@ -77,7 +77,17 @@
 
 	/* These added in 1003.1b-1993 */
 	"Operation canceled",
-	"Operation in progress"
+	"Operation in progress",
+
+	/* from research unix */
+	"Text file is busy",			/* ETXTBSY */
+
+	/* Added in more recent 1003.x versions */
+	"Operation already in progress",	/* EALREADY */
+	"Connection reset by peer",		/* ECONNRESET */
+
+	"Value too large for defined data type", /* EOVERFLOW */
+	"Too many symbolic links encountered",	/* ELOOP */
 };
 #define	_IO_nerr	(sizeof sys_errlist/sizeof sys_errlist[0])
 int sys_nerr = _IO_nerr;
diff -r bfe93397b157 sys/src/ape/lib/ap/stdio/vfprintf.c
--- a/sys/src/ape/lib/ap/stdio/vfprintf.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/ap/stdio/vfprintf.c	Fri Feb 12 10:59:32 2021 -0700
@@ -215,7 +215,7 @@
 		}
 		return -1;
 	}
-	return nprint;
+	return ferror(f) ? -1 : nprint;
 }
 
 static int
diff -r bfe93397b157 sys/src/ape/lib/bsd/arc4random.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/bsd/arc4random.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,30 @@
+#include <u.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <libsec.h>
+
+void
+arc4random_buf(void *buf, size_t nbytes)
+{
+	genrandom(buf, nbytes);
+}
+
+unsigned int
+arc4random(void)
+{
+	uint v;
+
+	arc4random_buf(&v, sizeof v);
+	return v;
+}
+
+int
+getentropy(void *buf, size_t len)
+{
+	if (len > 256) {
+		errno = EIO;
+		return -1;
+	}
+	genrandom(buf, len);
+	return 0;
+}
diff -r 8b4cfdf43705 sys/src/ape/lib/bsd/gethostname.c
--- a/sys/src/ape/lib/bsd/gethostname.c	Tue Feb 16 22:04:50 2021 +0100
+++ b/sys/src/ape/lib/bsd/gethostname.c	Tue Feb 16 21:43:40 2021 -0700
@@ -6,7 +6,7 @@
 #include <string.h>
 
 int
-gethostname(char *name, int namelen)
+gethostname(char *name, size_t namelen)
 {
 	int n, fd;
 	char buf[128];
diff -r bfe93397b157 sys/src/ape/lib/bsd/getprogname.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/bsd/getprogname.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,7 @@
+#include <lib9.h>
+
+const char *
+getprogname(void)
+{
+	return argv0;
+}
diff -r bfe93397b157 sys/src/ape/lib/bsd/mkfile
--- a/sys/src/ape/lib/bsd/mkfile	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/bsd/mkfile	Fri Feb 12 10:59:32 2021 -0700
@@ -4,6 +4,7 @@
 LIB=/$objtype/lib/ape/libbsd.a
 OFILES=\
 	accept.$O\
+	arc4random.$O\
 	bcopy.$O\
 	bind.$O\
 	connect.$O\
@@ -19,6 +20,7 @@
 	getopt.$O\
 	getpeername.$O\
 	getprotobyname.$O\
+	getprogname.$O\
 	getservbyaddr.$O\
 	getservbyname.$O\
 	getsockname.$O\
@@ -41,6 +43,7 @@
 	send.$O\
 	sendto.$O\
 	setlinebuf.$O\
+	setprogname.$O\
 	shutdown.$O\
 	_sock_ingetaddr.$O\
 	_sock_ipattr.$O\
diff -r bfe93397b157 sys/src/ape/lib/bsd/setprogname.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/ape/lib/bsd/setprogname.c	Fri Feb 12 10:59:32 2021 -0700
@@ -0,0 +1,7 @@
+#include <lib9.h>
+
+void
+setprogname(const char *progname)
+{
+	argv0 = (char *)progname;
+}
diff -r bfe93397b157 sys/src/ape/lib/bsd/socket.c
--- a/sys/src/ape/lib/bsd/socket.c	Wed Sep 16 20:45:49 2020 +0000
+++ b/sys/src/ape/lib/bsd/socket.c	Fri Feb 12 10:59:32 2021 -0700
@@ -180,9 +180,23 @@
 /*
  * probably should do better than this
  */
-int getsockopt(int, int, int, void *, int *)
+int getsockopt(int fd, int level, int opt, void *v, int *len)
 {
-	return -1;
+	// should we check what fd is socket?
+	USED(fd, len);
+
+	if(level != SOL_SOCKET){
+		errno = ENOPROTOOPT;
+		return -1;
+	}
+	switch(opt){
+	case SO_ERROR:
+		*(int *)v = 0;
+		return 0;
+	default:
+		errno = EINVAL;
+		return -1;
+	}
 }
 
 int setsockopt(int, int, int, void *, int)
diff -r 3540943c10e8 sys/src/cmd/python/pyconfig.h
--- a/sys/src/cmd/python/pyconfig.h	Thu Feb 18 15:13:25 2021 +0100
+++ b/sys/src/cmd/python/pyconfig.h	Thu Feb 18 15:23:05 2021 -0700
@@ -46,7 +46,7 @@
 
 #define SIGWINCH 21	/* for curses */
 
-#define S_ISSOCK S_ISFIFO /* for hg, see /sys/include/ape/sys/stat.h */
+/* #define S_ISSOCK S_ISFIFO for hg, see /sys/include/ape/sys/stat.h */
 
 /* Define if --enable-ipv6 is specified */
 #define ENABLE_IPV6 1

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] Re: [Patch] APE changes (2019 Lufia patches)
  2021-02-26 19:23 ` [9front] " bsdsm
@ 2021-03-01  3:41   ` ori
  2021-03-15  3:20   ` ori
  2021-03-15  3:36   ` ori
  2 siblings, 0 replies; 13+ messages in thread
From: ori @ 2021-03-01  3:41 UTC (permalink / raw)
  To: 9front

Quoth bsdsm@sdf.org:
> 
> Sorry for the delay. Attached are the changes, minus architecture
> specific code, with interests separated as previously discussed.
> 
> Please allow me a moment to address my intentions. My goals are more
> human than software related. I began working on a port of Lufia's
> changes not long after the pull requests. I stopped for some of the
> reasons already discussed: git9, libressl isn't desired, etc. I
> decided to put something together only after finding out that those
> original patches are still in limbo after two years through 9fans. My
> intention being that if even a single line presented was thought
> worthy of import then I could reach out to Lufia and say, 'hey check
> this out, and maybe your next patch can come straight to 9front.'
> 
> Perhaps my goals were misguided. It is not my intention to denigrate
> anyone, 9fan or otherwise.
> 
> As a final note I agree with the comments made previously about
> nitpicking the changes. Please nitpick everything. I just wanted to
> make my intentions clearer.
> 

Thanks for posting -- I'll start looking
over it in the next while; if I don't post
an initial response in 2 weeks, please feel
free to ping.


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] Re: [Patch] APE changes (2019 Lufia patches)
  2021-02-26 19:23 ` [9front] " bsdsm
  2021-03-01  3:41   ` ori
@ 2021-03-15  3:20   ` ori
  2021-03-15  3:36   ` ori
  2 siblings, 0 replies; 13+ messages in thread
From: ori @ 2021-03-15  3:20 UTC (permalink / raw)
  To: 9front

Quoth bsdsm@sdf.org:
> 
> Sorry for the delay. Attached are the changes, minus architecture
> specific code, with interests separated as previously discussed.
> 
> Please allow me a moment to address my intentions. My goals are more
> human than software related. I began working on a port of Lufia's
> changes not long after the pull requests. I stopped for some of the
> reasons already discussed: git9, libressl isn't desired, etc. I
> decided to put something together only after finding out that those
> original patches are still in limbo after two years through 9fans. My
> intention being that if even a single line presented was thought
> worthy of import then I could reach out to Lufia and say, 'hey check
> this out, and maybe your next patch can come straight to 9front.'
> 
> Perhaps my goals were misguided. It is not my intention to denigrate
> anyone, 9fan or otherwise.
> 
> As a final note I agree with the comments made previously about
> nitpicking the changes. Please nitpick everything. I just wanted to
> make my intentions clearer.

Alright, finally started looking over the pthreads patch,
since it's the most interesting.

> 
> diff -r bfe93397b157 sys/include/ape/pthread.h
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/sys/include/ape/pthread.h	Fri Feb 12 10:59:32 2021 -0700
> @@ -0,0 +1,105 @@
> +#ifndef __PTHREAD_H
> +#define __PTHREAD_H
> +#pragma lib "/$M/lib/ape/libpthread.a"
> +
> +#define _LOCK_EXTENSION
> +#define _QLOCK_EXTENSION
> +#include <sys/types.h>
> +#include <unistd.h>
> +#include <lock.h>
> +#include <qlock.h>

We need to be careful here -- this litters the namespace,
in a way that it strictly shouldn't. the types that get
dragged in should, in theory, get prefixed by an '_' or
'__' to prevent namespace clashes.

> +
> +typedef struct pthread_once pthread_once_t;
> +typedef pid_t pthread_t;
> +typedef int pthread_attr_t;
> +typedef struct pthread_mutex pthread_mutex_t;
> +typedef int pthread_mutexattr_t;
> +typedef struct pthread_cond pthread_cond_t;
> +typedef int pthread_condattr_t;
> +typedef struct pthread_key pthread_key_t;
> +
> +enum {
> +	PTHREAD_THREADS_MAX = 1000,

This is an odd limit. As far as I can tell, this is entirely
due to the privates array -- if we move the array contents
into the pthread itself, we can remove the limit.

> +
> +	PTHREAD_CANCEL_DISABLE = 1,
> +};
> +
> +struct pthread_once {
> +	Lock l;
> +	int once;
> +};
> +struct pthread_mutex {
> +	QLock l;
> +
> +	Lock mu;
> +	pthread_t pid;
> +	int ref;
> +	pthread_mutexattr_t attr;
> +};
> +struct pthread_cond {
> +	QLock l;
> +	Rendez r;
> +};
> +struct pthread_key {
> +	Lock l;
> +	void (*destroy)(void*);
> +	struct {
> +		pthread_t	pid;
> +		const void	*p;
> +	} *arenas;
> +	int n;
> +};
> +
> +#define PTHREAD_ONCE_INIT		{ 0 }
> +#define PTHREAD_MUTEX_INITIALIZER	{ 0 }
> +#define PTHREAD_MUTEX_DEFAULT		0
> +#define PTHREAD_MUTEX_NORAML		1

Typo: NORMAL. We should also use an enum here,
and

	#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL

> +#define PTHREAD_MUTEX_RECURSIVE	2
> +#define PTHREAD_COND_INITIALIZER	{ 0 }
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +extern int	pthread_atfork(void (*)(void), void (*)(void), void (*)(void));
> +extern int	pthread_once(pthread_once_t*, void (*)(void));
> +extern pthread_t	pthread_self(void);
> +extern int	pthread_equal(pthread_t, pthread_t);
> +extern int	pthread_create(pthread_t*, pthread_attr_t*, void *(*)(void*), void*);
> +extern void	pthread_exit(void*);
> +extern int	pthread_join(pthread_t, void**);
> +
> +extern int	pthread_mutexattr_init(pthread_mutexattr_t*);
> +extern int	pthread_mutexattr_destroy(pthread_mutexattr_t*);
> +extern int	pthread_mutexattr_settype(pthread_mutexattr_t*, int);
> +
> +extern int	pthread_mutex_init(pthread_mutex_t*, const pthread_mutexattr_t*);
> +extern int	pthread_mutex_lock(pthread_mutex_t*);
> +extern int	pthread_mutex_unlock(pthread_mutex_t*);
> +extern int	pthread_mutex_trylock(pthread_mutex_t*);
> +extern int	pthread_mutex_destroy(pthread_mutex_t*);
> +
> +extern int	pthread_cond_init(pthread_cond_t*, pthread_condattr_t*);
> +extern int	pthread_cond_wait(pthread_cond_t*, pthread_mutex_t*);
> +extern int	pthread_cond_signal(pthread_cond_t*);
> +extern int	pthread_cond_broadcast(pthread_cond_t*);
> +extern int	pthread_cond_destroy(pthread_cond_t*);
> +
> +extern int	pthread_key_create(pthread_key_t*, void (*)(void*));
> +extern int	pthread_key_delete(pthread_key_t);
> +extern void	*pthread_getspecific(pthread_key_t);
> +extern int	pthread_setspecific(pthread_key_t, const void*);
> +
> +extern int	pthread_setcancelstate(int, int*);
> +
> +#ifndef _PTHREAD_SIGMASK
> +#define _PTHREAD_SIGMASK
> +#include <signal.h>
> +extern int	pthread_sigmask(int, const sigset_t*, sigset_t*);
> +#endif
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif
> diff -r bfe93397b157 sys/include/ape/qlock.h
> --- a/sys/include/ape/qlock.h	Wed Sep 16 20:45:49 2020 +0000
> +++ b/sys/include/ape/qlock.h	Fri Feb 12 10:59:32 2021 -0700

To get the namespacing, we should get rid of the _LOCK_EXTENSION
define from pthread.; instead, we should do something like:

	typedef struct _Rendez _Rendez;
	#ifdef _LOCK_EXTENSION
	typedef _Rendez Rendez;
	#endif

> @@ -26,6 +26,14 @@
>  	QLp 	*tail;
>  } QLock;
>  
> +typedef
> +struct Rendez
> +{
> +	QLock	*l;
> +	QLp	*head;
> +	QLp	*tail;
> +} Rendez;
> +
>  #ifdef __cplusplus
>  extern "C" {
>  #endif
> @@ -34,6 +42,10 @@
>  extern	void	qunlock(QLock*);
>  extern	int	canqlock(QLock*);
>  
> +extern	void	rsleep(Rendez*);
> +extern	int	rwakeup(Rendez*);
> +extern	int	rwakeupall(Rendez*);
> +
>  #ifdef __cplusplus
>  }
>  #endif
> diff -r 02e3059af5bc -r 5904c2b92376 sys/src/ape/lib/ap/plan9/qlock.c
> --- a/sys/src/ape/lib/ap/plan9/qlock.c	Thu Feb 11 09:37:36 2021 +0100
> +++ b/sys/src/ape/lib/ap/plan9/qlock.c	Thu Oct 26 02:42:26 2017 +0200
> @@ -22,6 +22,9 @@
>  enum
>  {
>  	Queuing,
> +	QueuingR,
> +	QueuingW,
> +	Sleeping,
>  };
>  
>  /* find a free shared memory location to queue ourselves in */
> @@ -108,3 +112,254 @@
>  	unlock(&q->lock);
>  	return 0;
>  }
> +
> +#if 0

Dead code should be deleted.

> +void
> +rlock(RWLock *q)
> +{
> +	QLp *p, *mp;
> +
> +	lock(&q->lock);
> +	if(q->writer == 0 && q->head == nil){
> +		/* no writer, go for it */
> +		q->readers++;
> +		unlock(&q->lock);
> +		return;
> +	}
> +
> +	mp = getqlp();
> +	p = q->tail;
> +	if(p == 0)
> +		q->head = mp;
> +	else
> +		p->next = mp;
> +	q->tail = mp;
> +	mp->next = nil;
> +	mp->state = QueuingR;
> +	unlock(&q->lock);
> +
> +	/* wait in kernel */
> +	while((*_rendezvousp)(mp, (void*)1) == (void*)~0)
> +		;
> +	mp->inuse = 0;
> +}
> +
> +int
> +canrlock(RWLock *q)
> +{
> +	lock(&q->lock);
> +	if (q->writer == 0 && q->head == nil) {
> +		/* no writer; go for it */
> +		q->readers++;
> +		unlock(&q->lock);
> +		return 1;
> +	}
> +	unlock(&q->lock);
> +	return 0;
> +}
> +
> +void
> +runlock(RWLock *q)
> +{
> +	QLp *p;
> +
> +	lock(&q->lock);
> +	if(q->readers <= 0)
> +		abort();
> +	p = q->head;
> +	if(--(q->readers) > 0 || p == nil){
> +		unlock(&q->lock);
> +		return;
> +	}
> +
> +	/* start waiting writer */
> +	if(p->state != QueuingW)
> +		abort();
> +	q->head = p->next;
> +	if(q->head == 0)
> +		q->tail = 0;
> +	q->writer = 1;
> +	unlock(&q->lock);
> +
> +	/* wakeup waiter */
> +	while((*_rendezvousp)(p, (void*)0) == (void*)~0)
> +		;
> +}
> +
> +void
> +wlock(RWLock *q)
> +{
> +	QLp *p, *mp;
> +
> +	lock(&q->lock);
> +	if(q->readers == 0 && q->writer == 0){
> +		/* noone waiting, go for it */
> +		q->writer = 1;
> +		unlock(&q->lock);
> +		return;
> +	}
> +
> +	/* wait */
> +	p = q->tail;
> +	mp = getqlp();
> +	if(p == nil)
> +		q->head = mp;
> +	else
> +		p->next = mp;
> +	q->tail = mp;
> +	mp->next = nil;
> +	mp->state = QueuingW;
> +	unlock(&q->lock);
> +
> +	/* wait in kernel */
> +	while((*_rendezvousp)(mp, (void*)1) == (void*)~0)
> +		;
> +	mp->inuse = 0;
> +}
> +
> +int
> +canwlock(RWLock *q)
> +{
> +	lock(&q->lock);
> +	if (q->readers == 0 && q->writer == 0) {
> +		/* no one waiting; go for it */
> +		q->writer = 1;
> +		unlock(&q->lock);
> +		return 1;
> +	}
> +	unlock(&q->lock);
> +	return 0;
> +}
> +
> +void
> +wunlock(RWLock *q)
> +{
> +	QLp *p;
> +
> +	lock(&q->lock);
> +	if(q->writer == 0)
> +		abort();
> +	p = q->head;
> +	if(p == nil){
> +		q->writer = 0;
> +		unlock(&q->lock);
> +		return;
> +	}
> +	if(p->state == QueuingW){
> +		/* start waiting writer */
> +		q->head = p->next;
> +		if(q->head == nil)
> +			q->tail = nil;
> +		unlock(&q->lock);
> +		while((*_rendezvousp)(p, (void*)0) == (void*)~0)
> +			;
> +		return;
> +	}
> +
> +	if(p->state != QueuingR)
> +		abort();
> +
> +	/* wake waiting readers */
> +	while(q->head != nil && q->head->state == QueuingR){
> +		p = q->head;
> +		q->head = p->next;
> +		q->readers++;
> +		while((*_rendezvousp)(p, (void*)0) == (void*)~0)
> +			;
> +	}
> +	if(q->head == nil)
> +		q->tail = nil;
> +	q->writer = 0;
> +	unlock(&q->lock);
> +}
> +
> +void
> +rsleep(Rendez *r)
> +{
> +	QLp *t, *me;
> +
> +	if(!r->l)
> +		abort();
> +	lock(&r->l->lock);
> +	/* we should hold the qlock */
> +	if(!r->l->locked)
> +		abort();
> +
> +	/* add ourselves to the wait list */
> +	me = getqlp();
> +	me->state = Sleeping;
> +	if(r->head == nil)
> +		r->head = me;
> +	else
> +		r->tail->next = me;
> +	me->next = nil;
> +	r->tail = me;
> +
> +	/* pass the qlock to the next guy */
> +	t = r->l->head;
> +	if(t){
> +		r->l->head = t->next;
> +		if(r->l->head == nil)
> +			r->l->tail = nil;
> +		unlock(&r->l->lock);
> +		while((*_rendezvousp)(t, (void*)0x12345) == (void*)~0)
> +			;
> +	}else{
> +		r->l->locked = 0;
> +		unlock(&r->l->lock);
> +	}
> +
> +	/* wait for a wakeup */
> +	while((*_rendezvousp)(me, (void*)1) == (void*)~0)
> +		;
> +	me->inuse = 0;
> +}
> +
> +int
> +rwakeup(Rendez *r)
> +{
> +	QLp *t;
> +
> +	/*
> +	 * take off wait and put on front of queue
> +	 * put on front so guys that have been waiting will not get starved
> +	 */
> +	
> +	if(!r->l)
> +		abort();
> +	lock(&r->l->lock);
> +	if(!r->l->locked)
> +		abort();
> +
> +	t = r->head;
> +	if(t == nil){
> +		unlock(&r->l->lock);
> +		return 0;
> +	}
> +
> +	r->head = t->next;
> +	if(r->head == nil)
> +		r->tail = nil;
> +
> +	t->next = r->l->head;
> +	r->l->head = t;
> +	if(r->l->tail == nil)
> +		r->l->tail = t;
> +
> +	t->state = Queuing;
> +	unlock(&r->l->lock);
> +	return 1;
> +}
> +
> +int
> +rwakeupall(Rendez *r)
> +{
> +	int i;
> +
> +	for(i=0; rwakeup(r); i++)
> +		;
> +	return i;
> +}
> +
> +#endif
> diff -r bfe93397b157 sys/src/ape/lib/ap/plan9/fork.c
> --- a/sys/src/ape/lib/ap/plan9/fork.c	Wed Sep 16 20:45:49 2020 +0000
> +++ b/sys/src/ape/lib/ap/plan9/fork.c	Fri Feb 12 10:59:32 2021 -0700
> @@ -2,18 +2,58 @@
>  #include <errno.h>
>  #include <unistd.h>
>  #include "sys9.h"
> +#include <pthread.h>
> +
> +enum {
> +	NHANDLERS = 100
> +};
> +
> +static void (*preparehdlr[NHANDLERS])(void);
> +static void (*parenthdlr[NHANDLERS])(void);
> +static void (*childhdlr[NHANDLERS])(void);
> +static int nprepare;
> +static int nparent;
> +static int nchild;
> +
> +int
> +pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
> +{

I think we need a lock to protect these handlers.

> +	if(prepare != NULL){
> +		if(nprepare >= NHANDLERS)
> +			return ENOMEM;
> +		preparehdlr[nprepare++] = prepare;
> +	}
> +	if(parent != NULL){
> +		if(nparent >= NHANDLERS)
> +			return ENOMEM;
> +		parenthdlr[nparent++] = parent;
> +	}
> +	if(child != NULL){
> +		if(nchild >= NHANDLERS)
> +			return ENOMEM;
> +		childhdlr[nchild++] = child;
> +	}
> +	return 0;
> +}
>  
>  pid_t
>  fork(void)
>  {
> -	int n;
> +	int n, i;
>  
> +	for(i = nprepare-1; i >= 0; i--)
> +		preparehdlr[i]();
>  	n = _RFORK(RFENVG|RFFDG|RFPROC);

Need to be careful -- are there any other places that can fork?
system()?

>  	if(n < 0)
>  		_syserrno();
>  	if(n == 0) {
>  		_detachbuf();
>  		_sessleader = 0;
> +		for(i = 0; i < nchild; i++)
> +			childhdlr[i]();
> +		return 0;
>  	}
> +	for(i = 0; i < nparent; i++)
> +		parenthdlr[i]();
>  	return n;
>  }
> diff -r bfe93397b157 sys/src/ape/lib/pthread/_pthread.c
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/sys/src/ape/lib/pthread/_pthread.c	Fri Feb 12 10:59:32 2021 -0700
> @@ -0,0 +1,83 @@
> +#include <pthread.h>
> +#include <string.h>
> +#include "lib.h"
> +
> +static Lock privlock;
> +static Thread privileges[PTHREAD_THREADS_MAX];

I think that 'privileges' is a misunderstanding of the
'priv' abbreviation -- I think we want 'privates' here.
I also don't think this should be a global array, but
something that we put into the pthread_t itself.

	typedef Thread* pthread_t;

We can even use privalloc() here, but I don't think
we need to.

I think this is an invasive enough change that I can
do another round after it gets done.

> +pthread_join(pthread_t t, void **ret)
> +{
> +	static Lock l;
> +	Thread *priv;
> +	int pid;
> +	Waitmsg *msg;
> +
> +	lock(&l);
> +	priv = _pthreadget(t);
> +	if(priv == NULL){
> +		unlock(&l);
> +		return EINVAL;
> +	}
> +	lock(&priv->l);
> +	if(priv->exited){
> +		emitexits(ret, priv);
> +		unlock(&priv->l);
> +		_pthreadfree(priv);
> +		unlock(&l);
> +		return 0;
> +	}
> +	unlock(&priv->l);
> +
> +	while((msg=_wait()) != NULL){
> +		pid = msg->pid;
> +		free(msg);
> +		if(pid == t)
> +			break;
> +	}

This loop is wrong -- if the threads exit out of
order, then we lose the wait messages and never
join. We'll probably need to set up some sort of
wait notification dance -- possibly an exit cond
var for non-detached threads.

> +	lock(&priv->l);
> +	if(priv->exited){
> +		emitexits(ret, priv);
> +		unlock(&priv->l);
> +		_pthreadfree(priv);
> +		unlock(&l);
> +		return 0;
> +	}
> +	unlock(&priv->l);
> +	unlock(&l);
> +	return ESRCH;
> +}

> diff -r bfe93397b157 sys/src/ape/lib/pthread/mutex_unlock.c
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/sys/src/ape/lib/pthread/mutex_unlock.c	Fri Feb 12 10:59:32 2021 -0700
> @@ -0,0 +1,29 @@
> +#include <pthread.h>
> +#include <errno.h>
> +
> +int
> +pthread_mutex_unlock(pthread_mutex_t *mutex)
> +{
> +	pthread_t pid;
> +
> +	pid = pthread_self();
> +	lock(&mutex->mu);
> +	if(mutex->pid != pid){
> +		unlock(&mutex->mu);
> +		return EPERM;
> +	}
> +	if(mutex->attr == PTHREAD_MUTEX_RECURSIVE){
> +		mutex->ref--;
> +		if(mutex->ref <= 0){

Abort if it's less than zero -- let's not paper over
programmer issues.

> +			mutex->pid = 0;
> +			mutex->ref = 0;
> +			qunlock(&mutex->l);
> +		}
> +		unlock(&mutex->mu);
> +		return 0;
> +	}
> +	mutex->ref--;
> +	qunlock(&mutex->l);
> +	unlock(&mutex->mu);
> +	return 0;
> diff -r bfe93397b157 sys/src/ape/lib/pthread/self.c
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/sys/src/ape/lib/pthread/self.c	Fri Feb 12 10:59:32 2021 -0700
> @@ -0,0 +1,8 @@
> +#include <pthread.h>
> +#include <unistd.h>
> +
> +pthread_t
> +pthread_self(void)
> +{
> +	return getpid();

Obviously, this would need to change to use
privalloc() or similar once we get rid of the
priv array.


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [9front] Re: [Patch] APE changes (2019 Lufia patches)
  2021-02-26 19:23 ` [9front] " bsdsm
  2021-03-01  3:41   ` ori
  2021-03-15  3:20   ` ori
@ 2021-03-15  3:36   ` ori
  2 siblings, 0 replies; 13+ messages in thread
From: ori @ 2021-03-15  3:36 UTC (permalink / raw)
  To: 9front

Quoth bsdsm@sdf.org:
> 	Content-Type: text/x-patch; name="include_next.diff"
> 	Content-Transfer-Encoding: 8bit
> 	Content-Disposition: attachment; filename="include_next.diff"

For this one, I poked around -- and it's used relatively rarely
in code that I don't think we'll have an easy time porting. We
can revisit if we run into something that uses this -- but for
now, not going to worry about it.

I grepped around the systems I have, as well as android code
search ( https://cs.android.com/) to get a feel for how much
it was used.


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2021-03-15  3:41 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-02-22  2:48 [9front] [Patch] APE changes (2019 Lufia patches) bsdsm
2021-02-22  4:14 ` ori
2021-02-22  5:20   ` Jens Staal
2021-02-25  2:15 ` ori
2021-02-25  2:21   ` ori
2021-02-25 16:37     ` Aaron
2021-02-25 18:06       ` ori
2021-02-26 10:28         ` cinap_lenrek
2021-02-26 15:38           ` Lucas Francesco
2021-02-26 19:23 ` [9front] " bsdsm
2021-03-01  3:41   ` ori
2021-03-15  3:20   ` ori
2021-03-15  3:36   ` ori

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).