diff options
Diffstat (limited to 'gnu/libexec/uucp/libunix')
76 files changed, 14489 insertions, 0 deletions
diff --git a/gnu/libexec/uucp/libunix/MANIFEST b/gnu/libexec/uucp/libunix/MANIFEST new file mode 100644 index 000000000000..d64e79922046 --- /dev/null +++ b/gnu/libexec/uucp/libunix/MANIFEST @@ -0,0 +1,76 @@ +Makefile.in +MANIFEST +access.c +addbas.c +app3.c +app4.c +basnam.c +bytfre.c +chmod.c +cohtty.c +cwd.c +cusub.c +detach.c +dirent.c +dup2.c +efopen.c +epopen.c +exists.c +filnam.c +fsusg.c +fsusg.h +ftw.c +getcwd.c +indir.c +init.c +isdir.c +isfork.c +iswait.c +jobid.c +lcksys.c +link.c +locfil.c +lock.c +loctim.c +mail.c +mkdir.c +mkdirs.c +mode.c +move.c +opensr.c +pause.c +picksb.c +portnm.c +proctm.c +recep.c +remove.c +rename.c +rmdir.c +run.c +seq.c +serial.c +signal.c +sindir.c +size.c +sleep.c +splcmd.c +splnam.c +spool.c +spawn.c +srmdir.c +statsb.c +status.c +strerr.c +time.c +tmpfil.c +trunc.c +uacces.c +ufopen.c +ultspl.c +unknwn.c +uuto.c +walk.c +wldcrd.c +work.c +xqtfil.c +xqtsub.c diff --git a/gnu/libexec/uucp/libunix/Makefile b/gnu/libexec/uucp/libunix/Makefile new file mode 100644 index 000000000000..3a6750b17164 --- /dev/null +++ b/gnu/libexec/uucp/libunix/Makefile @@ -0,0 +1,22 @@ +# This subdirectory contains Unix specific support functions. +# $Id: Makefile,v 1.1 1993/08/05 18:23:34 conklin Exp $ + +LIB= unix +SRCS= access.c addbas.c app3.c app4.c basnam.c bytfre.c cwd.c \ + chmod.c cohtty.c cusub.c detach.c efopen.c epopen.c exists.c \ + filnam.c fsusg.c indir.c init.c isdir.c isfork.c iswait.c \ + jobid.c lcksys.c link.c locfil.c lock.c loctim.c mail.c \ + mkdirs.c mode.c move.c opensr.c pause.c picksb.c portnm.c \ + proctm.c recep.c run.c seq.c serial.c signal.c sindir.c size.c \ + sleep.c spawn.c splcmd.c splnam.c spool.c srmdir.c statsb.c \ + status.c time.c tmpfil.c trunc.c uacces.c ufopen.c ultspl.c \ + unknwn.c uuto.c walk.c wldcrd.c work.c xqtfil.c xqtsub.c ftw.c +CFLAGS+= -I$(.CURDIR)/../common_sources \ + -DOWNER=\"$(owner)\" -DSBINDIR=\"$(sbindir)\" + +NOMAN= noman +NOPROFILE= noprofile + +install: + +.include <bsd.lib.mk> diff --git a/gnu/libexec/uucp/libunix/access.c b/gnu/libexec/uucp/libunix/access.c new file mode 100644 index 000000000000..c2c0eef21084 --- /dev/null +++ b/gnu/libexec/uucp/libunix/access.c @@ -0,0 +1,83 @@ +/* access.c + Check access to files by the user and by the daemon. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +/* See if the user has access to a file, to prevent the setuid uucp + and uux programs handing out unauthorized access. */ + +boolean +fsysdep_access (zfile) + const char *zfile; +{ + if (access (zfile, R_OK) == 0) + return TRUE; + ulog (LOG_ERROR, "%s: %s", zfile, strerror (errno)); + return FALSE; +} + +/* See if the daemon has access to a file. This is called if a file + is not being transferred to the spool directory, since if the + daemon does not have access the later transfer will fail. We + assume that the daemon will have the same euid (or egid) as the one + we are running under. If our uid (gid) and euid (egid) are the + same, we assume that we have access. Note that is not important + for security, since the check will be (implicitly) done again when + the daemon tries to transfer the file. This routine should work + whether the UUCP programs are installed setuid or setgid. */ + +boolean +fsysdep_daemon_access (zfile) + const char *zfile; +{ + struct stat s; + uid_t ieuid, iuid, iegid, igid; + boolean fok; + + ieuid = geteuid (); + if (ieuid == 0) + return TRUE; + iuid = getuid (); + iegid = getegid (); + igid = getgid (); + + /* If our effective uid and gid are the same as our real uid and + gid, we assume the daemon will have access to the file. */ + if (ieuid == iuid && iegid == igid) + return TRUE; + + if (stat ((char *) zfile, &s) != 0) + { + ulog (LOG_ERROR, "stat (%s): %s", zfile, strerror (errno)); + return FALSE; + } + + /* If our euid is not our uid, but it is the file's uid, see if the + owner has read access. Otherwise, if our egid is not our gid, + but it is the file's gid, see if the group has read access. + Otherwise, see if the world has read access. We know from the + above check that at least one of our euid and egid are different, + so that is the only one we want to check. This check could fail + if the UUCP programs were both setuid and setgid, but why would + they be? */ + if (ieuid != iuid && ieuid == s.st_uid) + fok = (s.st_mode & S_IRUSR) != 0; + else if (iegid != igid && iegid == s.st_gid) + fok = (s.st_mode & S_IRGRP) != 0; + else + fok = (s.st_mode & S_IROTH) != 0; + + if (! fok) + { + ulog (LOG_ERROR, "%s: cannot be read by daemon", zfile); + return FALSE; + } + + return TRUE; +} diff --git a/gnu/libexec/uucp/libunix/addbas.c b/gnu/libexec/uucp/libunix/addbas.c new file mode 100644 index 000000000000..8597918a3c95 --- /dev/null +++ b/gnu/libexec/uucp/libunix/addbas.c @@ -0,0 +1,50 @@ +/* addbas.c + If we have a directory, add in a base name. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +/* If we have a directory, add a base name. */ + +char * +zsysdep_add_base (zfile, zname) + const char *zfile; + const char *zname; +{ + size_t clen; + const char *zlook; + char *zfree; + char *zret; + +#if DEBUG > 0 + if (*zfile != '/') + ulog (LOG_FATAL, "zsysdep_add_base: %s: Can't happen", zfile); +#endif + + clen = strlen (zfile); + + if (zfile[clen - 1] != '/') + { + if (! fsysdep_directory (zfile)) + return zbufcpy (zfile); + zfree = NULL; + } + else + { + /* Trim out the trailing '/'. */ + zfree = zbufcpy (zfile); + zfree[clen - 1] = '\0'; + zfile = zfree; + } + + zlook = strrchr (zname, '/'); + if (zlook != NULL) + zname = zlook + 1; + + zret = zsysdep_in_dir (zfile, zname); + ubuffree (zfree); + return zret; +} diff --git a/gnu/libexec/uucp/libunix/app3.c b/gnu/libexec/uucp/libunix/app3.c new file mode 100644 index 000000000000..5c0b58938515 --- /dev/null +++ b/gnu/libexec/uucp/libunix/app3.c @@ -0,0 +1,29 @@ +/* app3.c + Stick two directories and a file name together. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" + +char * +zsappend3 (zdir1, zdir2, zfile) + const char *zdir1; + const char *zdir2; + const char *zfile; +{ + size_t cdir1, cdir2, cfile; + char *zret; + + cdir1 = strlen (zdir1); + cdir2 = strlen (zdir2); + cfile = strlen (zfile); + zret = zbufalc (cdir1 + cdir2 + cfile + 3); + memcpy (zret, zdir1, cdir1); + memcpy (zret + cdir1 + 1, zdir2, cdir2); + memcpy (zret + cdir1 + cdir2 + 2, zfile, cfile); + zret[cdir1] = '/'; + zret[cdir1 + cdir2 + 1] = '/'; + zret[cdir1 + cdir2 + cfile + 2] = '\0'; + return zret; +} diff --git a/gnu/libexec/uucp/libunix/app4.c b/gnu/libexec/uucp/libunix/app4.c new file mode 100644 index 000000000000..a3b3787f68fd --- /dev/null +++ b/gnu/libexec/uucp/libunix/app4.c @@ -0,0 +1,33 @@ +/* app4.c + Stick three directories and a file name together. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" + +char * +zsappend4 (zdir1, zdir2, zdir3, zfile) + const char *zdir1; + const char *zdir2; + const char *zdir3; + const char *zfile; +{ + size_t cdir1, cdir2, cdir3, cfile; + char *zret; + + cdir1 = strlen (zdir1); + cdir2 = strlen (zdir2); + cdir3 = strlen (zdir3); + cfile = strlen (zfile); + zret = zbufalc (cdir1 + cdir2 + cdir3 + cfile + 4); + memcpy (zret, zdir1, cdir1); + memcpy (zret + cdir1 + 1, zdir2, cdir2); + memcpy (zret + cdir1 + cdir2 + 2, zdir3, cdir3); + memcpy (zret + cdir1 + cdir2 + cdir3 + 3, zfile, cfile); + zret[cdir1] = '/'; + zret[cdir1 + cdir2 + 1] = '/'; + zret[cdir1 + cdir2 + cdir3 + 2] = '/'; + zret[cdir1 + cdir2 + cdir3 + cfile + 3] = '\0'; + return zret; +} diff --git a/gnu/libexec/uucp/libunix/basnam.c b/gnu/libexec/uucp/libunix/basnam.c new file mode 100644 index 000000000000..c61fcaa8de61 --- /dev/null +++ b/gnu/libexec/uucp/libunix/basnam.c @@ -0,0 +1,22 @@ +/* basnam.c + Get the base name of a file. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +/* Get the base name of a file name. */ + +char * +zsysdep_base_name (zfile) + const char *zfile; +{ + const char *z; + + z = strrchr (zfile, '/'); + if (z != NULL) + return zbufcpy (z + 1); + return zbufcpy (zfile); +} diff --git a/gnu/libexec/uucp/libunix/bytfre.c b/gnu/libexec/uucp/libunix/bytfre.c new file mode 100644 index 000000000000..568eebe0307a --- /dev/null +++ b/gnu/libexec/uucp/libunix/bytfre.c @@ -0,0 +1,19 @@ +/* bytfre.c + Get the number of bytes free on a file system. */ + +#include "uucp.h" + +#include "system.h" +#include "sysdep.h" +#include "fsusg.h" + +long +csysdep_bytes_free (zfile) + const char *zfile; +{ + struct fs_usage s; + + if (get_fs_usage ((char *) zfile, (char *) NULL, &s) < 0) + return -1; + return s.fsu_bavail * (long) 512; +} diff --git a/gnu/libexec/uucp/libunix/chmod.c b/gnu/libexec/uucp/libunix/chmod.c new file mode 100644 index 000000000000..cf69f3eb0140 --- /dev/null +++ b/gnu/libexec/uucp/libunix/chmod.c @@ -0,0 +1,25 @@ +/* chmod.c + Change the mode of a file. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +/* Change the mode of a file. */ + +boolean +fsysdep_change_mode (zfile, imode) + const char *zfile; + unsigned int imode; +{ + if (chmod ((char *) zfile, imode) < 0) + { + ulog (LOG_ERROR, "chmod (%s): %s", zfile, strerror (errno)); + return FALSE; + } + return TRUE; +} diff --git a/gnu/libexec/uucp/libunix/cohtty.c b/gnu/libexec/uucp/libunix/cohtty.c new file mode 100644 index 000000000000..a7aec1cae338 --- /dev/null +++ b/gnu/libexec/uucp/libunix/cohtty.c @@ -0,0 +1,244 @@ +/* Coherent tty locking support. This file was contributed by Bob + Hemedinger <bob@dalek.mwc.com> of Mark Williams Corporation and + lightly edited by Ian Lance Taylor. */ + +/* The bottom part of this file is lock.c. + * This is a hacked lock.c. A full lock.c can be found in the libmisc sources + * under /usr/src/misc.tar.Z. + * + * These are for checking for the existence of locks: + * lockexist(resource) + * lockttyexist(ttyname) + */ + +#include "uucp.h" + +#if HAVE_COHERENT_LOCKFILES + +/* cohtty.c: Given a serial device name, read /etc/ttys and determine if + * the device is already enabled. If it is, disable the + * device and return a string so that it can be re-enabled + * at the completion of the uucico session as part of the + * function that resets the serial device before uucico + * terminates. + * + */ + +#include "uudefs.h" +#include "sysdep.h" + +#include <ctype.h> +#include <access.h> + +/* fscoherent_disable_tty() is a COHERENT specific function. It takes the name + * of a serial device and then scans /etc/ttys for a match. If it finds one, + * it checks the first field of the entry. If it is a '1', then it will disable + * the port and set a flag. The flag will be checked later when uucico wants to + * reset the serial device to see if the device needs to be re-enabled. + */ + +boolean +fscoherent_disable_tty (zdevice, pzenable) + const char *zdevice; + char **pzenable; +{ + + +struct ttyentry{ /* this is an /etc/ttys entry */ + char enable_disable[1]; + char remote_local[1]; + char baud_rate[1]; + char tty_device[16]; +}; + +struct ttyentry sought_tty; + +int x,y,z; /* dummy */ +FILE * infp; /* this will point to /etc/ttys */ +char disable_command[66]; /* this will be the disable command + * passed to the system. + */ +char enable_device[16]; /* this will hold our device name + * to enable. + */ + + *pzenable = NULL; + + strcpy(enable_device,""); /* initialize our strings */ + strcpy(sought_tty.tty_device,""); + + if( (infp = fopen("/etc/ttys","r")) == NULL){ + ulog(LOG_ERROR,"Error: check_disable_tty: failed to open /etc/ttys\n"); + return FALSE; + } + + while (NULL !=(fgets(&sought_tty, sizeof (sought_tty), infp ))){ + sought_tty.tty_device[strlen(sought_tty.tty_device) -1] = '\0'; + strcpy(enable_device,sought_tty.tty_device); + + /* we must strip away the suffix to the com port name or + * we will never find a match. For example, if we are passed + * /dev/com4l to call out with and the port is already enabled, + * 9/10 the port enabled will be com4r. After we strip away the + * suffix of the port found in /etc/ttys, then we can test + * if the base port name appears in the device name string + * passed to us. + */ + + for(z = strlen(sought_tty.tty_device) ; z > 0 ; z--){ + if(isdigit(sought_tty.tty_device[z])){ + break; + } + } + y = strlen(sought_tty.tty_device); + for(x = z+1 ; x <= y; x++){ + sought_tty.tty_device[x] = '\0'; + } + + +/* ulog(LOG_NORMAL,"found device {%s}\n",sought_tty.tty_device); */ + if(strstr(zdevice, sought_tty.tty_device)){ + if(sought_tty.enable_disable[0] == '1'){ + ulog(LOG_NORMAL, "coh_tty: Disabling device %s {%s}\n", + zdevice, sought_tty.tty_device); + sprintf(disable_command, "/etc/disable %s",enable_device); + { + pid_t ipid; + const char *azargs[3]; + int aidescs[3]; + + azargs[0] = "/etc/disable"; + azargs[1] = enable_device; + azargs[2] = NULL; + aidescs[0] = SPAWN_NULL; + aidescs[1] = SPAWN_NULL; + aidescs[2] = SPAWN_NULL; + ipid = ixsspawn (azargs, aidescs, TRUE, + FALSE, + (const char *) NULL, TRUE, + TRUE, + (const char *) NULL, + (const char *) NULL, + (const char *) NULL); + if (ipid < 0) + x = 1; + else + x = ixswait ((unsigned long) ipid, + (const char *) NULL); + } + *pzenable = zbufalc (sizeof "/dev/" + + strlen (enable_device)); + sprintf(*pzenable,"/dev/%s", enable_device); +/* ulog(LOG_NORMAL,"Enable string is {%s}",*pzenable); */ + return(x==0? TRUE : FALSE); /* disable either failed + or succeded */ + }else{ + return FALSE; /* device in tty entry not enabled */ + } + } + } + return FALSE; /* no ttys entry found */ +} + +/* The following is COHERENT 4.0 specific. It is used to test for any + * existing lockfiles on a port which would have been created by init + * when a user logs into a port. + */ + +#define LOCKSIG 9 /* Significant Chars of Lockable Resources. */ +#define LOKFLEN 64 /* Max Length of UUCP Lock File Name. */ + +#define LOCKPRE "LCK.." +#define PIDLEN 6 /* Maximum length of string representing a pid. */ + +#ifndef LOCKDIR +#define LOCKDIR SPOOLDIR +#endif + +/* There is a special version of DEVMASK for the PE multiport driver + * because of the peculiar way it uses the minor device number. For + * all other drivers, the lower 5 bits describe the physical port-- + * the upper 3 bits give attributes for the port. + */ + +#define PE_DRIVER 21 /* Major device number for the PE driver. */ +#define PE_DEVMASK 0x3f /* PE driver minor device mask. */ +#define DEVMASK 0x1f /* Minor device mask. */ + +/* + * Generates a resource name for locking, based on the major number + * and the lower 4 bits of the minor number of the tty device. + * + * Builds the name in buff as two "." separated decimal numbers. + * Returns NULL on failure, buff on success. + */ +static char * +gen_res_name(path, buff) +char *path; +char *buff; +{ + struct stat sbuf; + int status; + + if (0 != (status = stat(path, &sbuf))) { + /* Can't stat the file. */ + return (NULL); + } + + if (PE_DRIVER == major(sbuf.st_rdev)) { + sprintf(buff, "%d.%d", major(sbuf.st_rdev), + PE_DEVMASK & minor(sbuf.st_rdev)); + } else { + sprintf(buff, "%d.%d", major(sbuf.st_rdev), + DEVMASK & minor(sbuf.st_rdev)); + } + + return(buff); +} /* gen_res_name */ + +/* + * lockexist(resource) char *resource; + * + * Test for existance of a lock on the given resource. + * + * Returns: (1) Resource is locked. + * (0) Resource is not locked. + */ + +static boolean +lockexist(resource) +const char *resource; +{ + char lockfn[LOKFLEN]; + + if ( resource == NULL ) + return(0); + sprintf(lockfn, "%s/%s%.*s", LOCKDIR, LOCKPRE, LOCKSIG, resource); + + return (!access(lockfn, AEXISTS)); +} /* lockexist() */ + +/* + * lockttyexist(ttyname) char *ttyname; + * + * Test for existance of a lock on the given tty. + * + * Returns: (1) Resource is locked. + * (0) Resource is not locked. + */ +boolean +lockttyexist(ttyn) +const char *ttyn; +{ + char resource[LOKFLEN]; + char filename[LOKFLEN]; + + sprintf(filename, "/dev/%s", ttyn); + if (NULL == gen_res_name(filename, resource)){ + return(0); /* Non-existent tty can not be locked :-) */ + } + + return(lockexist(resource)); +} /* lockttyexist() */ + +#endif /* HAVE_COHERENT_LOCKFILES */ diff --git a/gnu/libexec/uucp/libunix/cusub.c b/gnu/libexec/uucp/libunix/cusub.c new file mode 100644 index 000000000000..d1110fd5c7b4 --- /dev/null +++ b/gnu/libexec/uucp/libunix/cusub.c @@ -0,0 +1,1163 @@ +/* cusub.c + System dependent routines for cu. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char cusub_rcsid[] = "$Id: cusub.c,v 1.1 1993/08/05 18:23:44 conklin Exp $"; +#endif + +#include "uudefs.h" +#include "uuconf.h" +#include "sysdep.h" +#include "system.h" +#include "cu.h" +#include "conn.h" +#include "prot.h" + +#include <errno.h> + +/* Get definitions for EAGAIN, EWOULDBLOCK and ENODATA. */ +#ifndef EAGAIN +#ifndef EWOULDBLOCK +#define EAGAIN (-1) +#define EWOULDBLOCK (-1) +#else /* defined (EWOULDBLOCK) */ +#define EAGAIN EWOULDBLOCK +#endif /* defined (EWOULDBLOCK) */ +#else /* defined (EAGAIN) */ +#ifndef EWOULDBLOCK +#define EWOULDBLOCK EAGAIN +#endif /* ! defined (EWOULDBLOCK) */ +#endif /* defined (EAGAIN) */ + +#ifndef ENODATA +#define ENODATA EAGAIN +#endif + +/* Local variables. */ + +/* The EOF character, as set by fsysdep_terminal_raw. */ +static char bSeof; + +/* The SUSP character, as set by fsysdep_terminal_raw. */ +static char bStstp; + +/* Local functions. */ + +static const char *zsport_line P((const struct uuconf_port *qport)); +static void uscu_child P((struct sconnection *qconn, int opipe)); +static RETSIGTYPE uscu_alarm P((int isig)); +static int cscu_escape P((char *pbcmd, const char *zlocalname)); +static RETSIGTYPE uscu_alarm_kill P((int isig)); + +/* Return the device name for a port, or NULL if none. */ + +static const char * +zsport_line (qport) + const struct uuconf_port *qport; +{ + const char *zline; + + if (qport == NULL) + return NULL; + + switch (qport->uuconf_ttype) + { + default: + case UUCONF_PORTTYPE_STDIN: + return NULL; + case UUCONF_PORTTYPE_MODEM: + zline = qport->uuconf_u.uuconf_smodem.uuconf_zdevice; + break; + case UUCONF_PORTTYPE_DIRECT: + zline = qport->uuconf_u.uuconf_sdirect.uuconf_zdevice; + break; + case UUCONF_PORTTYPE_TCP: + case UUCONF_PORTTYPE_TLI: + return NULL; + } + + if (zline == NULL) + zline = qport->uuconf_zname; + return zline; +} + +/* Check whether the user has legitimate access to a port. */ + +boolean +fsysdep_port_access (qport) + struct uuconf_port *qport; +{ + const char *zline; + char *zfree; + boolean fret; + + zline = zsport_line (qport); + if (zline == NULL) + return TRUE; + + zfree = NULL; + if (*zline != '/') + { + zfree = zbufalc (sizeof "/dev/" + strlen (zline)); + sprintf (zfree, "/dev/%s", zline); + zline = zfree; + } + + fret = access (zline, R_OK | W_OK) == 0; + ubuffree (zfree); + return fret; +} + +/* Return whether the given port is named by the given line. */ + +boolean +fsysdep_port_is_line (qport, zline) + struct uuconf_port *qport; + const char *zline; +{ + const char *zpline; + char *zfree1, *zfree2; + boolean fret; + + zpline = zsport_line (qport); + if (zpline == NULL) + return FALSE; + + if (strcmp (zline, zpline) == 0) + return TRUE; + + zfree1 = NULL; + zfree2 = NULL; + if (*zline != '/') + { + zfree1 = zbufalc (sizeof "/dev/" + strlen (zline)); + sprintf (zfree1, "/dev/%s", zline); + zline = zfree1; + } + if (*zpline != '/') + { + zfree2 = zbufalc (sizeof "/dev/" + strlen (zpline)); + sprintf (zfree2, "/dev/%s", zpline); + zpline = zfree2; + } + + fret = strcmp (zline, zpline) == 0; + ubuffree (zfree1); + ubuffree (zfree2); + return fret; +} + +/* The cu program wants the system dependent layer to handle the + details of copying data from the communications port to the + terminal. This copying need only be done while executing + fsysdep_cu. On Unix, however, we set up a subprocess to do it all + the time. This subprocess must be controllable via the + fsysdep_cu_copy function. + + We keep a pipe open to the subprocess. When we want it to stop we + send it a signal, and then wait for it to write a byte to us over + the pipe. */ + +/* The subprocess pid. */ +static volatile pid_t iSchild; + +/* The pipe from the subprocess. */ +static int oSpipe; + +/* When we tell the child to stop, it sends this. */ +#define CHILD_STOPPED ('S') + +/* When we tell the child to start, it sends this. */ +#define CHILD_STARTED ('G') + +/* Initialize the subprocess, and have it start copying data. */ + +boolean +fsysdep_cu_init (qconn) + struct sconnection *qconn; +{ + int ai[2]; + + /* Write out anything we may have buffered up during the chat + script. We do this before forking the child only to make it easy + to move the child into a separate executable. */ + while (iPrecend != iPrecstart) + { + char *z; + int c; + + z = abPrecbuf + iPrecstart; + if (iPrecend > iPrecstart) + c = iPrecend - iPrecstart; + else + c = CRECBUFLEN - iPrecstart; + + iPrecstart = (iPrecstart + c) % CRECBUFLEN; + + while (c > 0) + { + int cwrote; + + cwrote = write (1, z, c); + if (cwrote <= 0) + { + if (cwrote < 0) + ulog (LOG_ERROR, "write: %s", strerror (errno)); + else + ulog (LOG_ERROR, "Line disconnected"); + return FALSE; + } + c -= cwrote; + z += cwrote; + } + } + + if (pipe (ai) < 0) + { + ulog (LOG_ERROR, "pipe: %s", strerror (errno)); + return FALSE; + } + + iSchild = ixsfork (); + if (iSchild < 0) + { + ulog (LOG_ERROR, "fork: %s", strerror (errno)); + return FALSE; + } + + if (iSchild == 0) + { + (void) close (ai[0]); + uscu_child (qconn, ai[1]); + /*NOTREACHED*/ + } + + (void) close (ai[1]); + + oSpipe = ai[0]; + + return TRUE; +} + +/* Copy all data from the terminal to the communications port. If we + see an escape character following a newline character, read the + next character and return it. */ + +boolean +fsysdep_cu (qconn, pbcmd, zlocalname) + struct sconnection *qconn; + char *pbcmd; + const char *zlocalname; +{ + boolean fstart; + char b; + int c; + + fstart = TRUE; + + while (TRUE) + { + if (fsysdep_catch ()) + usysdep_start_catch (); + else + { + ulog (LOG_ERROR, (const char *) NULL); + return FALSE; + } + + c = read (0, &b, 1); + + usysdep_end_catch (); + + if (c <= 0) + break; + + if (fstart && b == *zCuvar_escape) + { + c = cscu_escape (pbcmd, zlocalname); + if (c <= 0) + break; + if (*pbcmd != b) + { + write (1, pbcmd, 1); + + /* For Unix, we let the eof character be the same as + '.', and we let the suspend character (if any) be the + same as 'z'. */ + if (*pbcmd == bSeof) + *pbcmd = '.'; + if (*pbcmd == bStstp) + *pbcmd = 'z'; + return TRUE; + } + } + if (! fconn_write (qconn, &b, (size_t) 1)) + return FALSE; + fstart = strchr (zCuvar_eol, b) != NULL; + } + + if (c < 0) + { + if (errno != EINTR) + ulog (LOG_ERROR, "read: %s", strerror (errno)); + else + ulog (LOG_ERROR, (const char *) NULL); + return FALSE; + } + + /* I'm not sure what's best in this case. */ + ulog (LOG_ERROR, "End of file on terminal"); + return FALSE; +} + +/* A SIGALRM handler that sets fScu_alarm and optionally longjmps. */ + +volatile sig_atomic_t fScu_alarm; + +static RETSIGTYPE +uscu_alarm (isig) + int isig; +{ +#if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET + (void) signal (isig, uscu_alarm); +#endif + + fScu_alarm = TRUE; + +#if HAVE_RESTARTABLE_SYSCALLS + if (fSjmp) + longjmp (sSjmp_buf, 1); +#endif +} + +/* We've just seen an escape character. We print the host name, + optionally after a 1 second delay. We read the next character from + the terminal and return it. The 1 second delay on the host name is + mostly to be fancy; it lets ~~ look smoother. */ + +static int +cscu_escape (pbcmd, zlocalname) + char *pbcmd; + const char *zlocalname; +{ + CATCH_PROTECT int c; + + write (1, zCuvar_escape, 1); + + fScu_alarm = FALSE; + usset_signal (SIGALRM, uscu_alarm, TRUE, (boolean *) NULL); + + if (fsysdep_catch ()) + { + usysdep_start_catch (); + alarm (1); + } + + c = 0; + + while (TRUE) + { + if (fScu_alarm) + { + char b; + + fScu_alarm = FALSE; + b = '['; + write (1, &b, 1); + write (1, zlocalname, strlen (zlocalname)); + b = ']'; + write (1, &b, 1); + } + + if (c <= 0) + c = read (0, pbcmd, 1); + if (c >= 0 || errno != EINTR) + { + usysdep_end_catch (); + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + alarm (0); + return c; + } + } +} + +/* A SIGALRM handler which does nothing but send a signal to the child + process and schedule another alarm. POSIX.1 permits kill and alarm + from a signal handler. The reference to static data may or may not + be permissible. */ + +static volatile sig_atomic_t iSsend_sig; + +static RETSIGTYPE +uscu_alarm_kill (isig) + int isig; +{ +#if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET + (void) signal (isig, uscu_alarm_kill); +#endif + + (void) kill (iSchild, iSsend_sig); + + alarm (1); +} + +/* Start or stop copying data from the communications port to the + terminal. We send a signal to the child process to tell it what to + do. Unfortunately, there are race conditions in the child, so we + keep sending it a signal once a second until it responds. We send + SIGUSR1 to make it start copying, and SIGUSR2 to make it stop. */ + +boolean +fsysdep_cu_copy (fcopy) + boolean fcopy; +{ + int ierr; + int c; + + usset_signal (SIGALRM, uscu_alarm_kill, TRUE, (boolean *) NULL); + if (fcopy) + iSsend_sig = SIGUSR1; + else + iSsend_sig = SIGUSR2; + + uscu_alarm_kill (SIGALRM); + + alarm (1); + + while (TRUE) + { + char b; + + c = read (oSpipe, &b, 1); + +#if DEBUG > 1 + if (c > 0) + DEBUG_MESSAGE1 (DEBUG_INCOMING, + "fsysdep_cu_copy: Got '%d'", b); +#endif + + if ((c < 0 && errno != EINTR) + || c == 0 + || (c > 0 && b == (fcopy ? CHILD_STARTED : CHILD_STOPPED))) + break; + + /* If none of the above conditions were true, then we either got + an EINTR error, in which case we probably timed out and the + SIGALRM handler resent the signal, or we read the wrong + character, in which case we will just read again from the + pipe. */ + } + + ierr = errno; + + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + alarm (0); + + if (c > 0) + return TRUE; + + if (c == 0) + ulog (LOG_ERROR, "EOF on child pipe"); + else + ulog (LOG_ERROR, "read: %s", strerror (ierr)); + + return FALSE; +} + +/* Shut down cu by killing the child process. */ + +boolean +fsysdep_cu_finish () +{ + (void) close (oSpipe); + + /* We hit the child with SIGTERM, give it two seconds to die, and + then send a SIGKILL. */ + if (kill (iSchild, SIGTERM) < 0) + { + /* Don't give an error if the child has already died. */ + if (errno != ESRCH) + ulog (LOG_ERROR, "kill: %s", strerror (errno)); + } + + usset_signal (SIGALRM, uscu_alarm_kill, TRUE, (boolean *) NULL); + iSsend_sig = SIGKILL; + alarm (2); + + (void) ixswait ((unsigned long) iSchild, "child"); + + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + alarm (0); + + return TRUE; +} + +/* Code for the child process. */ + +/* This signal handler just records the signal. In this case we only + care about which signal we received most recently. */ + +static volatile sig_atomic_t iSchild_sig; + +static RETSIGTYPE +uscu_child_handler (isig) + int isig; +{ +#if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET + (void) signal (isig, uscu_child_handler); +#endif + + iSchild_sig = isig; + +#if HAVE_RESTARTABLE_SYSCALLS + if (fSjmp) + longjmp (sSjmp_buf, 1); +#endif /* HAVE_RESTARTABLE_SYSCALLS */ +} + +/* The child process. This copies the port to the terminal, except + when it is stopped by a signal. It would be reasonable to write a + separate program for this, probably passing it the port on stdin. + This would reduce the memory requirements, since we wouldn't need a + second process holding all the configuration stuff, and also let it + work reasonably on 680x0 versions of MINIX. */ + +static void +uscu_child (qconn, opipe) + struct sconnection *qconn; + int opipe; +{ + CATCH_PROTECT int oport; + CATCH_PROTECT boolean fstopped, fgot; + CATCH_PROTECT int cwrite; + CATCH_PROTECT char abbuf[1024]; + + /* It would be nice if we could just use fsserial_read, but that + will log signals that we don't want logged. There should be a + generic way to extract the file descriptor from the port. */ + if (qconn->qport == NULL) + oport = 0; + else + { + switch (qconn->qport->uuconf_ttype) + { +#if DEBUG > 0 + default: + ulog (LOG_FATAL, "uscu_child: Can't happen"); + oport = -1; + break; +#endif + case UUCONF_PORTTYPE_STDIN: + oport = 0; + break; + case UUCONF_PORTTYPE_MODEM: + case UUCONF_PORTTYPE_DIRECT: + case UUCONF_PORTTYPE_TCP: + case UUCONF_PORTTYPE_TLI: + oport = ((struct ssysdep_conn *) qconn->psysdep)->o; + break; + } + } + + usset_signal (SIGUSR1, uscu_child_handler, TRUE, (boolean *) NULL); + usset_signal (SIGUSR2, uscu_child_handler, TRUE, (boolean *) NULL); + usset_signal (SIGINT, SIG_IGN, TRUE, (boolean *) NULL); + usset_signal (SIGQUIT, SIG_IGN, TRUE, (boolean *) NULL); + usset_signal (SIGPIPE, SIG_DFL, TRUE, (boolean *) NULL); + usset_signal (SIGTERM, uscu_child_handler, TRUE, (boolean *) NULL); + + fstopped = FALSE; + fgot = FALSE; + iSchild_sig = 0; + cwrite = 0; + + if (fsysdep_catch ()) + usysdep_start_catch (); + + while (TRUE) + { + int isig; + int c; + + /* There is a race condition here between checking the signal + and receiving a new and possibly different one. This is + solved by having the parent resend the signal until it gets a + response. */ + isig = iSchild_sig; + iSchild_sig = 0; + if (isig != 0) + { + char b; + + if (isig == SIGTERM) + exit (EXIT_SUCCESS); + + if (isig == SIGUSR1) + { + fstopped = FALSE; + b = CHILD_STARTED; + } + else + { + fstopped = TRUE; + b = CHILD_STOPPED; + cwrite = 0; + } + + c = write (opipe, &b, 1); + + /* Apparently on some systems we can get EAGAIN here. */ + if (c < 0 && + (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA)) + c = 0; + + if (c <= 0) + { + /* Should we give an error message here? */ + (void) kill (getppid (), SIGHUP); + exit (EXIT_FAILURE); + } + } + + if (fstopped) + pause (); + else if (cwrite > 0) + { + char *zbuf; + + zbuf = abbuf; + while (cwrite > 0) + { + c = write (1, zbuf, cwrite); + + /* Apparently on some systems we can get EAGAIN here. */ + if (c < 0 && + (errno == EAGAIN + || errno == EWOULDBLOCK + || errno == ENODATA)) + c = 0; + + if (c < 0 && errno == EINTR) + break; + if (c <= 0) + { + /* Should we give an error message here? */ + (void) kill (getppid (), SIGHUP); + exit (EXIT_FAILURE); + } + cwrite -= c; + zbuf += c; + } + } + else + { + /* On some systems apparently read will return 0 until + something has been written to the port. We therefore + accept a 0 return until after we have managed to read + something. Setting errno to 0 apparently avoids a + problem on Coherent. */ + errno = 0; + c = read (oport, abbuf, sizeof abbuf); + + /* Apparently on some systems we can get EAGAIN here. */ + if (c < 0 && + (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA)) + c = 0; + + if ((c == 0 && fgot) + || (c < 0 && errno != EINTR)) + { + /* This can be a normal way to exit, depending on just + how the connection is dropped. */ + (void) kill (getppid (), SIGHUP); + exit (EXIT_SUCCESS); + } + if (c > 0) + { + fgot = TRUE; + cwrite = c; + } + } + } +} + +/* Terminal control routines. */ + +/* Whether file descriptor 0 is attached to a terminal or not. */ +static boolean fSterm; + +/* Whether we are doing local echoing. */ +static boolean fSlocalecho; + +/* The original state of the terminal. */ +static sterminal sSterm_orig; + +/* The new state of the terminal. */ +static sterminal sSterm_new; + +#if ! HAVE_BSD_TTY +#ifdef SIGTSTP +/* Whether SIGTSTP is being ignored. */ +static boolean fStstp_ignored; +#endif +#endif + +/* Set the terminal into raw mode. */ + +boolean +fsysdep_terminal_raw (flocalecho) + boolean flocalecho; +{ + fSlocalecho = flocalecho; + + /* This defaults may be overriden below. */ + bSeof = '\004'; + bStstp = '\032'; + + if (! fgetterminfo (0, &sSterm_orig)) + { + fSterm = FALSE; + return TRUE; + } + + fSterm = TRUE; + + sSterm_new = sSterm_orig; + +#if HAVE_BSD_TTY + + /* We use CBREAK mode rather than RAW mode, because RAW mode turns + off all output processing, which we don't want to do. This means + that we have to disable the interrupt characters, which we do by + setting them to -1. */ + bSeof = sSterm_orig.stchars.t_eofc; + + sSterm_new.stchars.t_intrc = -1; + sSterm_new.stchars.t_quitc = -1; + sSterm_new.stchars.t_startc = -1; + sSterm_new.stchars.t_stopc = -1; + sSterm_new.stchars.t_eofc = -1; + sSterm_new.stchars.t_brkc = -1; + + bStstp = sSterm_orig.sltchars.t_suspc; + + sSterm_new.sltchars.t_suspc = -1; + sSterm_new.sltchars.t_dsuspc = -1; + sSterm_new.sltchars.t_rprntc = -1; + sSterm_new.sltchars.t_flushc = -1; + sSterm_new.sltchars.t_werasc = -1; + sSterm_new.sltchars.t_lnextc = -1; + + if (! flocalecho) + { + sSterm_new.stty.sg_flags |= (CBREAK | ANYP); + sSterm_new.stty.sg_flags &=~ (ECHO | CRMOD | TANDEM); + } + else + { + sSterm_new.stty.sg_flags |= (CBREAK | ANYP | ECHO); + sSterm_new.stty.sg_flags &=~ (CRMOD | TANDEM); + } + +#endif /* HAVE_BSD_TTY */ + +#if HAVE_SYSV_TERMIO + + bSeof = sSterm_new.c_cc[VEOF]; + if (! flocalecho) + sSterm_new.c_lflag &=~ (ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHONL); + else + sSterm_new.c_lflag &=~ (ICANON | ISIG); + sSterm_new.c_iflag &=~ (INLCR | IGNCR | ICRNL); + sSterm_new.c_oflag &=~ (OPOST); + sSterm_new.c_cc[VMIN] = 1; + sSterm_new.c_cc[VTIME] = 0; + +#endif /* HAVE_SYSV_TERMIO */ + +#if HAVE_POSIX_TERMIOS + + bSeof = sSterm_new.c_cc[VEOF]; + bStstp = sSterm_new.c_cc[VSUSP]; + if (! flocalecho) + sSterm_new.c_lflag &=~ + (ICANON | IEXTEN | ISIG | ECHO | ECHOE | ECHOK | ECHONL); + else + sSterm_new.c_lflag &=~ (ICANON | IEXTEN | ISIG); + sSterm_new.c_iflag &=~ (INLCR | IGNCR | ICRNL); + sSterm_new.c_oflag &=~ (OPOST); + sSterm_new.c_cc[VMIN] = 1; + sSterm_new.c_cc[VTIME] = 0; + +#endif /* HAVE_POSIX_TERMIOS */ + + if (! fsetterminfo (0, &sSterm_new)) + { + ulog (LOG_ERROR, "Can't set terminal settings: %s", strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/* Restore the terminal to its original setting. */ + +boolean +fsysdep_terminal_restore () +{ + if (! fSterm) + return TRUE; + + if (! fsetterminfo (0, &sSterm_orig)) + { + ulog (LOG_ERROR, "Can't restore terminal: %s", strerror (errno)); + return FALSE; + } + return TRUE; +} + +/* Read a line from the terminal. This will be called after + fsysdep_terminal_raw has been called. */ + +char * +zsysdep_terminal_line (zprompt) + const char *zprompt; +{ + CATCH_PROTECT size_t cbuf = 0; + CATCH_PROTECT char *zbuf = NULL; + CATCH_PROTECT size_t cgot = 0; + + if (zprompt != NULL && *zprompt != '\0') + (void) write (1, zprompt, strlen (zprompt)); + + /* Forgot about any previous SIGINT or SIGQUIT signals we may have + received. We don't worry about the race condition here, since we + can't get these signals from the terminal at the moment and it's + not too likely that somebody else will be sending them to us. */ + afSignal[INDEXSIG_SIGINT] = 0; + afSignal[INDEXSIG_SIGQUIT] = 0; + + if (! fsysdep_terminal_restore ()) + return NULL; + + if (fsysdep_catch ()) + { + usysdep_start_catch (); + cbuf = 0; + zbuf = NULL; + cgot = 0; + } + + while (TRUE) + { + char b; + int c; + + if (afSignal[INDEXSIG_SIGINT] + || afSignal[INDEXSIG_SIGQUIT]) + { + usysdep_end_catch (); + /* Make sure the signal is logged. */ + ulog (LOG_ERROR, (const char *) NULL); + /* Return an empty string. */ + cgot = 0; + break; + } + + /* There's a race here between checking the signals and calling + read. It just means that the user will have to hit ^C more + than once. */ + + c = read (0, &b, 1); + if (c < 0) + { + if (errno == EINTR) + continue; + usysdep_end_catch (); + ulog (LOG_ERROR, "read: %s", strerror (errno)); + (void) fsysdep_terminal_raw (fSlocalecho); + return NULL; + } + if (c == 0) + { + /* I'm not quite sure what to do here. */ + usysdep_end_catch (); + ulog (LOG_ERROR, "EOF on terminal"); + (void) fsysdep_terminal_raw (fSlocalecho); + return NULL; + } + + if (cgot >= cbuf) + { + char *znew; + + cbuf += 64; + znew = zbufalc (cbuf); + if (zbuf != NULL) + { + memcpy (znew, zbuf, cgot); + ubuffree (zbuf); + } + zbuf = znew; + } + + zbuf[cgot] = b; + + ++cgot; + + if (b == '\n') + { + usysdep_end_catch (); + break; + } + } + + if (cgot >= cbuf) + { + char *znew; + + ++cbuf; + znew = zbufalc (cbuf); + if (zbuf != NULL) + { + memcpy (znew, zbuf, cgot); + ubuffree (zbuf); + } + zbuf = znew; + } + + zbuf[cgot] = '\0'; + + if (! fsysdep_terminal_raw (fSlocalecho)) + return NULL; + + return zbuf; +} + +/* Write a line to the terminal with a trailing newline. */ + +boolean +fsysdep_terminal_puts (zline) + const char *zline; +{ + char *zalc, *zprint; + size_t clen; + + if (zline == NULL) + { + zalc = zbufalc (2); + clen = 0; + } + else + { + clen = strlen (zline); + zalc = zbufalc (clen + 2); + memcpy (zalc, zline, clen); + } + + if (fSterm) + { + zalc[clen] = '\r'; + ++clen; + } + zalc[clen] = '\n'; + ++clen; + + zprint = zalc; + while (clen > 0) + { + int c; + + c = write (1, zprint, clen); + if (c <= 0) + { + ubuffree (zalc); + ulog (LOG_ERROR, "write: %s", strerror (errno)); + return FALSE; + } + clen -= c; + zprint += c; + } + + ubuffree (zalc); + + return TRUE; +} + +/* Allow or disallow signals from the terminal. */ + +boolean +fsysdep_terminal_signals (faccept) + boolean faccept; +{ +#if HAVE_BSD_TTY + + if (faccept) + { + sSterm_new.stchars.t_intrc = sSterm_orig.stchars.t_intrc; + sSterm_new.stchars.t_quitc = sSterm_orig.stchars.t_quitc; + } + else + { + sSterm_new.stchars.t_intrc = -1; + sSterm_new.stchars.t_quitc = -1; + } + +#else /* ! HAVE_BSD_TTY */ + + if (faccept) + sSterm_new.c_lflag |= ISIG; + else + sSterm_new.c_lflag &=~ ISIG; + +#ifdef SIGTSTP + /* We only want to get SIGINT and SIGQUIT, not SIGTSTP. This + function will be called with faccept TRUE before it is called + with faccept FALSE, so fStstp_ignored will be correctly + initialized. */ + if (faccept) + usset_signal (SIGTSTP, SIG_IGN, FALSE, &fStstp_ignored); + else if (! fStstp_ignored) + usset_signal (SIGTSTP, SIG_DFL, TRUE, (boolean *) NULL); +#endif + +#endif /* ! HAVE_BSD_TTY */ + + if (! fsetterminfo (0, &sSterm_new)) + { + ulog (LOG_ERROR, "Can't set terminal: %s", strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/* Start up a command, or possibly just a shell. Optionally attach + stdin or stdout to the port. We attach directly to the port, + rather than copying the data ourselves. */ + +boolean +fsysdep_shell (qconn, zcmd, tcmd) + struct sconnection *qconn; + const char *zcmd; + enum tshell_cmd tcmd; +{ + const char *azargs[4]; + int oread, owrite; + int aidescs[3]; + pid_t ipid; + + azargs[0] = "/bin/sh"; + if (zcmd == NULL || *zcmd == '\0') + azargs[1] = NULL; + else + { + azargs[1] = "-c"; + azargs[2] = zcmd; + azargs[3] = NULL; + } + + if (qconn->qport == NULL) + { + oread = 0; + owrite = 1; + } + else + { + switch (qconn->qport->uuconf_ttype) + { + default: + oread = owrite = -1; + break; + case UUCONF_PORTTYPE_STDIN: + oread = 0; + owrite = 1; + break; + case UUCONF_PORTTYPE_MODEM: + case UUCONF_PORTTYPE_DIRECT: + case UUCONF_PORTTYPE_TCP: + case UUCONF_PORTTYPE_TLI: + oread = owrite = ((struct ssysdep_conn *) qconn->psysdep)->o; + break; + } + } + + aidescs[0] = 0; + aidescs[1] = 1; + aidescs[2] = 2; + + if (tcmd == SHELL_STDIN_FROM_PORT || tcmd == SHELL_STDIO_ON_PORT) + aidescs[0] = oread; + if (tcmd == SHELL_STDOUT_TO_PORT || tcmd == SHELL_STDIO_ON_PORT) + aidescs[1] = owrite; + + ipid = ixsspawn (azargs, aidescs, FALSE, TRUE, (const char *) NULL, + FALSE, FALSE, (const char *) NULL, + (const char *) NULL, (const char *) NULL); + if (ipid < 0) + { + ulog (LOG_ERROR, "ixsspawn (/bin/sh): %s", strerror (errno)); + return FALSE; + } + + return ixswait ((unsigned long) ipid, "shell") == 0; +} + +/* Change directories. */ + +boolean +fsysdep_chdir (zdir) + const char *zdir; +{ + if (zdir == NULL || *zdir == '\0') + { + zdir = getenv ("HOME"); + if (zdir == NULL) + { + ulog (LOG_ERROR, "HOME not defined"); + return FALSE; + } + } + if (chdir (zdir) < 0) + { + ulog (LOG_ERROR, "chdir (%s): %s", zdir, strerror (errno)); + return FALSE; + } + return TRUE; +} + +/* Suspend the current process. */ + +boolean +fsysdep_suspend () +{ +#ifndef SIGTSTP + return fsysdep_terminal_puts ("[process suspension not supported]"); +#else + return kill (getpid (), SIGTSTP) == 0; +#endif +} diff --git a/gnu/libexec/uucp/libunix/cwd.c b/gnu/libexec/uucp/libunix/cwd.c new file mode 100644 index 000000000000..433025db6c3a --- /dev/null +++ b/gnu/libexec/uucp/libunix/cwd.c @@ -0,0 +1,55 @@ +/* cwd.c + Routines dealing with the current working directory. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +/* See whether running this file through zsysdep_add_cwd would require + knowing the current working directory. This is used to avoid + determining the cwd if it will not be needed. */ + +boolean +fsysdep_needs_cwd (zfile) + const char *zfile; +{ + return *zfile != '/' && *zfile != '~'; +} + +/* Expand a local file, putting relative pathnames in the current + working directory. Note that ~/file is placed in the public + directory, rather than in the user's home directory. This is + consistent with other UUCP packages. */ + +char * +zsysdep_local_file_cwd (zfile, zpubdir) + const char *zfile; + const char *zpubdir; +{ + if (*zfile == '/') + return zbufcpy (zfile); + else if (*zfile == '~') + return zsysdep_local_file (zfile, zpubdir); + else + return zsysdep_add_cwd (zfile); +} + +/* Add the current working directory to a remote file name. */ + +char * +zsysdep_add_cwd (zfile) + const char *zfile; +{ + if (*zfile == '/' || *zfile == '~') + return zbufcpy (zfile); + + if (zScwd == NULL) + { + ulog (LOG_ERROR, "Can't determine current directory"); + return NULL; + } + + return zsysdep_in_dir (zScwd, zfile); +} diff --git a/gnu/libexec/uucp/libunix/detach.c b/gnu/libexec/uucp/libunix/detach.c new file mode 100644 index 000000000000..73144da001d4 --- /dev/null +++ b/gnu/libexec/uucp/libunix/detach.c @@ -0,0 +1,165 @@ +/* detach.c + Detach from the controlling terminal. + + Copyright (C) 1992, 1993 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "system.h" +#include "sysdep.h" + +#include <errno.h> + +#if HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif + +#ifdef TIOCNOTTY +#define HAVE_TIOCNOTTY 1 +#else +#define HAVE_TIOCNOTTY 0 +#endif + +#if HAVE_FCNTL_H +#include <fcntl.h> +#else +#if HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#if HAVE_BROKEN_SETSID +#undef HAVE_SETSID +#define HAVE_SETSID 0 +#endif + +/* Detach from the controlling terminal. This is called by uucico if + it is calling out to another system, so that it can receive SIGHUP + signals from the port it calls out on. It is also called by uucico + just before it starts uuxqt, so that uuxqt is completely + independent of the terminal. */ + +void +usysdep_detach () +{ + pid_t igrp; + + /* Make sure we are not a process group leader. */ +#if HAVE_BSD_PGRP + igrp = getpgrp (0); +#else + igrp = getpgrp (); +#endif + + if (igrp == getpid ()) + { + boolean fignored; + pid_t ipid; + + /* Ignore SIGHUP, since our process group leader is about to + die. */ + usset_signal (SIGHUP, SIG_IGN, FALSE, &fignored); + + ipid = ixsfork (); + if (ipid < 0) + ulog (LOG_FATAL, "fork: %s", strerror (errno)); + + if (ipid != 0) + _exit (EXIT_SUCCESS); + + /* We'll always wind up as a child of process number 1, right? + Right? We have to wait for our parent to die before + reenabling SIGHUP. */ + while (getppid () != 1) + sleep (1); + + ulog_id (getpid ()); + + /* Restore SIGHUP catcher if it wasn't being ignored. */ + if (! fignored) + usset_signal (SIGHUP, ussignal, TRUE, (boolean *) NULL); + } + +#if ! HAVE_SETSID && HAVE_TIOCNOTTY + /* Lose the original controlling terminal as well as our process + group. If standard input has been reopened to /dev/null, this + will do no harm. If another port has been opened to become the + controlling terminal, it should have been detached when it was + closed. */ + (void) ioctl (0, TIOCNOTTY, (char *) NULL); +#endif /* ! HAVE_SETSID && HAVE_TIOCNOTTY */ + + /* Close stdin, stdout and stderr and reopen them on /dev/null, to + make sure we have no connection at all to the terminal. */ + (void) close (0); + (void) close (1); + (void) close (2); + if (open ((char *) "/dev/null", O_RDONLY) != 0 + || open ((char *) "/dev/null", O_WRONLY) != 1 + || open ((char *) "/dev/null", O_WRONLY) != 2) + ulog (LOG_FATAL, "open (/dev/null): %s", strerror (errno)); + +#if HAVE_SETSID + + /* Under POSIX the setsid call creates a new session for which we + are the process group leader. It also detaches us from our + controlling terminal. */ + if (setsid () < 0) + ulog (LOG_ERROR, "setsid: %s", strerror (errno)); + +#else /* ! HAVE_SETSID */ + +#if ! HAVE_SETPGRP + #error Cannot detach from controlling terminal +#endif + + /* If we don't have setsid, we must use setpgrp. On an old System V + system setpgrp will make us the leader of a new process group and + detach the controlling terminal. On an old BSD system the call + setpgrp (0, 0) will set our process group to 0 so that we can + acquire a new controlling terminal (TIOCNOTTY may or may not have + already done that anyhow). */ +#if HAVE_BSD_SETPGRP + if (setpgrp (0, 0) < 0) +#else + if (setpgrp () < 0) +#endif + { + /* Some systems seem to give EPERM errors inappropriately. */ + if (errno != EPERM) + ulog (LOG_ERROR, "setpgrp: %s", strerror (errno)); + } + +#endif /* ! HAVE_SETSID */ + + /* At this point we have completely detached from our controlling + terminal. The next terminal device we open will probably become + our controlling terminal. */ +} diff --git a/gnu/libexec/uucp/libunix/dirent.c b/gnu/libexec/uucp/libunix/dirent.c new file mode 100644 index 000000000000..83db496cabbd --- /dev/null +++ b/gnu/libexec/uucp/libunix/dirent.c @@ -0,0 +1,123 @@ +/* dirent.c + Replacements for opendir, readdir and closedir for the original + Unix filesystem only. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "sysdep.h" + +#include <errno.h> + +#if HAVE_FCNTL_H +#include <fcntl.h> +#else +#if HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +/* Simple emulations of opendir/readdir/closedir for systems which + have the original format of Unix directories. It's probably better + to get Doug Gwyn's public domain set of emulation functions. */ + +DIR * +opendir (zdir) + const char *zdir; +{ + int o; + struct stat s; + DIR *qret; + + o = open ((char *) zdir, O_RDONLY | O_NOCTTY, 0); + if (o < 0) + return NULL; + if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0 + || fstat (o, &s) < 0) + { + int isave; + + isave = errno; + (void) close (o); + errno = isave; + return NULL; + } + if (! S_ISDIR (s.st_mode)) + { + (void) close (o); + errno = ENOTDIR; + return NULL; + } + qret = (DIR *) xmalloc (sizeof (DIR)); + qret->o = o; + return qret; +} + +struct dirent * +readdir (q) + DIR *q; +{ + struct direct sdir; + int cgot; + + do + { + cgot = read (q->o, &sdir, sizeof (struct direct)); + if (cgot <= 0) + return NULL; + if (cgot != sizeof (struct direct)) + { + errno = ENOENT; + return NULL; + } + } + while (sdir.d_ino == 0); + + strncpy (q->s.d_name, sdir.d_name, DIRSIZ); + q->s.d_name[DIRSIZ] = '\0'; + return &q->s; +} + +int +closedir (q) + DIR *q; +{ + int o; + + o = q->o; + xfree (q); + return close (o); +} diff --git a/gnu/libexec/uucp/libunix/dup2.c b/gnu/libexec/uucp/libunix/dup2.c new file mode 100644 index 000000000000..6a7359fe92e8 --- /dev/null +++ b/gnu/libexec/uucp/libunix/dup2.c @@ -0,0 +1,69 @@ +/* dup2.c + The Unix dup2 function, for systems which only have dup. + + Copyright (C) 1985, 1986, 1987, 1988, 1990 Free Software Foundation, Inc. + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" +#include "sysdep.h" + +#include <errno.h> + +#if HAVE_FCNTL_H +#include <fcntl.h> +#else +#if HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#endif + +/* I basically took this from the emacs 18.57 distribution, although I + cleaned it up a bit and made it POSIX compliant. */ + +int +dup2 (oold, onew) + int oold; + int onew; +{ + if (oold == onew) + return onew; + (void) close (onew); + +#ifdef F_DUPFD + return fcntl (oold, F_DUPFD, onew); +#else + { + int onext, oret, isave; + + onext = dup (oold); + if (onext == onew) + return onext; + if (onext < 0) + return -1; + oret = dup2 (oold, onew); + isave = errno; + (void) close (onext); + errno = isave; + return oret; + } +#endif +} diff --git a/gnu/libexec/uucp/libunix/efopen.c b/gnu/libexec/uucp/libunix/efopen.c new file mode 100644 index 000000000000..7e360b616876 --- /dev/null +++ b/gnu/libexec/uucp/libunix/efopen.c @@ -0,0 +1,132 @@ +/* efopen.c + Open a stdio file with appropriate permissions. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +#if HAVE_FCNTL_H +#include <fcntl.h> +#else +#if HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef O_APPEND +#ifdef FAPPEND +#define O_APPEND FAPPEND +#endif +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +FILE * +esysdep_fopen (zfile, fpublic, fappend, fmkdirs) + const char *zfile; + boolean fpublic; + boolean fappend; + boolean fmkdirs; +{ + int imode; + int o; + FILE *e; + + if (fpublic) + imode = IPUBLIC_FILE_MODE; + else + imode = IPRIVATE_FILE_MODE; + + if (! fappend) + o = creat ((char *) zfile, imode); + else + { +#ifdef O_CREAT + o = open ((char *) zfile, + O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, + imode); +#else + o = open ((char *) zfile, O_WRONLY | O_NOCTTY); + if (o < 0 && errno == ENOENT) + o = creat ((char *) zfile, imode); +#endif /* ! defined (O_CREAT) */ + } + + if (o < 0) + { + if (errno == ENOENT && fmkdirs) + { + if (! fsysdep_make_dirs (zfile, fpublic)) + return NULL; + if (! fappend) + o = creat ((char *) zfile, imode); + else + { +#ifdef O_CREAT + o = open ((char *) zfile, + O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, + imode); +#else + o = creat ((char *) zfile, imode); +#endif + } + } + if (o < 0) + { + ulog (LOG_ERROR, "open (%s): %s", zfile, strerror (errno)); + return NULL; + } + } + +#ifndef O_CREAT +#ifdef O_APPEND + if (fappend) + { + if (fcntl (o, F_SETFL, O_APPEND) < 0) + { + ulog (LOG_ERROR, "fcntl (%s, O_APPEND): %s", zfile, + strerror (errno)); + (void) close (o); + return NULL; + } + } +#endif /* defined (O_APPEND) */ +#endif /* ! defined (O_CREAT) */ + + if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (%s, FD_CLOEXEC): %s", zfile, + strerror (errno)); + (void) close (o); + return NULL; + } + + if (fappend) + e = fdopen (o, (char *) "a"); + else + e = fdopen (o, (char *) "w"); + + if (e == NULL) + { + ulog (LOG_ERROR, "fdopen: %s", strerror (errno)); + (void) close (o); + } + + return e; +} diff --git a/gnu/libexec/uucp/libunix/epopen.c b/gnu/libexec/uucp/libunix/epopen.c new file mode 100644 index 000000000000..dec1b3999d64 --- /dev/null +++ b/gnu/libexec/uucp/libunix/epopen.c @@ -0,0 +1,85 @@ +/* epopen.c + A version of popen that goes through ixsspawn. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "sysdep.h" + +#include <errno.h> + +/* A version of popen that goes through ixsspawn. This actually takes + an array of arguments rather than a string, and takes a boolean + read/write value rather than a string. It sets *pipid to the + process ID of the child. */ + +FILE * +espopen (pazargs, frd, pipid) + const char **pazargs; + boolean frd; + pid_t *pipid; +{ + int aidescs[3]; + pid_t ipid; + FILE *eret; + + if (frd) + { + aidescs[0] = SPAWN_NULL; + aidescs[1] = SPAWN_READ_PIPE; + } + else + { + aidescs[0] = SPAWN_WRITE_PIPE; + aidescs[1] = SPAWN_NULL; + } + aidescs[2] = SPAWN_NULL; + + ipid = ixsspawn (pazargs, aidescs, FALSE, FALSE, + (const char *) NULL, FALSE, TRUE, + (const char *) NULL, (const char *) NULL, + (const char *) NULL); + if (ipid < 0) + return NULL; + + if (frd) + eret = fdopen (aidescs[1], (char *) "r"); + else + eret = fdopen (aidescs[0], (char *) "w"); + if (eret == NULL) + { + int ierr; + + ierr = errno; + (void) close (frd ? aidescs[1] : aidescs[0]); + (void) kill (ipid, SIGKILL); + (void) ixswait ((unsigned long) ipid, (const char *) NULL); + errno = ierr; + return NULL; + } + + *pipid = ipid; + + return eret; +} diff --git a/gnu/libexec/uucp/libunix/exists.c b/gnu/libexec/uucp/libunix/exists.c new file mode 100644 index 000000000000..9473922f0d53 --- /dev/null +++ b/gnu/libexec/uucp/libunix/exists.c @@ -0,0 +1,16 @@ +/* exists.c + Check whether a file exists. */ + +#include "uucp.h" + +#include "sysdep.h" +#include "system.h" + +boolean +fsysdep_file_exists (zfile) + const char *zfile; +{ + struct stat s; + + return stat ((char *) zfile, &s) == 0; +} diff --git a/gnu/libexec/uucp/libunix/filnam.c b/gnu/libexec/uucp/libunix/filnam.c new file mode 100644 index 000000000000..62054767b89a --- /dev/null +++ b/gnu/libexec/uucp/libunix/filnam.c @@ -0,0 +1,376 @@ +/* filnam.c + Get names to use for UUCP files. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +#if HAVE_FCNTL_H +#include <fcntl.h> +#else +#if HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +/* We need a definition for SEEK_SET. */ + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +/* External functions. */ +#ifndef lseek +extern off_t lseek (); +#endif + +#define ZCHARS \ + "0123456789ABCDEFGHIJKLMNOPQRTSUVWXYZabcdefghijklmnopqrstuvwxyz" + +/* Local functions. */ + +static boolean fscmd_seq P((const char *zsystem, char *zseq)); +static char *zsfile_name P((int btype, const char *zsystem, + const char *zlocalname, int bgrade, + boolean fxqt, char *ztname, char *zdname, + char *zxname)); + +/* Get a new command sequence number (this is not a sequence number to + be used for communicating with another system, but a sequence + number to be used when generating the name of a command file). + The sequence number is placed into zseq, which should be five + characters long. */ + +static boolean +fscmd_seq (zsystem, zseq) + const char *zsystem; + char *zseq; +{ + boolean ferr; + char *zfree; + const char *zfile; + int o; + int i; + + /* Lock the sequence file. This may not be correct for all systems, + but it only matters if the system UUCP and this UUCP are running + at the same time. */ + while (! fsdo_lock ("LCK..SEQ", TRUE, &ferr)) + { + if (ferr || FGOT_SIGNAL ()) + return FALSE; + sleep (5); + } + + zfree = NULL; + +#if SPOOLDIR_V2 || SPOOLDIR_BSD42 || SPOOLDIR_BSD43 + zfile = "SEQF"; +#endif +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 + zfree = zsysdep_in_dir (".Sequence", zsystem); + zfile = zfree; +#endif +#if SPOOLDIR_ULTRIX + if (! fsultrix_has_spool (zsystem)) + zfile = "sys/DEFAULT/.SEQF"; + else + { + zfree = zsappend3 ("sys", zsystem, ".SEQF"); + zfile = zfree; + } +#endif /* SPOOLDIR_ULTRIX */ +#if SPOOLDIR_TAYLOR + zfree = zsysdep_in_dir (zsystem, "SEQF"); + zfile = zfree; +#endif /* SPOOLDIR_TAYLOR */ + +#ifdef O_CREAT + o = open ((char *) zfile, O_RDWR | O_CREAT | O_NOCTTY, IPUBLIC_FILE_MODE); +#else + o = open ((char *) zfile, O_RDWR | O_NOCTTY); + if (o < 0 && errno == ENOENT) + { + o = creat ((char *) zfile, IPUBLIC_FILE_MODE); + if (o >= 0) + { + (void) close (o); + o = open ((char *) zfile, O_RDWR | O_NOCTTY); + } + } +#endif + + if (o < 0) + { + if (errno == ENOENT) + { + if (! fsysdep_make_dirs (zfile, FALSE)) + { + (void) fsdo_unlock ("LCK..SEQ", TRUE); + return FALSE; + } +#ifdef O_CREAT + o = open ((char *) zfile, + O_RDWR | O_CREAT | O_NOCTTY, + IPUBLIC_FILE_MODE); +#else + o = creat ((char *) zfile, IPUBLIC_FILE_MODE); + if (o >= 0) + { + (void) close (o); + o = open ((char *) zfile, O_RDWR | O_NOCTTY); + } +#endif + } + if (o < 0) + { + ulog (LOG_ERROR, "open (%s): %s", zfile, strerror (errno)); + (void) fsdo_unlock ("LCK..SEQ", TRUE); + return FALSE; + } + } + + if (read (o, zseq, CSEQLEN) != CSEQLEN) + strcpy (zseq, "0000"); + zseq[CSEQLEN] = '\0'; + + /* We must add one to the sequence number and return the new value. + On Ultrix, arbitrary characters are allowed in the sequence + number. On other systems, the sequence number apparently must be + in hex. */ +#if SPOOLDIR_V2 || SPOOLDIR_BSD42 || SPOOLDIR_BSD43 || SPOOLDIR_HDB || SPOOLDIR_SVR4 + i = (int) strtol (zseq, (char **) NULL, 16); + ++i; + if (i > 0xffff) + i = 0; + /* The sprintf argument has CSEQLEN built into it. */ + sprintf (zseq, "%04x", (unsigned int) i); +#endif +#if SPOOLDIR_ULTRIX || SPOOLDIR_TAYLOR + for (i = CSEQLEN - 1; i >= 0; i--) + { + const char *zdig; + + zdig = strchr (ZCHARS, zseq[i]); + if (zdig == NULL || zdig[0] == '\0' || zdig[1] == '\0') + zseq[i] = '0'; + else + { + zseq[i] = zdig[1]; + break; + } + } +#endif /* SPOOLDIR_ULTRIX || SPOOLDIR_TAYLOR */ + + if (lseek (o, (off_t) 0, SEEK_SET) < 0 + || write (o, zseq, CSEQLEN) != CSEQLEN + || close (o) < 0) + { + ulog (LOG_ERROR, "lseek or write or close: %s", strerror (errno)); + (void) close (o); + (void) fsdo_unlock ("LCK..SEQ", TRUE); + return FALSE; + } + + (void) fsdo_unlock ("LCK..SEQ", TRUE); + + return TRUE; +} + +/* Get the name of a command or data file for a remote system. The + btype argument should be C for a command file or D for a data file. + If the grade of a data file is X, it is assumed that this is going + to become an execute file on some other system. The zsystem + argument is the system that the file will be transferred to. The + ztname argument will be set to a file name that could be passed to + zsysdep_spool_file_name. The zdname argument, if not NULL, will be + set to a data file name appropriate for the remote system. The + zxname argument, if not NULL, will be set to the name of an execute + file on the remote system. None of the names will be more than 14 + characters long. */ + +/*ARGSUSED*/ +static char * +zsfile_name (btype, zsystem, zlocalname, bgrade, fxqt, ztname, zdname, zxname) + int btype; + const char *zsystem; + const char *zlocalname; + int bgrade; + boolean fxqt; + char *ztname; + char *zdname; + char *zxname; +{ + char abseq[CSEQLEN + 1]; + char absimple[11 + CSEQLEN]; + char *zname; + + if (zlocalname == NULL) + zlocalname = zSlocalname; + + while (TRUE) + { + if (! fscmd_seq (zsystem, abseq)) + return NULL; + + if (btype == 'C') + { +#if ! SPOOLDIR_TAYLOR + sprintf (absimple, "C.%.7s%c%s", zsystem, bgrade, abseq); +#else + sprintf (absimple, "C.%c%s", bgrade, abseq); +#endif + } + else if (btype == 'D') + { + /* This name doesn't really matter that much; it's just the + name we use on the local system. The name we use on the + remote system, which we return in zdname, should contain + our system name so that remote UUCP's running SPOOLDIR_V2 + and the like can distinguish while files come from which + systems. */ +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 + sprintf (absimple, "D.%.7s%c%s", zsystem, bgrade, abseq); +#else /* ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 */ +#if ! SPOOLDIR_TAYLOR + sprintf (absimple, "D.%.7s%c%s", zlocalname, bgrade, abseq); +#else /* SPOOLDIR_TAYLOR */ + if (fxqt) + sprintf (absimple, "D.X%s", abseq); + else + sprintf (absimple, "D.%s", abseq); +#endif /* SPOOLDIR_TAYLOR */ +#endif /* ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 */ + } +#if DEBUG > 0 + else + ulog (LOG_FATAL, "zsfile_name: Can't happen"); +#endif + + zname = zsfind_file (absimple, zsystem, bgrade); + if (zname == NULL) + return NULL; + + if (! fsysdep_file_exists (zname)) + break; + + ubuffree (zname); + } + + if (ztname != NULL) + strcpy (ztname, absimple); + + if (zdname != NULL) + sprintf (zdname, "D.%.7s%c%s", zlocalname, bgrade, abseq); + + if (zxname != NULL) + sprintf (zxname, "X.%.7s%c%s", zlocalname, bgrade, abseq); + + return zname; +} + +/* Return a name to use for a data file to be copied to another + system. The name returned will be for a real file. The zlocalname + argument is the local name as seen by the remote system, the bgrade + argument is the file grade, and the fxqt argument is TRUE if this + file will become an execution file. The ztname argument, if not + NULL, will be set to a name that could be passed to + zsysdep_spool_file_name to get back the return value of this + function. The zdname argument, if not NULL, will be set to a name + that the file could be given on another system. The zxname + argument, if not NULL, will be set to a name for an execute file on + another system. */ + +char * +zsysdep_data_file_name (qsys, zlocalname, bgrade, fxqt, ztname, zdname, + zxname) + const struct uuconf_system *qsys; + const char *zlocalname; + int bgrade; + boolean fxqt; + char *ztname; + char *zdname; + char *zxname; +{ + return zsfile_name ('D', qsys->uuconf_zname, zlocalname, bgrade, fxqt, + ztname, zdname, zxname); +} + +/* Get a command file name. */ + +char * +zscmd_file (qsys, bgrade) + const struct uuconf_system *qsys; + int bgrade; +{ + return zsfile_name ('C', qsys->uuconf_zname, (const char *) NULL, + bgrade, FALSE, (char *) NULL, (char *) NULL, + (char *) NULL); +} + +/* Return a name for an execute file to be created locally. This is + used by uux to execute a command locally with remote files. */ + +char * +zsysdep_xqt_file_name () +{ + char abseq[CSEQLEN + 1]; + char absx[11 + CSEQLEN]; + char *zname; + + while (TRUE) + { + if (! fscmd_seq (zSlocalname, abseq)) + return NULL; + + sprintf (absx, "X.%.7sX%s", zSlocalname, abseq); + + zname = zsfind_file (absx, zSlocalname, -1); + if (zname == NULL) + return NULL; + + if (! fsysdep_file_exists (zname)) + break; + + ubuffree (zname); + } + + return zname; +} diff --git a/gnu/libexec/uucp/libunix/fsusg.c b/gnu/libexec/uucp/libunix/fsusg.c new file mode 100644 index 000000000000..e2b40a8ad5a5 --- /dev/null +++ b/gnu/libexec/uucp/libunix/fsusg.c @@ -0,0 +1,231 @@ +/* fsusage.c -- return space usage of mounted filesystems + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file was modified slightly by Ian Lance Taylor, December 1992, + for use with Taylor UUCP. */ + +#include "uucp.h" +#include "sysdep.h" +#include "fsusg.h" + +int statfs (); + +#if STAT_STATFS2_BSIZE +#ifndef _IBMR2 /* 4.3BSD, SunOS 4, HP-UX, AIX PS/2. */ +#include <sys/vfs.h> +#endif +#endif + +#if STAT_STATFS2_FSIZE /* 4.4BSD. */ +#include <sys/mount.h> +#endif + +#if STAT_STATFS2_FS_DATA /* Ultrix. */ +#include <sys/param.h> +#include <sys/mount.h> +#endif + +#if STAT_USTAT /* SVR2 and others. */ +#include <ustat.h> +#endif + +#if STAT_STATFS4 /* SVR3, Dynix, Irix. */ +#include <sys/statfs.h> +#endif +#ifdef _AIX +#ifdef _IBMR2 /* AIX RS6000. */ +#include <sys/statfs.h> +#endif +#endif + +#ifdef _AIX +#ifdef _I386 /* AIX PS/2. */ +#include <sys/stat.h> +#include <sys/dustat.h> +#endif +#endif + +#if STAT_STATVFS /* SVR4. */ +#include <sys/statvfs.h> +int statvfs (); +#endif + +#define STAT_NONE 0 + +#if ! STAT_STATVFS +#if ! STAT_STATFS2_BSIZE +#if ! STAT_STATFS2_FSIZE +#if ! STAT_STATFS2_FS_DATA +#if ! STAT_STATFS4 +#if ! STAT_USTAT +#undef STAT_NONE +#define STAT_NONE 1 +#endif +#endif +#endif +#endif +#endif +#endif + +#if ! STAT_NONE + +/* Return the number of TOSIZE-byte blocks used by + BLOCKS FROMSIZE-byte blocks, rounding up. */ + +static long +adjust_blocks (blocks, fromsize, tosize) + long blocks; + int fromsize, tosize; +{ + if (fromsize == tosize) /* E.g., from 512 to 512. */ + return blocks; + else if (fromsize > tosize) /* E.g., from 2048 to 512. */ + return blocks * (fromsize / tosize); + else /* E.g., from 256 to 512. */ + return (blocks + 1) / (tosize / fromsize); +} + +#endif + +/* Fill in the fields of FSP with information about space usage for + the filesystem on which PATH resides. + DISK is the device on which PATH is mounted, for space-getting + methods that need to know it. + Return 0 if successful, -1 if not. */ + +int +get_fs_usage (path, disk, fsp) + char *path, *disk; + struct fs_usage *fsp; +{ +#if STAT_NONE + return -1; +#endif + +#if STAT_STATFS2_FS_DATA /* Ultrix. */ + struct fs_data fsd; + + if (statfs (path, &fsd) != 1) + return -1; +#define convert_blocks(b) adjust_blocks ((b), 1024, 512) + fsp->fsu_blocks = convert_blocks (fsd.fd_req.btot); + fsp->fsu_bfree = convert_blocks (fsd.fd_req.bfree); + fsp->fsu_bavail = convert_blocks (fsd.fd_req.bfreen); + fsp->fsu_files = fsd.fd_req.gtot; + fsp->fsu_ffree = fsd.fd_req.gfree; +#endif + +#if STAT_STATFS2_BSIZE /* 4.3BSD, SunOS 4, HP-UX, AIX. */ + struct statfs fsd; + + if (statfs (path, &fsd) < 0) + return -1; +#define convert_blocks(b) adjust_blocks ((b), fsd.f_bsize, 512) +#endif + +#if STAT_STATFS2_FSIZE /* 4.4BSD. */ + struct statfs fsd; + + if (statfs (path, &fsd) < 0) + return -1; +#define convert_blocks(b) adjust_blocks ((b), fsd.f_fsize, 512) +#endif + +#if STAT_STATFS4 /* SVR3, Dynix, Irix. */ + struct statfs fsd; + + if (statfs (path, &fsd, sizeof fsd, 0) < 0) + return -1; + /* Empirically, the block counts on most SVR3 and SVR3-derived + systems seem to always be in terms of 512-byte blocks, + no matter what value f_bsize has. */ +#define convert_blocks(b) (b) +#ifndef _SEQUENT_ /* _SEQUENT_ is DYNIX/ptx. */ +#define f_bavail f_bfree +#endif +#endif + +#if STAT_STATVFS /* SVR4. */ + struct statvfs fsd; + + if (statvfs (path, &fsd) < 0) + return -1; + /* f_frsize isn't guaranteed to be supported. */ +#define convert_blocks(b) \ + adjust_blocks ((b), fsd.f_frsize ? fsd.f_frsize : fsd.f_bsize, 512) +#endif + +#if STAT_USTAT + { + struct stat sstat; + struct ustat s; + + if (stat (path, &sstat) < 0 + || ustat (sstat.st_dev, &s) < 0) + return -1; + fsp->fsu_blocks = -1; + fsp->fsu_bfree = f_tfree; + fsp->fsu_bavail = f_tfree; + fsp->fsu_files = -1; + fsp->fsu_ffree = -1; + } +#endif + +#if ! STAT_STATFS2_FS_DATA /* ! Ultrix */ +#if ! STAT_USTAT +#if ! STAT_NONE + fsp->fsu_blocks = convert_blocks (fsd.f_blocks); + fsp->fsu_bfree = convert_blocks (fsd.f_bfree); + fsp->fsu_bavail = convert_blocks (fsd.f_bavail); + fsp->fsu_files = fsd.f_files; + fsp->fsu_ffree = fsd.f_ffree; +#endif +#endif +#endif + + return 0; +} + +#ifdef _AIX +#ifdef _I386 +/* AIX PS/2 does not supply statfs. */ + +int +statfs (path, fsb) + char *path; + struct statfs *fsb; +{ + struct stat stats; + struct dustat fsd; + + if (stat (path, &stats)) + return -1; + if (dustat (stats.st_dev, 0, &fsd, sizeof (fsd))) + return -1; + fsb->f_type = 0; + fsb->f_bsize = fsd.du_bsize; + fsb->f_blocks = fsd.du_fsize - fsd.du_isize; + fsb->f_bfree = fsd.du_tfree; + fsb->f_bavail = fsd.du_tfree; + fsb->f_files = (fsd.du_isize - 2) * fsd.du_inopb; + fsb->f_ffree = fsd.du_tinode; + fsb->f_fsid.val[0] = fsd.du_site; + fsb->f_fsid.val[1] = fsd.du_pckno; + return 0; +} +#endif +#endif /* _AIX && _I386 */ diff --git a/gnu/libexec/uucp/libunix/fsusg.h b/gnu/libexec/uucp/libunix/fsusg.h new file mode 100644 index 000000000000..8d4d054cb5ac --- /dev/null +++ b/gnu/libexec/uucp/libunix/fsusg.h @@ -0,0 +1,31 @@ +/* fsusage.h -- declarations for filesystem space usage info + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This files was modified slightly by Ian Lance Taylor for use with + Taylor UUCP. */ + +/* Space usage statistics for a filesystem. Blocks are 512-byte. */ +struct fs_usage +{ + long fsu_blocks; /* Total blocks. */ + long fsu_bfree; /* Free blocks available to superuser. */ + long fsu_bavail; /* Free blocks available to non-superuser. */ + long fsu_files; /* Total file nodes. */ + long fsu_ffree; /* Free file nodes. */ +}; + +extern int get_fs_usage P((char *path, char *disk, struct fs_usage *fsp)); diff --git a/gnu/libexec/uucp/libunix/ftw.c b/gnu/libexec/uucp/libunix/ftw.c new file mode 100644 index 000000000000..c3372b53ca48 --- /dev/null +++ b/gnu/libexec/uucp/libunix/ftw.c @@ -0,0 +1,250 @@ +/* Copyright (C) 1991, 1992 Free Software Foundation, Inc. +This file is part of the GNU C Library. +Contributed by Ian Lance Taylor (ian@airs.com). + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. + +Modified by Ian Lanc Taylor for Taylor UUCP, June 1992. */ + +#include "uucp.h" + +#include "sysdep.h" + +#include <errno.h> + +#if HAVE_LIMITS_H +#include <limits.h> +#endif + +#if HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#if HAVE_OPENDIR +#if HAVE_DIRENT_H +#include <dirent.h> +#else /* ! HAVE_DIRENT_H */ +#include <sys/dir.h> +#define dirent direct +#endif /* ! HAVE_DIRENT_H */ +#endif /* HAVE_OPENDIR */ + +#if HAVE_FTW_H +#include <ftw.h> +#endif + +#ifndef PATH_MAX +#ifdef MAXPATHLEN +#define PATH_MAX MAXPATHLEN +#else +#define PATH_MAX 1024 +#endif +#endif + +/* Traverse one level of a directory tree. */ + +static int +ftw_dir (dirs, level, descriptors, dir, len, func) + DIR **dirs; + int level; + int descriptors; + char *dir; + size_t len; + int (*func) P((const char *file, const struct stat *status, int flag)); +{ + int got; + struct dirent *entry; + + got = 0; + + errno = 0; + + while ((entry = readdir (dirs[level])) != NULL) + { + size_t namlen; + struct stat s; + int flag, ret, newlev; + + ++got; + + namlen = strlen (entry->d_name); + if (entry->d_name[0] == '.' + && (namlen == 1 || + (namlen == 2 && entry->d_name[1] == '.'))) + { + errno = 0; + continue; + } + + if (namlen + len + 1 > PATH_MAX) + { +#ifdef ENAMETOOLONG + errno = ENAMETOOLONG; +#else + errno = ENOMEM; +#endif + return -1; + } + + dir[len] = '/'; + memcpy ((dir + len + 1), entry->d_name, namlen + 1); + + if (stat (dir, &s) < 0) + { + if (errno != EACCES) + return -1; + flag = FTW_NS; + } + else if (S_ISDIR (s.st_mode)) + { + newlev = (level + 1) % descriptors; + + if (dirs[newlev] != NULL) + closedir (dirs[newlev]); + + dirs[newlev] = opendir (dir); + if (dirs[newlev] != NULL) + flag = FTW_D; + else + { + if (errno != EACCES) + return -1; + flag = FTW_DNR; + } + } + else + flag = FTW_F; + + ret = (*func) (dir, &s, flag); + + if (flag == FTW_D) + { + if (ret == 0) + ret = ftw_dir (dirs, newlev, descriptors, dir, + namlen + len + 1, func); + if (dirs[newlev] != NULL) + { + int save; + + save = errno; + closedir (dirs[newlev]); + errno = save; + dirs[newlev] = NULL; + } + } + + if (ret != 0) + return ret; + + if (dirs[level] == NULL) + { + int skip; + + dir[len] = '\0'; + dirs[level] = opendir (dir); + if (dirs[level] == NULL) + return -1; + skip = got; + while (skip-- != 0) + { + errno = 0; + if (readdir (dirs[level]) == NULL) + return errno == 0 ? 0 : -1; + } + } + + errno = 0; + } + + return errno == 0 ? 0 : -1; +} + +/* Call a function on every element in a directory tree. */ + +int +ftw (dir, func, descriptors) + const char *dir; + int (*func) P((const char *file, const struct stat *status, int flag)); + int descriptors; +{ + DIR **dirs; + int c; + DIR **p; + size_t len; + char buf[PATH_MAX + 1]; + struct stat s; + int flag, ret; + + if (descriptors <= 0) + descriptors = 1; + + dirs = (DIR **) malloc (descriptors * sizeof (DIR *)); + if (dirs == NULL) + return -1; + c = descriptors; + p = dirs; + while (c-- != 0) + *p++ = NULL; + + len = strlen (dir); + memcpy (buf, dir, len + 1); + + if (stat (dir, &s) < 0) + { + if (errno != EACCES) + { + free ((pointer) dirs); + return -1; + } + flag = FTW_NS; + } + else if (S_ISDIR (s.st_mode)) + { + dirs[0] = opendir (dir); + if (dirs[0] != NULL) + flag = FTW_D; + else + { + if (errno != EACCES) + { + free ((pointer) dirs); + return -1; + } + flag = FTW_DNR; + } + } + else + flag = FTW_F; + + ret = (*func) (buf, &s, flag); + + if (flag == FTW_D) + { + if (ret == 0) + ret = ftw_dir (dirs, 0, descriptors, buf, len, func); + if (dirs[0] != NULL) + { + int save; + + save = errno; + closedir (dirs[0]); + errno = save; + } + } + + free ((pointer) dirs); + return ret; +} diff --git a/gnu/libexec/uucp/libunix/getcwd.c b/gnu/libexec/uucp/libunix/getcwd.c new file mode 100644 index 000000000000..d3623bd2cd82 --- /dev/null +++ b/gnu/libexec/uucp/libunix/getcwd.c @@ -0,0 +1,59 @@ +/* getcwd.c + Replacement for the getcwd function that just calls /bin/pwd. */ + +#include "uucp.h" + +#include "sysdep.h" + +#include <errno.h> + +char * +getcwd (zbuf, cbuf) + char *zbuf; + size_t cbuf; +{ + const char *azargs[2]; + FILE *e; + pid_t ipid; + int cread; + int ierr; + + azargs[0] = PWD_PROGRAM; + azargs[1] = NULL; + e = espopen (azargs, TRUE, &ipid); + if (e == NULL) + return NULL; + + ierr = 0; + + cread = fread (zbuf, sizeof (char), cbuf, e); + if (cread == 0) + ierr = errno; + + (void) fclose (e); + + if (ixswait ((unsigned long) ipid, (const char *) NULL) != 0) + { + ierr = EACCES; + cread = 0; + } + + if (cread != 0) + { + if (zbuf[cread - 1] == '\n') + zbuf[cread - 1] = '\0'; + else + { + ierr = ERANGE; + cread = 0; + } + } + + if (cread == 0) + { + errno = ierr; + return NULL; + } + + return zbuf; +} diff --git a/gnu/libexec/uucp/libunix/indir.c b/gnu/libexec/uucp/libunix/indir.c new file mode 100644 index 000000000000..2484ec23f85d --- /dev/null +++ b/gnu/libexec/uucp/libunix/indir.c @@ -0,0 +1,133 @@ +/* indir.c + See if a file is in a directory. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +/* See whether a file is in a directory, and optionally check access. */ + +boolean +fsysdep_in_directory (zfile, zdir, fcheck, freadable, zuser) + const char *zfile; + const char *zdir; + boolean fcheck; + boolean freadable; + const char *zuser; +{ + size_t c; + char *zcopy, *zslash; + struct stat s; + + if (*zfile != '/') + return FALSE; + c = strlen (zdir); + if (c > 0 && zdir[c - 1] == '/') + c--; + if (strncmp (zfile, zdir, c) != 0 + || (zfile[c] != '/' && zfile[c] != '\0')) + return FALSE; + if (strstr (zfile + c, "/../") != NULL) + return FALSE; + + /* If we're not checking access, get out now. */ + if (! fcheck) + return TRUE; + + zcopy = zbufcpy (zfile); + + /* Start checking directories after zdir. Otherwise, we would + require that all directories down to /usr/spool/uucppublic be + publically searchable; they probably are but it should not be a + requirement. */ + zslash = zcopy + c; + do + { + char b; + struct stat shold; + + b = *zslash; + *zslash = '\0'; + + shold = s; + if (stat (zcopy, &s) != 0) + { + if (errno != ENOENT) + { + ulog (LOG_ERROR, "stat (%s): %s", zcopy, strerror (errno)); + ubuffree (zcopy); + return FALSE; + } + + /* If this is the top directory, any problems will be caught + later when we try to open it. */ + if (zslash == zcopy + c) + { + ubuffree (zcopy); + return TRUE; + } + + /* Go back and check the last directory for read or write + access. */ + s = shold; + break; + } + + /* If this is not a directory, get out of the loop. */ + if (! S_ISDIR (s.st_mode)) + break; + + /* Make sure the directory is searchable. */ + if (! fsuser_access (&s, X_OK, zuser)) + { + ulog (LOG_ERROR, "%s: %s", zcopy, strerror (EACCES)); + ubuffree (zcopy); + return FALSE; + } + + /* If we've reached the end of the string, get out. */ + if (b == '\0') + break; + + *zslash = b; + } + while ((zslash = strchr (zslash + 1, '/')) != NULL); + + /* At this point s holds a stat on the last component of the path. + We must check it for readability or writeability. */ + if (! fsuser_access (&s, freadable ? R_OK : W_OK, zuser)) + { + ulog (LOG_ERROR, "%s: %s", zcopy, strerror (EACCES)); + ubuffree (zcopy); + return FALSE; + } + + ubuffree (zcopy); + return TRUE; +} diff --git a/gnu/libexec/uucp/libunix/init.c b/gnu/libexec/uucp/libunix/init.c new file mode 100644 index 000000000000..d4a137762813 --- /dev/null +++ b/gnu/libexec/uucp/libunix/init.c @@ -0,0 +1,394 @@ +/* init.c + Initialize the system dependent routines. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" +#include "sysdep.h" + +#include <errno.h> +#include <pwd.h> + +#if HAVE_FCNTL_H +#include <fcntl.h> +#else +#if HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#if ! HAVE_GETHOSTNAME && HAVE_UNAME +#include <sys/utsname.h> +#endif + +/* Use getcwd in preference to getwd; if we have neither, we will be + using a getcwd replacement. */ +#if HAVE_GETCWD +#undef HAVE_GETWD +#define HAVE_GETWD 0 +#else /* ! HAVE_GETCWD */ +#if ! HAVE_GETWD +#undef HAVE_GETCWD +#define HAVE_GETCWD 1 +#endif /* ! HAVE_GETWD */ +#endif /* ! HAVE_GETCWD */ + +#if HAVE_GETWD +/* Get a value for MAXPATHLEN. */ +#if HAVE_SYS_PARAMS_H +#include <sys/params.h> +#endif + +#if HAVE_LIMITS_H +#include <limits.h> +#endif + +#ifndef MAXPATHLEN +#ifdef PATH_MAX +#define MAXPATHLEN PATH_MAX +#else /* ! defined (PATH_MAX) */ +#define MAXPATHLEN 1024 +#endif /* ! defined (PATH_MAX) */ +#endif /* ! defined (MAXPATHLEN) */ +#endif /* HAVE_GETWD */ + +/* External functions. */ +#ifndef getlogin +extern char *getlogin (); +#endif +#if GETPWNAM_DECLARATION_OK +#ifndef getpwnam +extern struct passwd *getpwnam (); +#endif +#endif +#if GETPWUID_DECLARATION_OK +#ifndef getpwuid +extern struct passwd *getpwuid (); +#endif +#endif +#if HAVE_GETCWD +#ifndef getcwd +extern char *getcwd (); +#endif +#endif +#if HAVE_GETWD +#ifndef getwd +extern char *getwd (); +#endif +#endif +#if HAVE_SYSCONF +#ifndef sysconf +extern long sysconf (); +#endif +#endif + +/* Initialize the system dependent routines. We will probably be running + suid to uucp, so we make sure that nothing is obviously wrong. We + save the login name since we will be losing the real uid. */ +static char *zSlogin; + +/* The UUCP spool directory. */ +const char *zSspooldir; + +/* The UUCP lock directory. */ +const char *zSlockdir; + +/* The local UUCP name. */ +const char *zSlocalname; + +/* We save the current directory since we will do a chdir to the + spool directory. */ +char *zScwd; + +/* The maximum length of a system name is controlled by the type of spool + directory we use. */ +#if SPOOLDIR_V2 || SPOOLDIR_BSD42 || SPOOLDIR_BSD43 || SPOOLDIR_ULTRIX +size_t cSysdep_max_name_len = 7; +#endif +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 +size_t cSysdep_max_name_len = 14; +#endif +#if SPOOLDIR_TAYLOR +#if HAVE_LONG_FILE_NAMES +size_t cSysdep_max_name_len = 255; +#else /* ! HAVE_LONG_FILE_NAMES */ +size_t cSysdep_max_name_len = 14; +#endif /* ! HAVE_LONG_FILE_NAMES */ +#endif /* SPOOLDIR_TAYLOR */ + +/* Initialize the system dependent routines. */ + +void +usysdep_initialize (puuconf,iflags) + pointer puuconf; + int iflags; +{ + int cdescs; + int o; + int iuuconf; + char *z; + struct passwd *q; + + ulog_id (getpid ()); + + /* Close everything but stdin, stdout and stderr. */ +#if HAVE_GETDTABLESIZE + cdescs = getdtablesize (); +#else +#if HAVE_SYSCONF + cdescs = sysconf (_SC_OPEN_MAX); +#else +#ifdef OPEN_MAX + cdescs = OPEN_MAX; +#else +#ifdef NOFILE + cdescs = NOFILE; +#else + cdescs = 20; +#endif /* ! defined (NOFILE) */ +#endif /* ! defined (OPEN_MAX) */ +#endif /* ! HAVE_SYSCONF */ +#endif /* ! HAVE_GETDTABLESIZE */ + + for (o = 3; o < cdescs; o++) + (void) close (o); + + /* Make sure stdin, stdout and stderr are open. */ + if (fcntl (0, F_GETFD, 0) < 0 + && open ((char *) "/dev/null", O_RDONLY, 0) != 0) + exit (EXIT_FAILURE); + if (fcntl (1, F_GETFD, 0) < 0 + && open ((char *) "/dev/null", O_WRONLY, 0) != 1) + exit (EXIT_FAILURE); + if (fcntl (2, F_GETFD, 0) < 0 + && open ((char *) "/dev/null", O_WRONLY, 0) != 2) + exit (EXIT_FAILURE); + + iuuconf = uuconf_spooldir (puuconf, &zSspooldir); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + iuuconf = uuconf_lockdir (puuconf, &zSlockdir); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + iuuconf = uuconf_localname (puuconf, &zSlocalname); + if (iuuconf == UUCONF_NOT_FOUND) + { +#if HAVE_GETHOSTNAME + char ab[256]; + + if (gethostname (ab, sizeof ab - 1) < 0) + ulog (LOG_FATAL, "gethostname: %s", strerror (errno)); + ab[sizeof ab - 1] = '\0'; + ab[strcspn (ab, ".")] = '\0'; + zSlocalname = zbufcpy (ab); +#else /* ! HAVE_GETHOSTNAME */ +#if HAVE_UNAME + struct utsname s; + + if (uname (&s) < 0) + ulog (LOG_FATAL, "uname: %s", strerror (errno)); + zSlocalname = zbufcpy (s.nodename); +#else /* ! HAVE_UNAME */ + ulog (LOG_FATAL, "Don't know how to get local node name"); +#endif /* ! HAVE_UNAME */ +#endif /* ! HAVE_GETHOSTNAME */ + } + else if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + /* We always set our file modes to exactly what we want. */ + umask (0); + + /* Get the login name, making sure that it matches the uid. Many + systems truncate the getlogin return value to 8 characters, but + keep the full name in the password file, so we prefer the name in + the password file. */ + z = getenv ("LOGNAME"); + if (z == NULL) + z = getenv ("USER"); + if (z == NULL) + z = getlogin (); + if (z == NULL) + q = NULL; + else + { + q = getpwnam (z); + if (q != NULL) + z = q->pw_name; + } + if (q == NULL || q->pw_uid != getuid ()) + { + q = getpwuid (getuid ()); + if (q == NULL) + z = NULL; + else + z = q->pw_name; + } + if (z != NULL) + zSlogin = zbufcpy (z); + + /* On some old systems, an suid program run by root is started with + an euid of 0. If this happens, we look up the uid we should have + and set ourselves to it manually. This means that on such a + system root will not be able to uucp or uux files that are not + readable by uucp. */ + if ((iflags & INIT_SUID) != 0 + && geteuid () == 0) + { + q = getpwnam (OWNER); + if (q != NULL) + setuid (q->pw_uid); + } + + if ((iflags & INIT_GETCWD) != 0) + { + const char *zenv; + struct stat senv, sdot; + + /* Get the current working directory. We have to get it now, + since we're about to do a chdir. We use PWD if it's defined + and if it really names the working directory, since if it's + not the same as whatever getcwd returns it's probably more + appropriate. */ + zenv = getenv ("PWD"); + if (zenv != NULL + && stat ((char *) zenv, &senv) == 0 + && stat ((char *) ".", &sdot) == 0 + && senv.st_ino == sdot.st_ino + && senv.st_dev == sdot.st_dev) + zScwd = zbufcpy (zenv); + else + { + +#if HAVE_GETCWD + { + size_t c; + + c = 128; + while (TRUE) + { + zScwd = (char *) xmalloc (c); + if (getcwd (zScwd, c) != NULL) + break; + xfree ((pointer) zScwd); + zScwd = NULL; + if (errno != ERANGE) + break; + c <<= 1; + } + } +#endif /* HAVE_GETCWD */ + +#if HAVE_GETWD + zScwd = (char *) xmalloc (MAXPATHLEN); + if (getwd (zScwd) == NULL) + { + xfree ((pointer) zScwd); + zScwd = NULL; + } +#endif /* HAVE_GETWD */ + + if (zScwd != NULL) + zScwd = (char *) xrealloc ((pointer) zScwd, + strlen (zScwd) + 1); + } + } + + if ((iflags & INIT_NOCHDIR) == 0) + { + /* Connect to the spool directory, and create it if it doesn't + exist. */ + if (chdir (zSspooldir) < 0) + { + if (errno == ENOENT + && mkdir ((char *) zSspooldir, IDIRECTORY_MODE) < 0) + ulog (LOG_FATAL, "mkdir (%s): %s", zSspooldir, + strerror (errno)); + if (chdir (zSspooldir) < 0) + ulog (LOG_FATAL, "chdir (%s): %s", zSspooldir, + strerror (errno)); + } + } +} + +/* Exit the program. */ + +void +usysdep_exit (fsuccess) + boolean fsuccess; +{ + exit (fsuccess ? EXIT_SUCCESS : EXIT_FAILURE); +} + +/* This is called when a non-standard configuration file is used, to + make sure the program doesn't hand out privileged file access. + This means that to test non-standard configuration files, you + should be logged in as uucp. This is called before + usysdep_initialize. It ensures that someone can't simply use an + alternate configuration file to steal UUCP transfers from other + systems. This will still permit people to set up their own + configuration file and pretend to be whatever system they choose. + The only real security is to use a high level of protection on the + modem ports. */ + +/*ARGSUSED*/ +boolean fsysdep_other_config (z) + const char *z; +{ + (void) setuid (getuid ()); + (void) setgid (getgid ()); + return TRUE; +} + +/* Get the node name to use if it was not specified in the configuration + file. */ + +const char * +zsysdep_localname () +{ + return zSlocalname; +} + +/* Get the login name. We actually get the login name in + usysdep_initialize, because after that we may switch away from the + real uid. */ + +const char * +zsysdep_login_name () +{ + if (zSlogin == NULL) + ulog (LOG_FATAL, "Can't get login name"); + return zSlogin; +} diff --git a/gnu/libexec/uucp/libunix/isdir.c b/gnu/libexec/uucp/libunix/isdir.c new file mode 100644 index 000000000000..fc95e5275a82 --- /dev/null +++ b/gnu/libexec/uucp/libunix/isdir.c @@ -0,0 +1,18 @@ +/* isdir.c + See whether a file exists and is a directory. */ + +#include "uucp.h" + +#include "system.h" +#include "sysdep.h" + +boolean +fsysdep_directory (z) + const char *z; +{ + struct stat s; + + if (stat ((char *) z, &s) < 0) + return FALSE; + return S_ISDIR (s.st_mode); +} diff --git a/gnu/libexec/uucp/libunix/isfork.c b/gnu/libexec/uucp/libunix/isfork.c new file mode 100644 index 000000000000..f067d07552cd --- /dev/null +++ b/gnu/libexec/uucp/libunix/isfork.c @@ -0,0 +1,25 @@ +/* isfork.c + Retry fork several times before giving up. */ + +#include "uucp.h" + +#include "sysdep.h" + +#include <errno.h> + +pid_t +ixsfork () +{ + int i; + pid_t iret; + + for (i = 0; i < 10; i++) + { + iret = fork (); + if (iret >= 0 || errno != EAGAIN) + return iret; + sleep (5); + } + + return iret; +} diff --git a/gnu/libexec/uucp/libunix/iswait.c b/gnu/libexec/uucp/libunix/iswait.c new file mode 100644 index 000000000000..d2610aa1f8bd --- /dev/null +++ b/gnu/libexec/uucp/libunix/iswait.c @@ -0,0 +1,159 @@ +/* iswait.c + Wait for a process to finish. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" + +#include <errno.h> + +#if HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif + +/* We use a typedef wait_status for wait (waitpid, wait4) to put + results into. We define the POSIX examination functions we need if + they are not already defined (if they aren't defined, I assume that + we have a standard wait status). */ + +#if HAVE_UNION_WAIT +typedef union wait wait_status; +#ifndef WIFEXITED +#define WIFEXITED(u) ((u).w_termsig == 0) +#endif +#ifndef WEXITSTATUS +#define WEXITSTATUS(u) ((u).w_retcode) +#endif +#ifndef WTERMSIG +#define WTERMSIG(u) ((u).w_termsig) +#endif +#else /* ! HAVE_UNION_WAIT */ +typedef int wait_status; +#ifndef WIFEXITED +#define WIFEXITED(i) (((i) & 0xff) == 0) +#endif +#ifndef WEXITSTATUS +#define WEXITSTATUS(i) (((i) >> 8) & 0xff) +#endif +#ifndef WTERMSIG +#define WTERMSIG(i) ((i) & 0x7f) +#endif +#endif /* ! HAVE_UNION_WAIT */ + +/* Wait for a particular process to finish. The ipid argument should + be pid_t, but then we couldn't have a prototype. If the zreport + argument is not NULL, then a wait error will be logged, and if the + exit status is non-zero it will be logged with zreport as the + header of the log message. If the zreport argument is NULL, no + errors will be logged. This function returns the exit status if + the process exited normally, or -1 on error or if the process was + killed by a signal (I don't just always return the exit status + because then the calling code would have to prepared to handle + union wait status vs. int status, and none of the callers care + which signal killed the program anyhow). + + This functions keeps waiting until the process finished, even if it + is interrupted by a signal. I think this is right for all uses. + The controversial one would be when called from uuxqt to wait for a + requested process. Hitting uuxqt with SIGKILL will approximate the + actions taken if we return from here with an error anyhow. If we + do get a signal, we call ulog with a NULL argument to get it in the + log file at about the right time. */ + +int +ixswait (ipid, zreport) + unsigned long ipid; + const char *zreport; +{ + wait_status istat; + +#if HAVE_WAITPID + while (waitpid ((pid_t) ipid, (pointer) &istat, 0) < 0) + { + if (errno != EINTR) + { + if (zreport != NULL) + ulog (LOG_ERROR, "waitpid: %s", strerror (errno)); + return -1; + } + ulog (LOG_ERROR, (const char *) NULL); + } +#else /* ! HAVE_WAITPID */ +#if HAVE_WAIT4 + while (wait4 ((pid_t) ipid, (pointer) &istat, 0, + (struct rusage *) NULL) < 0) + { + if (errno != EINTR) + { + if (zreport != NULL) + ulog (LOG_ERROR, "wait4: %s", strerror (errno)); + return -1; + } + ulog (LOG_ERROR, (const char *) NULL); + } +#else /* ! HAVE_WAIT4 */ + pid_t igot; + + /* We could theoretically get the wrong child here if we're in some + kind of weird pipeline, so we don't give any error messages for + it. */ + while ((igot = wait ((pointer) &istat)) != (pid_t) ipid) + { + if (igot < 0) + { + if (errno != EINTR) + { + if (zreport != NULL) + ulog (LOG_ERROR, "wait: %s", strerror (errno)); + return -1; + } + ulog (LOG_ERROR, (const char *) NULL); + } + } +#endif /* ! HAVE_WAIT4 */ +#endif /* ! HAVE_WAITPID */ + + DEBUG_MESSAGE2 (DEBUG_EXECUTE, "%s %d", + WIFEXITED (istat) ? "Exit status" : "Signal", + WIFEXITED (istat) ? WEXITSTATUS (istat) : WTERMSIG (istat)); + + if (WIFEXITED (istat) && WEXITSTATUS (istat) == 0) + return 0; + + if (zreport != NULL) + { + if (! WIFEXITED (istat)) + ulog (LOG_ERROR, "%s: Got signal %d", zreport, WTERMSIG (istat)); + else + ulog (LOG_ERROR, "%s: Exit status %d", zreport, + WEXITSTATUS (istat)); + } + + if (WIFEXITED (istat)) + return WEXITSTATUS (istat); + else + return -1; +} diff --git a/gnu/libexec/uucp/libunix/jobid.c b/gnu/libexec/uucp/libunix/jobid.c new file mode 100644 index 000000000000..7f22f1d37d75 --- /dev/null +++ b/gnu/libexec/uucp/libunix/jobid.c @@ -0,0 +1,101 @@ +/* jobid.c + Convert file names to jobids and vice versa. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uuconf.h" +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +/* Translate a file name and an associated system into a job id. + These job ids are used by uustat. We use the system name attached + to the grade and sequence number. This won't work correctly if the + file name was actually created by some other version of uucp that + uses a different length for the sequence number. Too bad. */ + +char * +zsfile_to_jobid (qsys, zfile, bgrade) + const struct uuconf_system *qsys; + const char *zfile; + int bgrade; +{ + size_t clen; + char *zret; + + clen = strlen (qsys->uuconf_zname); + zret = zbufalc (clen + CSEQLEN + 2); + memcpy (zret, qsys->uuconf_zname, clen); + zret[clen] = bgrade; + memcpy (zret + clen + 1, zfile + strlen (zfile) - CSEQLEN, CSEQLEN + 1); + return zret; +} + +/* Turn a job id back into a file name. */ + +char * +zsjobid_to_file (zid, pzsystem, pbgrade) + const char *zid; + char **pzsystem; + char *pbgrade; +{ + size_t clen; + const char *zend; + char *zsys; + char abname[CSEQLEN + 11]; + char *zret; + + clen = strlen (zid); + if (clen <= CSEQLEN) + { + ulog (LOG_ERROR, "%s: Bad job id", zid); + return NULL; + } + + zend = zid + clen - CSEQLEN - 1; + + zsys = zbufalc (clen - CSEQLEN); + memcpy (zsys, zid, clen - CSEQLEN - 1); + zsys[clen - CSEQLEN - 1] = '\0'; + + /* This must correspond to zsfile_name. */ +#if ! SPOOLDIR_TAYLOR + sprintf (abname, "C.%.7s%s", zsys, zend); +#else + sprintf (abname, "C.%s", zend); +#endif + + zret = zsfind_file (abname, zsys, *zend); + + if (zret != NULL && pzsystem != NULL) + *pzsystem = zsys; + else + ubuffree (zsys); + + if (pbgrade != NULL) + *pbgrade = *zend; + + return zret; +} diff --git a/gnu/libexec/uucp/libunix/lcksys.c b/gnu/libexec/uucp/libunix/lcksys.c new file mode 100644 index 000000000000..4ece16afe7b5 --- /dev/null +++ b/gnu/libexec/uucp/libunix/lcksys.c @@ -0,0 +1,41 @@ +/* lcksys.c + Lock and unlock a remote system. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "sysdep.h" +#include "system.h" + +/* Lock a remote system. */ + +boolean +fsysdep_lock_system (qsys) + const struct uuconf_system *qsys; +{ + char *z; + boolean fret; + + z = zbufalc (strlen (qsys->uuconf_zname) + sizeof "LCK.."); + sprintf (z, "LCK..%.8s", qsys->uuconf_zname); + fret = fsdo_lock (z, FALSE, (boolean *) NULL); + ubuffree (z); + return fret; +} + +/* Unlock a remote system. */ + +boolean +fsysdep_unlock_system (qsys) + const struct uuconf_system *qsys; +{ + char *z; + boolean fret; + + z = zbufalc (strlen (qsys->uuconf_zname) + sizeof "LCK.."); + sprintf (z, "LCK..%.8s", qsys->uuconf_zname); + fret = fsdo_unlock (z, FALSE); + ubuffree (z); + return fret; +} diff --git a/gnu/libexec/uucp/libunix/link.c b/gnu/libexec/uucp/libunix/link.c new file mode 100644 index 000000000000..4550c76c94d9 --- /dev/null +++ b/gnu/libexec/uucp/libunix/link.c @@ -0,0 +1,38 @@ +/* link.c + Link two files. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +boolean +fsysdep_link (zfrom, zto, pfworked) + const char *zfrom; + const char *zto; + boolean *pfworked; +{ + *pfworked = FALSE; + if (link (zfrom, zto) == 0) + { + *pfworked = TRUE; + return TRUE; + } + if (errno == ENOENT) + { + if (! fsysdep_make_dirs (zto, TRUE)) + return FALSE; + if (link (zfrom, zto) == 0) + { + *pfworked = TRUE; + return TRUE; + } + } + if (errno == EXDEV) + return TRUE; + ulog (LOG_ERROR, "link (%s, %s): %s", zfrom, zto, strerror (errno)); + return FALSE; +} diff --git a/gnu/libexec/uucp/libunix/locfil.c b/gnu/libexec/uucp/libunix/locfil.c new file mode 100644 index 000000000000..0e05af9bcee9 --- /dev/null +++ b/gnu/libexec/uucp/libunix/locfil.c @@ -0,0 +1,95 @@ +/* locfil.c + Expand a file name on the local system. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <pwd.h> + +#if GETPWNAM_DECLARATION_OK +#ifndef getpwnam +extern struct passwd *getpwnam (); +#endif +#endif + +/* Turn a file name into an absolute path, by doing tilde expansion + and moving any other type of file into the public directory. */ + +char * +zsysdep_local_file (zfile, zpubdir) + const char *zfile; + const char *zpubdir; +{ + const char *zdir; + + if (*zfile == '/') + return zbufcpy (zfile); + + if (*zfile != '~') + zdir = zpubdir; + else + { + if (zfile[1] == '\0') + return zbufcpy (zpubdir); + + if (zfile[1] == '/') + { + zdir = zpubdir; + zfile += 2; + } + else + { + size_t cuserlen; + char *zcopy; + struct passwd *q; + + ++zfile; + cuserlen = strcspn ((char *) zfile, "/"); + zcopy = zbufalc (cuserlen + 1); + memcpy (zcopy, zfile, cuserlen); + zcopy[cuserlen] = '\0'; + + q = getpwnam (zcopy); + if (q == NULL) + { + ulog (LOG_ERROR, "User %s not found", zcopy); + ubuffree (zcopy); + return NULL; + } + ubuffree (zcopy); + + if (zfile[cuserlen] == '\0') + return zbufcpy (q->pw_dir); + + zdir = q->pw_dir; + zfile += cuserlen + 1; + } + } + + return zsysdep_in_dir (zdir, zfile); +} diff --git a/gnu/libexec/uucp/libunix/lock.c b/gnu/libexec/uucp/libunix/lock.c new file mode 100644 index 000000000000..d823213017b6 --- /dev/null +++ b/gnu/libexec/uucp/libunix/lock.c @@ -0,0 +1,477 @@ +/* lock.c + Lock and unlock a file name. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char lock_rcsid[] = "$Id: lock.c,v 1.1 1993/08/05 18:24:06 conklin Exp $"; +#endif + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +#if HAVE_FCNTL_H +#include <fcntl.h> +#else +#if HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +/* Lock something. If the fspooldir argument is TRUE, the argument is + a file name relative to the spool directory; otherwise the argument + is a simple file name which should be created in the system lock + directory (under HDB this is /etc/locks). */ + +boolean +fsdo_lock (zlock, fspooldir, pferr) + const char *zlock; + boolean fspooldir; + boolean *pferr; +{ + char *zfree; + const char *zpath, *zslash; + size_t cslash; + pid_t ime; + char *ztempfile; + char abtempfile[sizeof "TMP1234567890"]; + int o; +#if HAVE_V2_LOCKFILES + int i; +#else + char ab[12]; +#endif + int cwrote; + const char *zerr; + boolean fret; + + if (pferr != NULL) + *pferr = TRUE; + + if (fspooldir) + { + zfree = NULL; + zpath = zlock; + } + else + { + zfree = zsysdep_in_dir (zSlockdir, zlock); + zpath = zfree; + } + + ime = getpid (); + + /* We do the actual lock by creating a file and then linking it to + the final file name we want. This avoids race conditions due to + one process checking the file before we have finished writing it, + and also works even if we are somehow running as root. + + First, create the file in the right directory (we must create the + file in the same directory since otherwise we might attempt a + cross-device link). */ + zslash = strrchr (zpath, '/'); + if (zslash == NULL) + cslash = 0; + else + cslash = zslash - zpath + 1; + + sprintf (abtempfile, "TMP%010lx", (unsigned long) ime); + ztempfile = zbufalc (cslash + sizeof abtempfile); + memcpy (ztempfile, zpath, cslash); + memcpy (ztempfile + cslash, abtempfile, sizeof abtempfile); + + o = creat (ztempfile, IPUBLIC_FILE_MODE); + if (o < 0) + { + if (errno == ENOENT) + { + if (! fsysdep_make_dirs (ztempfile, FALSE)) + { + ubuffree (zfree); + ubuffree (ztempfile); + return FALSE; + } + o = creat (ztempfile, IPUBLIC_FILE_MODE); + } + if (o < 0) + { + ulog (LOG_ERROR, "creat (%s): %s", ztempfile, strerror (errno)); + ubuffree (zfree); + ubuffree (ztempfile); + return FALSE; + } + } + +#if HAVE_V2_LOCKFILES + i = ime; + cwrote = write (o, &i, sizeof i); +#else + sprintf (ab, "%10d\n", (int) ime); + cwrote = write (o, ab, strlen (ab)); +#endif + + zerr = NULL; + if (cwrote < 0) + zerr = "write"; + if (close (o) < 0) + zerr = "close"; + if (zerr != NULL) + { + ulog (LOG_ERROR, "%s (%s): %s", zerr, ztempfile, strerror (errno)); + (void) remove (ztempfile); + ubuffree (zfree); + ubuffree (ztempfile); + return FALSE; + } + + /* Now try to link the file we just created to the lock file that we + want. If it fails, try reading the existing file to make sure + the process that created it still exists. We do this in a loop + to make it easy to retry if the old locking process no longer + exists. */ + fret = TRUE; + if (pferr != NULL) + *pferr = FALSE; + o = -1; + zerr = NULL; + + while (link (ztempfile, zpath) != 0) + { + int cgot; + int ipid; + boolean freadonly; + + fret = FALSE; + + if (errno != EEXIST) + { + ulog (LOG_ERROR, "link (%s, %s): %s", ztempfile, zpath, + strerror (errno)); + if (pferr != NULL) + *pferr = TRUE; + break; + } + + freadonly = FALSE; + o = open ((char *) zpath, O_RDWR | O_NOCTTY, 0); + if (o < 0) + { + if (errno == EACCES) + { + freadonly = TRUE; + o = open ((char *) zpath, O_RDONLY, 0); + } + if (o < 0) + { + if (errno == ENOENT) + { + /* The file was presumably removed between the link + and the open. Try the link again. */ + fret = TRUE; + continue; + } + zerr = "open"; + break; + } + } + + /* The race starts here. See below for a discussion. */ + +#if HAVE_V2_LOCKFILES + cgot = read (o, &i, sizeof i); +#else + cgot = read (o, ab, sizeof ab - 1); +#endif + + if (cgot < 0) + { + zerr = "read"; + break; + } + +#if HAVE_V2_LOCKFILES + ipid = i; +#else + ab[cgot] = '\0'; + ipid = strtol (ab, (char **) NULL, 10); +#endif + + /* On NFS, the link might have actually succeeded even though we + got a failure return. This can happen if the original + acknowledgement was lost or delayed and the operation was + retried. In this case the pid will be our own. This + introduces a rather improbable race condition: if a stale + lock was left with our process ID in it, and another process + just did the kill, below, but has not yet changed the lock + file to hold its own process ID, we could start up and make + it all the way to here and think we have the lock. I'm not + going to worry about this possibility. */ + if (ipid == ime) + { + fret = TRUE; + break; + } + + /* If the process still exists, we will get EPERM rather than + ESRCH. We then return FALSE to indicate that we cannot make + the lock. */ + if (kill (ipid, 0) == 0 || errno == EPERM) + break; + + ulog (LOG_ERROR, "Found stale lock %s held by process %d", + zpath, ipid); + + /* This is a stale lock, created by a process that no longer + exists. + + Now we could remove the file (and, if the file mode disallows + writing, that's what we have to do), but we try to avoid + doing so since it causes a race condition. If we remove the + file, and are interrupted any time after we do the read until + we do the remove, another process could get in, open the + file, find that it was a stale lock, remove the file and + create a new one. When we regained control we would remove + the file the other process just created. + + These files are being generated partially for the benefit of + cu, and it would be nice to avoid the race however cu avoids + it, so that the programs remain compatible. Unfortunately, + nobody seems to know how cu avoids the race, or even if it + tries to avoid it at all. + + There are a few ways to avoid the race. We could use kernel + locking primitives, but they may not be available. We could + link to a special file name, but if that file were left lying + around then no stale lock could ever be broken (Henry Spencer + would think this was a good thing). + + Instead I've implemented the following procedure: seek to the + start of the file, write our pid into it, sleep for five + seconds, and then make sure our pid is still there. Anybody + who checks the file while we're asleep will find our pid + there and fail the lock. The only race will come from + another process which has done the read by the time we do our + write. That process will then have five seconds to do its + own write. When we wake up, we'll notice that our pid is no + longer in the file, and retry the lock from the beginning. + + This relies on the atomicity of write(2). If it possible for + the writes of two processes to be interleaved, the two + processes could livelock. POSIX unfortunately leaves this + case explicitly undefined; however, given that the write is + of less than a disk block, it's difficult to imagine an + interleave occurring. + + Note that this is still a race. If it takes the second + process more than five seconds to do the kill, the lseek, and + the write, both processes will think they have the lock. + Perhaps the length of time to sleep should be configurable. + Even better, perhaps I should add a configuration option to + use a permanent lock file, which eliminates any race and + forces the installer to be aware of the existence of the + permanent lock file. + + We stat the file after the sleep, to make sure some other + program hasn't deleted it for us. */ + if (freadonly) + { + (void) close (o); + o = -1; + (void) remove (zpath); + continue; + } + + if (lseek (o, (off_t) 0, SEEK_SET) != 0) + { + zerr = "lseek"; + break; + } + +#if HAVE_V2_LOCKFILES + i = ime; + cwrote = write (o, &i, sizeof i); +#else + sprintf (ab, "%10d\n", (int) ime); + cwrote = write (o, ab, strlen (ab)); +#endif + + if (cwrote < 0) + { + zerr = "write"; + break; + } + + (void) sleep (5); + + if (lseek (o, (off_t) 0, SEEK_SET) != 0) + { + zerr = "lseek"; + break; + } + +#if HAVE_V2_LOCKFILES + cgot = read (o, &i, sizeof i); +#else + cgot = read (o, ab, sizeof ab - 1); +#endif + + if (cgot < 0) + { + zerr = "read"; + break; + } + +#if HAVE_V2_LOCKFILES + ipid = i; +#else + ab[cgot] = '\0'; + ipid = strtol (ab, (char **) NULL, 10); +#endif + + if (ipid == ime) + { + struct stat sfile, sdescriptor; + + /* It looks like we have the lock. Do the final stat + check. */ + if (stat ((char *) zpath, &sfile) < 0) + { + if (errno != ENOENT) + { + zerr = "stat"; + break; + } + /* Loop around and try again. */ + } + else + { + if (fstat (o, &sdescriptor) < 0) + { + zerr = "fstat"; + break; + } + + if (sfile.st_ino == sdescriptor.st_ino + && sfile.st_dev == sdescriptor.st_dev) + { + /* Close the file before assuming we've succeeded to + pick up any trailing errors. */ + if (close (o) < 0) + { + zerr = "close"; + break; + } + + o = -1; + + /* We have the lock. */ + fret = TRUE; + break; + } + } + } + + /* Loop around and try the lock again. We keep doing this until + the lock file holds a pid that exists. */ + (void) close (o); + o = -1; + fret = TRUE; + } + + if (zerr != NULL) + { + ulog (LOG_ERROR, "%s (%s): %s", zerr, zpath, strerror (errno)); + if (pferr != NULL) + *pferr = TRUE; + } + + if (o >= 0) + (void) close (o); + + ubuffree (zfree); + + /* It would be nice if we could leave the temporary file around for + future calls, but considering that we create lock files in + various different directories it's probably more trouble than + it's worth. */ + if (remove (ztempfile) != 0) + ulog (LOG_ERROR, "remove (%s): %s", ztempfile, strerror (errno)); + + ubuffree (ztempfile); + + return fret; +} + +/* Unlock something. The fspooldir argument is as in fsdo_lock. */ + +boolean +fsdo_unlock (zlock, fspooldir) + const char *zlock; + boolean fspooldir; +{ + char *zfree; + const char *zpath; + + if (fspooldir) + { + zfree = NULL; + zpath = zlock; + } + else + { + zfree = zsysdep_in_dir (zSlockdir, zlock); + zpath = zfree; + } + + if (remove (zpath) == 0 + || errno == ENOENT) + { + ubuffree (zfree); + return TRUE; + } + else + { + ulog (LOG_ERROR, "remove (%s): %s", zpath, strerror (errno)); + ubuffree (zfree); + return FALSE; + } +} diff --git a/gnu/libexec/uucp/libunix/loctim.c b/gnu/libexec/uucp/libunix/loctim.c new file mode 100644 index 000000000000..da5f32e2d273 --- /dev/null +++ b/gnu/libexec/uucp/libunix/loctim.c @@ -0,0 +1,25 @@ +/* loctim.c + Turn a time epoch into a struct tm. This is trivial on Unix. */ + +#include "uucp.h" + +#if HAVE_TIME_H +#include <time.h> +#endif + +#include "system.h" + +#ifndef localtime +extern struct tm *localtime (); +#endif + +void +usysdep_localtime (itime, q) + long itime; + struct tm *q; +{ + time_t i; + + i = (time_t) itime; + *q = *localtime (&i); +} diff --git a/gnu/libexec/uucp/libunix/mail.c b/gnu/libexec/uucp/libunix/mail.c new file mode 100644 index 000000000000..74c1aa95adc6 --- /dev/null +++ b/gnu/libexec/uucp/libunix/mail.c @@ -0,0 +1,85 @@ +/* mail.c + Send mail to a user. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +#if HAVE_TIME_H +#include <time.h> +#endif + +#ifndef ctime +extern char *ctime (); +#endif + +/* Mail a message to a user. */ + +boolean +fsysdep_mail (zto, zsubject, cstrs, paz) + const char *zto; + const char *zsubject; + int cstrs; + const char **paz; +{ + const char *az[3]; + FILE *e; + pid_t ipid; + time_t itime; + int i; + + az[0] = MAIL_PROGRAM; + az[1] = zto; + az[2] = NULL; + + e = espopen (az, FALSE, &ipid); + if (e == NULL) + { + ulog (LOG_ERROR, "espopen (%s): %s", MAIL_PROGRAM, + strerror (errno)); + return FALSE; + } + + fprintf (e, "Subject: %s\n", zsubject); + fprintf (e, "To: %s\n", zto); + + fprintf (e, "\n"); + + (void) time (&itime); + /* Remember that ctime includes a \n, so this skips a line. */ + fprintf (e, "Message from UUCP on %s %s\n", zSlocalname, + ctime (&itime)); + + for (i = 0; i < cstrs; i++) + fputs (paz[i], e); + + (void) fclose (e); + + return ixswait ((unsigned long) ipid, MAIL_PROGRAM) == 0; +} diff --git a/gnu/libexec/uucp/libunix/mkdir.c b/gnu/libexec/uucp/libunix/mkdir.c new file mode 100644 index 000000000000..f59ad5dfd6e6 --- /dev/null +++ b/gnu/libexec/uucp/libunix/mkdir.c @@ -0,0 +1,58 @@ +/* mkdir.c + Create a directory. We must go through a subsidiary program to + force our real uid to be the uucp owner before invoking the setuid + /bin/mkdir program. */ + +#include "uucp.h" + +#include "sysdep.h" + +#include <errno.h> + +int +mkdir (zdir, imode) + const char *zdir; + int imode; +{ + struct stat s; + const char *azargs[3]; + int aidescs[3]; + pid_t ipid; + + /* Make sure the directory does not exist, since we will otherwise + get the wrong errno value. */ + if (stat (zdir, &s) == 0) + { + errno = EEXIST; + return -1; + } + + /* /bin/mkdir will create the directory with mode 777, so we set our + umask to get the mode we want. */ + (void) umask ((~ imode) & (S_IRWXU | S_IRWXG | S_IRWXO)); + + azargs[0] = UUDIR_PROGRAM; + azargs[1] = zdir; + azargs[2] = NULL; + aidescs[0] = SPAWN_NULL; + aidescs[1] = SPAWN_NULL; + aidescs[2] = SPAWN_NULL; + + ipid = ixsspawn (azargs, aidescs, FALSE, FALSE, (const char *) NULL, + TRUE, FALSE, (const char *) NULL, + (const char *) NULL, (const char *) NULL); + + (void) umask (0); + + if (ipid < 0) + return -1; + + if (ixswait ((unsigned long) ipid, (const char *) NULL) != 0) + { + /* Make up an errno value. */ + errno = EACCES; + return -1; + } + + return 0; +} diff --git a/gnu/libexec/uucp/libunix/mkdirs.c b/gnu/libexec/uucp/libunix/mkdirs.c new file mode 100644 index 000000000000..a4e0b67bb8c3 --- /dev/null +++ b/gnu/libexec/uucp/libunix/mkdirs.c @@ -0,0 +1,49 @@ +/* mkdirs.c + Create any directories needed for a file name. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +boolean +fsysdep_make_dirs (zfile, fpublic) + const char *zfile; + boolean fpublic; +{ + char *zcopy, *z; + int imode; + + zcopy = zbufcpy (zfile); + + if (fpublic) + imode = IPUBLIC_DIRECTORY_MODE; + else + imode = IDIRECTORY_MODE; + + for (z = zcopy; *z != '\0'; z++) + { + if (*z == '/' && z != zcopy) + { + *z = '\0'; + if (! fsysdep_directory (zcopy)) + { + if (mkdir (zcopy, imode) != 0) + { + ulog (LOG_ERROR, "mkdir (%s): %s", zcopy, + strerror (errno)); + ubuffree (zcopy); + return FALSE; + } + } + *z = '/'; + } + } + + ubuffree (zcopy); + + return TRUE; +} diff --git a/gnu/libexec/uucp/libunix/mode.c b/gnu/libexec/uucp/libunix/mode.c new file mode 100644 index 000000000000..53f74ec81c3e --- /dev/null +++ b/gnu/libexec/uucp/libunix/mode.c @@ -0,0 +1,33 @@ +/* mode.c + Get the Unix file mode of a file. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +unsigned int +ixsysdep_file_mode (zfile) + const char *zfile; +{ + struct stat s; + + if (stat ((char *) zfile, &s) != 0) + { + ulog (LOG_ERROR, "stat (%s): %s", zfile, strerror (errno)); + return 0; + } + +#if S_IRWXU != 0700 + #error Files modes need to be translated +#endif + + /* We can't return 0, since that indicate an error. */ + if ((s.st_mode & 0777) == 0) + return 0400; + + return s.st_mode & 0777; +} diff --git a/gnu/libexec/uucp/libunix/move.c b/gnu/libexec/uucp/libunix/move.c new file mode 100644 index 000000000000..ccfe6d4d728d --- /dev/null +++ b/gnu/libexec/uucp/libunix/move.c @@ -0,0 +1,176 @@ +/* move.c + Move a file. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +#if HAVE_FCNTL_H +#include <fcntl.h> +#else +#if HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#endif + +/* Move (rename) a file from one name to another. This routine will + optionally create necessary directories, and fpublic indicates + whether the new directories should be publically accessible or not. + If fcheck is true, it will try to determine whether the named user + has write access to the new file. */ + +boolean +fsysdep_move_file (zorig, zto, fmkdirs, fpublic, fcheck, zuser) + const char *zorig; + const char *zto; + boolean fmkdirs; + boolean fpublic; + boolean fcheck; + const char *zuser; +{ + struct stat s; + int o; + + DEBUG_MESSAGE2 (DEBUG_SPOOLDIR, + "fsysdep_move_file: Moving %s to %s", zorig, zto); + + /* Optionally make sure that zuser has write access on the + directory. */ + if (fcheck) + { + char *zcopy; + char *zslash; + + zcopy = zbufcpy (zto); + zslash = strrchr (zcopy, '/'); + if (zslash == zcopy) + zslash[1] = '\0'; + else + *zslash = '\0'; + + if (stat (zcopy, &s) != 0) + { + ulog (LOG_ERROR, "stat (%s): %s", zcopy, strerror (errno)); + (void) remove (zorig); + ubuffree (zcopy); + return FALSE; + } + if (! fsuser_access (&s, W_OK, zuser)) + { + ulog (LOG_ERROR, "%s: %s", zcopy, strerror (EACCES)); + (void) remove (zorig); + ubuffree (zcopy); + return FALSE; + } + ubuffree (zcopy); + + /* A malicious user now has a few milliseconds to change a + symbolic link to a directory uucp has write permission on but + the user does not (the obvious choice being /usr/lib/uucp). + The only certain method I can come up with to close this race + is to fork an suid process which takes on the users identity + and does the actual copy. This is sufficiently high overhead + that I'm not going to do it. */ + } + + /* We try to use rename to move the file. */ + + if (rename (zorig, zto) == 0) + return TRUE; + + if (fmkdirs && errno == ENOENT) + { + if (! fsysdep_make_dirs (zto, fpublic)) + { + (void) remove (zorig); + return FALSE; + } + if (rename (zorig, zto) == 0) + return TRUE; + } + +#if HAVE_RENAME + /* On some systems the system call rename seems to fail for + arbitrary reasons. To get around this, we always try to copy the + file by hand if the rename failed. */ + errno = EXDEV; +#endif + + /* If we can't link across devices, we must copy the file by hand. */ + if (errno != EXDEV) + { + ulog (LOG_ERROR, "rename (%s, %s): %s", zorig, zto, + strerror (errno)); + (void) remove (zorig); + return FALSE; + } + + /* Copy the file. */ + if (stat ((char *) zorig, &s) < 0) + { + ulog (LOG_ERROR, "stat (%s): %s", zorig, strerror (errno)); + (void) remove (zorig); + return FALSE; + } + + /* Make sure the file gets the right mode by creating it before we + call fcopy_file. */ + (void) remove (zto); + o = creat ((char *) zto, s.st_mode); + if (o < 0) + { + if (fmkdirs && errno == ENOENT) + { + if (! fsysdep_make_dirs (zto, fpublic)) + { + (void) remove (zorig); + return FALSE; + } + o = creat ((char *) zto, s.st_mode); + } + if (o < 0) + { + ulog (LOG_ERROR, "creat (%s): %s", zto, strerror (errno)); + (void) remove (zorig); + return FALSE; + } + } + (void) close (o); + + if (! fcopy_file (zorig, zto, fpublic, fmkdirs)) + { + (void) remove (zorig); + return FALSE; + } + + if (remove (zorig) != 0) + ulog (LOG_ERROR, "remove (%s): %s", zorig, strerror (errno)); + + return TRUE; +} diff --git a/gnu/libexec/uucp/libunix/opensr.c b/gnu/libexec/uucp/libunix/opensr.c new file mode 100644 index 000000000000..3a8ca7a8b8ac --- /dev/null +++ b/gnu/libexec/uucp/libunix/opensr.c @@ -0,0 +1,244 @@ +/* opensr.c + Open files for sending and receiving. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" +#include "sysdep.h" + +#include <errno.h> + +#if HAVE_TIME_H +#include <time.h> +#endif + +#if HAVE_FCNTL_H +#include <fcntl.h> +#else +#if HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +#ifndef time +extern time_t time (); +#endif + +/* Open a file to send to another system, and return the mode and + the size. */ + +openfile_t +esysdep_open_send (qsys, zfile, fcheck, zuser) + const struct uuconf_system *qsys; + const char *zfile; + boolean fcheck; + const char *zuser; +{ + struct stat s; + openfile_t e; + int o; + + if (fsysdep_directory (zfile)) + { + ulog (LOG_ERROR, "%s: is a directory", zfile); + return EFILECLOSED; + } + +#if USE_STDIO + e = fopen (zfile, BINREAD); + if (e == NULL) + { + ulog (LOG_ERROR, "fopen (%s): %s", zfile, strerror (errno)); + return NULL; + } + o = fileno (e); +#else + e = open ((char *) zfile, O_RDONLY | O_NOCTTY, 0); + if (e == -1) + { + ulog (LOG_ERROR, "open (%s): %s", zfile, strerror (errno)); + return -1; + } + o = e; +#endif + + if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + (void) ffileclose (e); + return EFILECLOSED; + } + + if (fstat (o, &s) == -1) + { + ulog (LOG_ERROR, "fstat: %s", strerror (errno)); + s.st_mode = 0666; + } + + /* We have to recheck the file permission, although we probably + checked it already, because otherwise there would be a window in + which somebody could change the contents of a symbolic link to + point to some file which was only readable by uucp. */ + if (fcheck) + { + if (! fsuser_access (&s, R_OK, zuser)) + { + ulog (LOG_ERROR, "%s: %s", zfile, strerror (EACCES)); + (void) ffileclose (e); + return EFILECLOSED; + } + } + + return e; +} + +/* Get a temporary file name to receive into. We use the ztemp + argument to pick the file name, so that we relocate the file if the + transmission is aborted. */ + +char * +zsysdep_receive_temp (qsys, zto, ztemp) + const struct uuconf_system *qsys; + const char *zto; + const char *ztemp; +{ + if (ztemp != NULL + && *ztemp == 'D' + && strcmp (ztemp, "D.0") != 0) + return zsappend3 (".Temp", qsys->uuconf_zname, ztemp); + else + return zstemp_file (qsys); +} + +/* Open a temporary file to receive into. This should, perhaps, check + that we have write permission on the receiving directory, but it + doesn't. */ + +openfile_t +esysdep_open_receive (qsys, zto, ztemp, zreceive, pcrestart) + const struct uuconf_system *qsys; + const char *zto; + const char *ztemp; + const char *zreceive; + long *pcrestart; +{ + int o; + openfile_t e; + + /* If we used the ztemp argument in zsysdep_receive_temp, above, + then we will have a name consistent across conversations. In + that case, we may have already received some portion of this + file. */ + o = -1; + *pcrestart = -1; + if (ztemp != NULL + && *ztemp == 'D' + && strcmp (ztemp, "D.0") != 0) + { + o = open ((char *) zreceive, O_WRONLY); + if (o >= 0) + { + struct stat s; + + /* For safety, we insist on the file being less than 1 week + old. This can still catch people, unfortunately. I + don't know of any good solution to the problem of old + files hanging around. If anybody has a file they want + restarted, and they know about this issue, they can touch + it to bring it up to date. */ + if (fstat (o, &s) < 0 + || s.st_mtime + 7 * 24 * 60 * 60 < time ((time_t *) NULL)) + { + (void) close (o); + o = -1; + } + else + { + DEBUG_MESSAGE1 (DEBUG_SPOOLDIR, + "esysdep_open_receive: Reusing %s", + zreceive); + *pcrestart = (long) s.st_size; + } + } + } + + if (o < 0) + o = creat ((char *) zreceive, IPRIVATE_FILE_MODE); + + if (o < 0) + { + if (errno == ENOENT) + { + if (! fsysdep_make_dirs (zreceive, FALSE)) + return EFILECLOSED; + o = creat ((char *) zreceive, IPRIVATE_FILE_MODE); + } + if (o < 0) + { + ulog (LOG_ERROR, "creat (%s): %s", zreceive, strerror (errno)); + return EFILECLOSED; + } + } + + if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + (void) close (o); + (void) remove (zreceive); + return EFILECLOSED; + } + +#if USE_STDIO + e = fdopen (o, (char *) BINWRITE); + + if (e == NULL) + { + ulog (LOG_ERROR, "fdopen (%s): %s", zreceive, strerror (errno)); + (void) close (o); + (void) remove (zreceive); + return EFILECLOSED; + } +#else + e = o; +#endif + + return e; +} diff --git a/gnu/libexec/uucp/libunix/pause.c b/gnu/libexec/uucp/libunix/pause.c new file mode 100644 index 000000000000..e774e0897bf2 --- /dev/null +++ b/gnu/libexec/uucp/libunix/pause.c @@ -0,0 +1,96 @@ +/* pause.c + Pause for half a second. */ + +#include "uucp.h" + +#include "sysdep.h" +#include "system.h" + +/* Pick a timing routine to use. I somewhat arbitrarily picked usleep + above nap above napms above poll above select. */ +#if HAVE_USLEEP || HAVE_NAP || HAVE_NAPMS || HAVE_POLL +#undef HAVE_SELECT +#define HAVE_SELECT 0 +#endif + +#if HAVE_USLEEP || HAVE_NAP || HAVE_NAPMS +#undef HAVE_POLL +#define HAVE_POLL 0 +#endif + +#if HAVE_USLEEP || HAVE_NAP +#undef HAVE_NAPMS +#define HAVE_NAPMS 0 +#endif + +#if HAVE_USLEEP +#undef HAVE_NAP +#define HAVE_NAP 0 +#endif + +#if HAVE_SELECT +#include <sys/time.h> +#if HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif +#endif + +#if HAVE_POLL +#if HAVE_STROPTS_H +#include <stropts.h> +#endif +#if HAVE_POLL_H +#include <poll.h> +#endif +#if ! HAVE_STROPTS_H && ! HAVE_POLL_H +/* We need a definition for struct pollfd, although it doesn't matter + what it contains. */ +struct pollfd +{ + int idummy; +}; +#endif /* ! HAVE_STROPTS_H && ! HAVE_POLL_H */ +#endif /* HAVE_POLL */ + +#if HAVE_TIME_H +#if HAVE_SYS_TIME_AND_TIME_H || ! USE_SELECT_TIMER +#include <time.h> +#endif +#endif + +void +usysdep_pause () +{ +#if HAVE_NAPMS + napms (500); +#endif /* HAVE_NAPMS */ +#if HAVE_NAP +#if HAVE_HUNDREDTHS_NAP + nap (50L); +#else + nap (500L); +#endif /* ! HAVE_HUNDREDTHS_NAP */ +#endif /* HAVE_NAP */ +#if HAVE_USLEEP + usleep (500 * (long) 1000); +#endif /* HAVE_USLEEP */ +#if HAVE_POLL + struct pollfd sdummy; + + /* We need to pass an unused pollfd structure because poll checks + the address before checking the number of elements. */ + poll (&sdummy, 0, 500); +#endif /* HAVE_POLL */ +#if HAVE_SELECT + struct timeval s; + + s.tv_sec = 0; + s.tv_usec = 500 * (long) 1000; + select (0, (pointer) NULL, (pointer) NULL, (pointer) NULL, &s); +#endif /* USE_SELECT_TIMER */ +#if ! HAVE_NAPMS && ! HAVE_NAP && ! HAVE_USLEEP +#if ! USE_SELECT_TIMER && ! HAVE_POLL + sleep (1); +#endif /* ! USE_SELECT_TIMER && ! HAVE_POLL */ +#endif /* ! HAVE_NAPMS && ! HAVE_NAP && ! HAVE_USLEEP */ +} diff --git a/gnu/libexec/uucp/libunix/picksb.c b/gnu/libexec/uucp/libunix/picksb.c new file mode 100644 index 000000000000..4d8cc4b2f3b6 --- /dev/null +++ b/gnu/libexec/uucp/libunix/picksb.c @@ -0,0 +1,230 @@ +/* picksb.c + System dependent routines for uupick. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char picksb_rcsid[] = "$Id: picksb.c,v 1.1 1993/08/05 18:24:15 conklin Exp $"; +#endif + +#include "uudefs.h" +#include "system.h" +#include "sysdep.h" + +#include <errno.h> +#include <pwd.h> + +#if HAVE_OPENDIR +#if HAVE_DIRENT_H +#include <dirent.h> +#else /* ! HAVE_DIRENT_H */ +#include <sys/dir.h> +#define dirent direct +#endif /* ! HAVE_DIRENT_H */ +#endif /* HAVE_OPENDIR */ + +#if GETPWUID_DECLARATION_OK +#ifndef getpwuid +extern struct passwd *getpwuid (); +#endif +#endif + +/* Local variables. */ + +/* Directory of ~/receive/USER. */ +static DIR *qStopdir; + +/* Name of ~/receive/USER. */ +static char *zStopdir; + +/* Directory of ~/receive/USER/SYSTEM. */ +static DIR *qSsysdir; + +/* Name of system. */ +static char *zSsysdir; + +/* Prepare to get a list of all the file to uupick for this user. */ + +/*ARGSUSED*/ +boolean +fsysdep_uupick_init (zsystem, zpubdir) + const char *zsystem; + const char *zpubdir; +{ + const char *zuser; + + zuser = zsysdep_login_name (); + + zStopdir = (char *) xmalloc (strlen (zpubdir) + + sizeof "/receive/" + + strlen (zuser)); + sprintf (zStopdir, "%s/receive/%s", zpubdir, zuser); + + qStopdir = opendir (zStopdir); + if (qStopdir == NULL && errno != ENOENT) + { + ulog (LOG_ERROR, "opendir (%s): %s", zStopdir, + strerror (errno)); + return FALSE; + } + + qSsysdir = NULL; + + return TRUE; +} + +/* Return the next file from the uupick directories. */ + +/*ARGSUSED*/ +char * +zsysdep_uupick (zsysarg, zpubdir, pzfrom, pzfull) + const char *zsysarg; + const char *zpubdir; + char **pzfrom; + char **pzfull; +{ + struct dirent *qentry; + + while (TRUE) + { + while (qSsysdir == NULL) + { + const char *zsystem; + char *zdir; + + if (qStopdir == NULL) + return NULL; + + if (zsysarg != NULL) + { + closedir (qStopdir); + qStopdir = NULL; + zsystem = zsysarg; + } + else + { + do + { + qentry = readdir (qStopdir); + if (qentry == NULL) + { + closedir (qStopdir); + qStopdir = NULL; + return NULL; + } + } + while (strcmp (qentry->d_name, ".") == 0 + || strcmp (qentry->d_name, "..") == 0); + + zsystem = qentry->d_name; + } + + zdir = zbufalc (strlen (zStopdir) + strlen (zsystem) + sizeof "/"); + sprintf (zdir, "%s/%s", zStopdir, zsystem); + + qSsysdir = opendir (zdir); + if (qSsysdir == NULL) + { + if (errno != ENOENT && errno != ENOTDIR) + ulog (LOG_ERROR, "opendir (%s): %s", zdir, strerror (errno)); + } + else + { + ubuffree (zSsysdir); + zSsysdir = zbufcpy (zsystem); + } + + ubuffree (zdir); + } + + qentry = readdir (qSsysdir); + if (qentry == NULL) + { + closedir (qSsysdir); + qSsysdir = NULL; + continue; + } + + if (strcmp (qentry->d_name, ".") == 0 + || strcmp (qentry->d_name, "..") == 0) + continue; + + *pzfrom = zbufcpy (zSsysdir); + *pzfull = zsappend3 (zStopdir, zSsysdir, qentry->d_name); + return zbufcpy (qentry->d_name); + } +} + +/*ARGSUSED*/ +boolean +fsysdep_uupick_free (zsystem, zpubdir) + const char *zsystem; + const char *zpubdir; +{ + xfree ((pointer) zStopdir); + if (qStopdir != NULL) + { + closedir (qStopdir); + qStopdir = NULL; + } + ubuffree (zSsysdir); + zSsysdir = NULL; + if (qSsysdir != NULL) + { + closedir (qSsysdir); + qSsysdir = NULL; + } + + return TRUE; +} + +/* Expand a local file name for uupick. */ + +char * +zsysdep_uupick_local_file (zfile) + const char *zfile; +{ + struct passwd *q; + + /* If this does not start with a simple ~, pass it to + zsysdep_local_file_cwd; as it happens, zsysdep_local_file_cwd + only uses the zpubdir argument if the file starts with a simple + ~, so it doesn't really matter what we pass for zpubdir. */ + if (zfile[0] != '~' + || (zfile[1] != '/' && zfile[1] != '\0')) + return zsysdep_local_file_cwd (zfile, (const char *) NULL); + + q = getpwuid (getuid ()); + if (q == NULL) + { + ulog (LOG_ERROR, "Can't get home directory"); + return NULL; + } + + if (zfile[1] == '\0') + return zbufcpy (q->pw_dir); + + return zsysdep_in_dir (q->pw_dir, zfile + 2); +} diff --git a/gnu/libexec/uucp/libunix/portnm.c b/gnu/libexec/uucp/libunix/portnm.c new file mode 100644 index 000000000000..9eda4ab012ba --- /dev/null +++ b/gnu/libexec/uucp/libunix/portnm.c @@ -0,0 +1,51 @@ +/* portnm.c + Get the port name of stdin. */ + +#include "uucp.h" + +#include "sysdep.h" +#include "system.h" + +#if HAVE_TCP +#if HAVE_SYS_TYPES_TCP_H +#include <sys/types.tcp.h> +#endif +#include <sys/socket.h> +#endif + +#ifndef ttyname +extern char *ttyname (); +#endif + +/* Get the port name of standard input. I assume that Unix systems + generally support ttyname. If they don't, this function can just + return NULL. It uses getsockname to see whether standard input is + a TCP connection. */ + +const char * +zsysdep_port_name (ftcp_port) + boolean *ftcp_port; +{ + const char *z; + + *ftcp_port = FALSE; + +#if HAVE_TCP + { + size_t clen; + struct sockaddr s; + + clen = sizeof (struct sockaddr); + if (getsockname (0, &s, &clen) == 0) + *ftcp_port = TRUE; + } +#endif /* HAVE_TCP */ + + z = ttyname (0); + if (z == NULL) + return NULL; + if (strncmp (z, "/dev/", sizeof "/dev/" - 1) == 0) + return z + sizeof "/dev/" - 1; + else + return z; +} diff --git a/gnu/libexec/uucp/libunix/proctm.c b/gnu/libexec/uucp/libunix/proctm.c new file mode 100644 index 000000000000..55cf96f0c972 --- /dev/null +++ b/gnu/libexec/uucp/libunix/proctm.c @@ -0,0 +1,197 @@ +/* proctm.c + Get the time spent in the process. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "sysdep.h" +#include "system.h" + +#if HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#if HAVE_LIMITS_H +#include <limits.h> +#endif + +/* Prefer gettimeofday to ftime to times. */ + +#if HAVE_GETTIMEOFDAY || HAVE_FTIME +#undef HAVE_TIMES +#define HAVE_TIMES 0 +#endif + +#if HAVE_GETTIMEOFDAY +#undef HAVE_FTIME +#define HAVE_FTIME 0 +#endif + +#if HAVE_TIME_H && (HAVE_SYS_TIME_AND_TIME_H || ! HAVE_GETTIMEOFDAY) +#include <time.h> +#endif + +#if HAVE_GETTIMEOFDAY +#include <sys/time.h> +#endif + +#if HAVE_FTIME +#include <sys/timeb.h> +#endif + +#if HAVE_TIMES + +#if HAVE_SYS_TIMES_H +#include <sys/times.h> +#endif + +#if TIMES_DECLARATION_OK +/* We use a macro to protect this because times really returns clock_t + and on some systems, such as Ultrix 4.0, clock_t is int. We don't + leave it out entirely because on some systems, such as System III, + the declaration is necessary for correct compilation. */ +#ifndef times +extern long times (); +#endif +#endif /* TIMES_DECLARATION_OK */ + +#ifdef _SC_CLK_TCK +#define HAVE_SC_CLK_TCK 1 +#else +#define HAVE_SC_CLK_TCK 0 +#endif + +/* TIMES_TICK may have been set in policy.h, or we may be able to get + it using sysconf. If neither is the case, try to find a useful + definition from the system header files. */ +#if TIMES_TICK == 0 && (! HAVE_SYSCONF || ! HAVE_SC_CLK_TCK) +#ifdef CLK_TCK +#undef TIMES_TICK +#define TIMES_TICK CLK_TCK +#else /* ! defined (CLK_TCK) */ +#ifdef HZ +#undef TIMES_TICK +#define TIMES_TICK HZ +#endif /* defined (HZ) */ +#endif /* ! defined (CLK_TCK) */ +#endif /* TIMES_TICK == 0 && (! HAVE_SYSCONF || ! HAVE_SC_CLK_TCK) */ + +#endif /* HAVE_TIMES */ + +#ifndef time +extern time_t time (); +#endif +#if HAVE_SYSCONF +#ifndef sysconf +extern long sysconf (); +#endif +#endif + +/* Get the time in seconds and microseconds; this need only work + within the process when called from the system independent code. + It is also called by ixsysdep_time. */ + +long +ixsysdep_process_time (pimicros) + long *pimicros; +{ +#if HAVE_GETTIMEOFDAY + struct timeval stime; + struct timezone stz; + + (void) gettimeofday (&stime, &stz); + if (pimicros != NULL) + *pimicros = (long) stime.tv_usec; + return (long) stime.tv_sec; +#endif /* HAVE_GETTIMEOFDAY */ + +#if HAVE_FTIME + static boolean fbad; + + if (! fbad) + { + struct timeb stime; + static struct timeb slast; + + (void) ftime (&stime); + + /* On some systems, such as SCO 3.2.2, ftime can go backwards in + time. If we detect this, we switch to using time. */ + if (slast.time != 0 + && (stime.time < slast.time + || (stime.time == slast.time && + stime.millitm < slast.millitm))) + fbad = TRUE; + else + { + slast = stime; + if (pimicros != NULL) + *pimicros = (long) stime.millitm * (long) 1000; + return (long) stime.time; + } + } + + if (pimicros != NULL) + *pimicros = 0; + return (long) time ((time_t *) NULL); +#endif /* HAVE_FTIME */ + +#if HAVE_TIMES + struct tms s; + long i; + static int itick; + + if (itick == 0) + { +#if TIMES_TICK == 0 +#if HAVE_SYSCONF && HAVE_SC_CLK_TCK + itick = (int) sysconf (_SC_CLK_TCK); +#else /* ! HAVE_SYSCONF || ! HAVE_SC_CLK_TCK */ + const char *z; + + z = getenv ("HZ"); + if (z != NULL) + itick = (int) strtol (z, (char **) NULL, 10); + + /* If we really couldn't get anything, just use 60. */ + if (itick == 0) + itick = 60; +#endif /* ! HAVE_SYSCONF || ! HAVE_SC_CLK_TCK */ +#else /* TIMES_TICK != 0 */ + itick = TIMES_TICK; +#endif /* TIMES_TICK == 0 */ + } + + i = (long) times (&s); + if (pimicros != NULL) + *pimicros = (i % (long) itick) * ((long) 1000000 / (long) itick); + return i / (long) itick; +#endif /* HAVE_TIMES */ + +#if ! HAVE_GETTIMEOFDAY && ! HAVE_FTIME && ! HAVE_TIMES + if (pimicros != NULL) + *pimicros = 0; + return (long) time ((time_t *) NULL); +#endif /* ! HAVE_GETTIMEOFDAY && ! HAVE_FTIME && ! HAVE_TIMES */ +} diff --git a/gnu/libexec/uucp/libunix/recep.c b/gnu/libexec/uucp/libunix/recep.c new file mode 100644 index 000000000000..84a211a7a946 --- /dev/null +++ b/gnu/libexec/uucp/libunix/recep.c @@ -0,0 +1,197 @@ +/* recep.c + See whether a file has already been received. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +#if HAVE_TIME_H +#include <time.h> +#endif + +#if HAVE_FCNTL_H +#include <fcntl.h> +#else +#if HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#endif + +static char *zsreceived_name P((const struct uuconf_system *qsys, + const char *ztemp)); + +/* These routines are used to see whether we have already received a + file in a previous UUCP connection. It is possible for the + acknowledgement of a received file to be lost. The sending system + will then now know that the file was correctly received, and will + send it again. This can be a problem particularly with protocols + which support channels, since they may send several small files in + a single window, all of which may be received correctly although + the sending system never sees the acknowledgement. If these files + involve an execution, the execution will happen twice, which will + be bad. + + We use a simple system. For each file we want to remember, we + create an empty file names .Received/SYS/TEMP, where SYS is the + name of the system and TEMP is the name of the temporary file used + by the sender. If no temporary file is used by the sender, we + don't remember that we received the file. This is not perfect, but + execution files will always have a temporary file, so the most + important case is handled. Also, any file received from Taylor + UUCP 1.04 or greater will always have a temporary file. */ + +/* Return the name we are going use for the marker, or NULL if we have + no name. */ + +static char * +zsreceived_name (qsys, ztemp) + const struct uuconf_system *qsys; + const char *ztemp; +{ + if (ztemp != NULL + && *ztemp == 'D' + && strcmp (ztemp, "D.0") != 0) + return zsappend3 (".Received", qsys->uuconf_zname, ztemp); + else + return NULL; +} + +/* Remember that we have already received a file. */ + +/*ARGSUSED*/ +boolean +fsysdep_remember_reception (qsys, zto, ztemp) + const struct uuconf_system *qsys; + const char *zto; + const char *ztemp; +{ + char *zfile; + int o; + + zfile = zsreceived_name (qsys, ztemp); + if (zfile == NULL) + return TRUE; + o = creat (zfile, IPUBLIC_FILE_MODE); + if (o < 0) + { + if (errno == ENOENT) + { + if (fsysdep_make_dirs (zfile, TRUE)) + { + ubuffree (zfile); + return FALSE; + } + o = creat (zfile, IPUBLIC_FILE_MODE); + } + if (o < 0) + { + ulog (LOG_ERROR, "creat (%s): %s", zfile, strerror (errno)); + ubuffree (zfile); + return FALSE; + } + } + + ubuffree (zfile); + + /* We don't have to actually put anything in the file; we just use + the name. This is more convenient than keeping a file with a + list of names. */ + if (close (o) < 0) + { + ulog (LOG_ERROR, "fsysdep_remember_reception: close: %s", + strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/* See if we have already received a file. Note that don't delete the + marker file here, because we need to know that the sending system + has received our denial first. This function returns TRUE if the + file has already been received, FALSE if it has not. */ + +/*ARGSUSED*/ +boolean +fsysdep_already_received (qsys, zto, ztemp) + const struct uuconf_system *qsys; + const char *zto; + const char *ztemp; +{ + char *zfile; + struct stat s; + boolean fret; + + zfile = zsreceived_name (qsys, ztemp); + if (zfile == NULL) + return FALSE; + if (stat (zfile, &s) < 0) + { + if (errno != ENOENT) + ulog (LOG_ERROR, "stat (%s): %s", zfile, strerror (errno)); + ubuffree (zfile); + return FALSE; + } + + /* Ignore the file (return FALSE) if it is over one week old. */ + fret = s.st_mtime + 7 * 24 * 60 * 60 >= time ((time_t *) NULL); + + if (fret) + DEBUG_MESSAGE1 (DEBUG_SPOOLDIR, "fsysdep_already_received: Found %s", + zfile); + + ubuffree (zfile); + + return fret; +} + +/* Forget that we have received a file. */ + +/*ARGSUSED*/ +boolean +fsysdep_forget_reception (qsys, zto, ztemp) + const struct uuconf_system *qsys; + const char *zto; + const char *ztemp; +{ + char *zfile; + + zfile = zsreceived_name (qsys, ztemp); + if (zfile == NULL) + return TRUE; + if (remove (zfile) < 0 + && errno != ENOENT) + { + ulog (LOG_ERROR, "remove (%s): %s", zfile, strerror (errno)); + ubuffree (zfile); + return FALSE; + } + return TRUE; +} diff --git a/gnu/libexec/uucp/libunix/remove.c b/gnu/libexec/uucp/libunix/remove.c new file mode 100644 index 000000000000..b695888ffaa3 --- /dev/null +++ b/gnu/libexec/uucp/libunix/remove.c @@ -0,0 +1,13 @@ +/* remove.c + Remove a file (Unix specific implementation). */ + +#include "uucp.h" + +#include "sysdep.h" + +int +remove (z) + const char *z; +{ + return unlink (z); +} diff --git a/gnu/libexec/uucp/libunix/rename.c b/gnu/libexec/uucp/libunix/rename.c new file mode 100644 index 000000000000..0947ef5cfaeb --- /dev/null +++ b/gnu/libexec/uucp/libunix/rename.c @@ -0,0 +1,27 @@ +/* rename.c + Rename a file to a new name (Unix specific implementation). */ + +#include "uucp.h" + +#include "sysdep.h" + +#include <errno.h> + +/* This implementation will not work on directories, but fortunately + we never want to rename directories. */ + +int +rename (zfrom, zto) + const char *zfrom; + const char *zto; +{ + if (link (zfrom, zto) < 0) + { + if (errno != EEXIST) + return -1; + if (unlink (zto) < 0 + || link (zfrom, zto) < 0) + return -1; + } + return unlink (zfrom); +} diff --git a/gnu/libexec/uucp/libunix/rmdir.c b/gnu/libexec/uucp/libunix/rmdir.c new file mode 100644 index 000000000000..12a7b9e4507c --- /dev/null +++ b/gnu/libexec/uucp/libunix/rmdir.c @@ -0,0 +1,43 @@ +/* rmdir.c + Remove a directory on a system which doesn't have the rmdir system + call. This is only called by uupick, which is not setuid, so we + don't have to worry about the problems of invoking the setuid + /bin/rmdir program. */ + +#include "uucp.h" + +#include "sysdep.h" + +#include <errno.h> + +int +rmdir (zdir) + const char *zdir; +{ + const char *azargs[3]; + int aidescs[3]; + pid_t ipid; + + azargs[0] = RMDIR_PROGRAM; + azargs[1] = zdir; + azargs[2] = NULL; + aidescs[0] = SPAWN_NULL; + aidescs[1] = SPAWN_NULL; + aidescs[2] = SPAWN_NULL; + + ipid = ixsspawn (azargs, aidescs, TRUE, FALSE, (const char *) NULL, + TRUE, TRUE, (const char *) NULL, + (const char *) NULL, (const char *) NULL); + + if (ipid < 0) + return -1; + + if (ixswait ((unsigned long) ipid, (const char *) NULL) != 0) + { + /* Make up an errno value. */ + errno = EBUSY; + return -1; + } + + return 0; +} diff --git a/gnu/libexec/uucp/libunix/run.c b/gnu/libexec/uucp/libunix/run.c new file mode 100644 index 000000000000..e21919628372 --- /dev/null +++ b/gnu/libexec/uucp/libunix/run.c @@ -0,0 +1,75 @@ +/* run.c + Run a program. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +/* Start up a new program and end the current one. We don't have to + worry about SIGHUP because the current process is either not a + process group leader (uucp, uux) or it does not have a controlling + terminal (uucico). */ + +boolean +fsysdep_run (zprogram, zarg1, zarg2) + const char *zprogram; + const char *zarg1; + const char *zarg2; +{ + char *zlib; + const char *azargs[4]; + int aidescs[3]; + pid_t ipid; + + zlib = zbufalc (sizeof SBINDIR + sizeof "/" + strlen (zprogram)); + sprintf (zlib, "%s/%s", SBINDIR, zprogram); + + azargs[0] = zlib; + azargs[1] = zarg1; + azargs[2] = zarg2; + azargs[3] = NULL; + + aidescs[0] = SPAWN_NULL; + aidescs[1] = SPAWN_NULL; + aidescs[2] = SPAWN_NULL; + + /* We pass fsetuid and fshell as TRUE, which permits uucico and + uuxqt to be replaced by (non-setuid) shell scripts. */ + ipid = ixsspawn (azargs, aidescs, TRUE, FALSE, (const char *) NULL, + FALSE, TRUE, (const char *) NULL, + (const char *) NULL, (const char *) NULL); + ubuffree (zlib); + if (ipid < 0) + { + ulog (LOG_ERROR, "ixsspawn: %s", strerror (errno)); + return FALSE; + } + + return TRUE; +} diff --git a/gnu/libexec/uucp/libunix/seq.c b/gnu/libexec/uucp/libunix/seq.c new file mode 100644 index 000000000000..2e01233022a9 --- /dev/null +++ b/gnu/libexec/uucp/libunix/seq.c @@ -0,0 +1,126 @@ +/* seq.c + Get and increment the conversation sequence number for a system. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +/* Get the current conversation sequence number for a remote system, + and increment it for next time. The conversation sequence number + is kept in a file named for the system in the directory .Sequence + in the spool directory. This is not compatible with other versions + of UUCP, but it makes more sense to me. The sequence file is only + used if specified in the information for that system. */ + +long +ixsysdep_get_sequence (qsys) + const struct uuconf_system *qsys; +{ + FILE *e; + char *zname; + struct stat s; + long iseq; + + /* This will only be called when the system is locked anyhow, so there + is no need to use a separate lock for the conversation sequence + file. */ + zname = zsysdep_in_dir (".Sequence", qsys->uuconf_zname); + + iseq = 0; + if (stat (zname, &s) == 0) + { + boolean fok; + char *zline; + size_t cline; + + /* The file should only be readable and writable by uucp. */ + if ((s.st_mode & (S_IRWXG | S_IRWXO)) != 0) + { + ulog (LOG_ERROR, + "Bad file protection for conversation sequence file"); + ubuffree (zname); + return -1; + } + + e = fopen (zname, "r+"); + if (e == NULL) + { + ulog (LOG_ERROR, "fopen (%s): %s", zname, strerror (errno)); + ubuffree (zname); + return -1; + } + + ubuffree (zname); + + fok = TRUE; + zline = NULL; + cline = 0; + if (getline (&zline, &cline, e) <= 0) + fok = FALSE; + else + { + char *zend; + + iseq = strtol (zline, &zend, 10); + if (zend == zline) + fok = FALSE; + } + + xfree ((pointer) zline); + + if (! fok) + { + ulog (LOG_ERROR, "Bad format for conversation sequence file"); + (void) fclose (e); + return -1; + } + + rewind (e); + } + else + { + e = esysdep_fopen (zname, FALSE, FALSE, TRUE); + ubuffree (zname); + if (e == NULL) + return -1; + } + + ++iseq; + + fprintf (e, "%ld", iseq); + + if (fclose (e) != 0) + { + ulog (LOG_ERROR, "fclose: %s", strerror (errno)); + return -1; + } + + return iseq; +} diff --git a/gnu/libexec/uucp/libunix/serial.c b/gnu/libexec/uucp/libunix/serial.c new file mode 100644 index 000000000000..cee90fcc7bb3 --- /dev/null +++ b/gnu/libexec/uucp/libunix/serial.c @@ -0,0 +1,2991 @@ +/* serial.c + The serial port communication routines for Unix. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char serial_rcsid[] = "$Id: serial.c,v 1.3 1994/02/07 23:47:51 ache Exp $"; +#endif + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" +#include "conn.h" +#include "sysdep.h" + +#include <errno.h> +#include <ctype.h> + +#if HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#if HAVE_LIMITS_H +#include <limits.h> +#endif + +#if HAVE_TLI +#if HAVE_TIUSER_H +#include <tiuser.h> +#else /* ! HAVE_TIUSER_H */ +#if HAVE_XTI_H +#include <xti.h> +#endif /* HAVE_XTI_H */ +#endif /* ! HAVE_TIUSER_H */ +#endif /* HAVE_TLI */ + +#if HAVE_FCNTL_H +#include <fcntl.h> +#else +#if HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +#if HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif + +#if HAVE_BSD_TTY +#include <sys/time.h> +#if HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif +#endif + +#if HAVE_TIME_H +#if HAVE_SYS_TIME_AND_TIME_H || ! HAVE_BSD_TTY +#include <time.h> +#endif +#endif + +#if HAVE_STRIP_BUG && HAVE_BSD_TTY +#include <termio.h> +#endif + +#if HAVE_SVR4_LOCKFILES +/* Get the right definitions for major and minor. */ +#if MAJOR_IN_MKDEV +#include <sys/mkdev.h> +#endif /* MAJOR_IN_MKDEV */ +#if MAJOR_IN_SYSMACROS +#include <sys/sysmacros.h> +#endif /* MAJOR_IN_SYSMACROS */ +#if ! MAJOR_IN_MKDEV && ! MAJOR_IN_SYSMACROS +#ifndef major +#define major(i) (((i) >> 8) & 0xff) +#endif +#ifndef minor +#define minor(i) ((i) & 0xff) +#endif +#endif /* ! MAJOR_IN_MKDEV && ! MAJOR_IN_SYSMACROS */ +#endif /* HAVE_SVR4_LOCKFILES */ + +/* Get definitions for both O_NONBLOCK and O_NDELAY. */ +#ifndef O_NDELAY +#ifdef FNDELAY +#define O_NDELAY FNDELAY +#else /* ! defined (FNDELAY) */ +#define O_NDELAY 0 +#endif /* ! defined (FNDELAY) */ +#endif /* ! defined (O_NDELAY) */ + +#ifndef O_NONBLOCK +#ifdef FNBLOCK +#define O_NONBLOCK FNBLOCK +#else /* ! defined (FNBLOCK) */ +#define O_NONBLOCK 0 +#endif /* ! defined (FNBLOCK) */ +#endif /* ! defined (O_NONBLOCK) */ + +#if O_NDELAY == 0 && O_NONBLOCK == 0 + #error No way to do nonblocking I/O +#endif + +/* Get definitions for EAGAIN, EWOULDBLOCK and ENODATA. */ +#ifndef EAGAIN +#ifndef EWOULDBLOCK +#define EAGAIN (-1) +#define EWOULDBLOCK (-1) +#else /* defined (EWOULDBLOCK) */ +#define EAGAIN EWOULDBLOCK +#endif /* defined (EWOULDBLOCK) */ +#else /* defined (EAGAIN) */ +#ifndef EWOULDBLOCK +#define EWOULDBLOCK EAGAIN +#endif /* ! defined (EWOULDBLOCK) */ +#endif /* defined (EAGAIN) */ + +#ifndef ENODATA +#define ENODATA EAGAIN +#endif + +/* Make sure we have a definition for MAX_INPUT. */ +#ifndef MAX_INPUT +#define MAX_INPUT (256) +#endif + +/* If we have the TIOCSINUSE ioctl call, we use it to lock a terminal. + Otherwise, if we have the TIOCEXCL ioctl call, we have to open the + terminal before we know that it is unlocked. */ +#ifdef TIOCSINUSE +#define HAVE_TIOCSINUSE 1 +#else +#ifdef TIOCEXCL +#define HAVE_TIOCEXCL 1 +#endif +#endif + +#if HAVE_TLI +extern int t_errno; +extern char *t_errlist[]; +extern int t_nerr; +#endif + +/* Determine bits to clear for the various terminal control fields for + HAVE_SYSV_TERMIO and HAVE_POSIX_TERMIOS. */ +#if HAVE_SYSV_TERMIO +#define ICLEAR_IFLAG (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK \ + | ISTRIP | INLCR | IGNCR | ICRNL | IUCLC \ + | IXON | IXANY | IXOFF) +#define ICLEAR_OFLAG (OPOST | OLCUC | ONLCR | OCRNL | ONOCR | ONLRET \ + | OFILL | OFDEL | NLDLY | CRDLY | TABDLY | BSDLY \ + | VTDLY | FFDLY) +#define ICLEAR_CFLAG (CBAUD | CLOCAL | CSIZE | PARENB | PARODD) +#define ISET_CFLAG (CS8 | CREAD | HUPCL) +#define ICLEAR_LFLAG (ISIG | ICANON | XCASE | ECHO | ECHOE | ECHOK \ + | ECHONL | NOFLSH) +#endif +#if HAVE_POSIX_TERMIOS +#ifdef IMAXBEL +#define CI_ADD1 IMAXBEL +#else +#define CI_ADD1 0 +#endif +#define ICLEAR_IFLAG (BRKINT | ICRNL | IGNBRK | IGNCR | IGNPAR \ + | INLCR | INPCK | ISTRIP | IXOFF | IXON \ + | PARMRK | CI_ADD1) +#define ICLEAR_OFLAG (OPOST) +#define ICLEAR_CFLAG (CLOCAL | CSIZE | PARENB | PARODD) +#define ISET_CFLAG (CS8 | CREAD | HUPCL) +#ifdef PENDIN +#define CL_ADD1 PENDIN +#else +#define CL_ADD1 0 +#endif +#define ICLEAR_LFLAG (ECHO | ECHOE | ECHOK | ECHONL | ICANON | IEXTEN \ + | ISIG | NOFLSH | TOSTOP | CL_ADD1) +#endif + +/* Local functions. */ + +static RETSIGTYPE usalarm P((int isig)); +static boolean fsserial_init P((struct sconnection *qconn, + const struct sconncmds *qcmds, + const char *zdevice)); +static void usserial_free P((struct sconnection *qconn)); +static boolean fsserial_lockfile P((boolean flok, + const struct sconnection *)); +static boolean fsserial_lock P((struct sconnection *qconn, + boolean fin)); +static boolean fsserial_unlock P((struct sconnection *qconn)); +static boolean fsserial_open P((struct sconnection *qconn, long ibaud, + boolean fwait)); +static boolean fsstdin_open P((struct sconnection *qconn, long ibaud, + boolean fwait)); +static boolean fsmodem_open P((struct sconnection *qconn, long ibaud, + boolean fwait)); +static boolean fsdirect_open P((struct sconnection *qconn, long ibaud, + boolean fwait)); +static boolean fsblock P((struct ssysdep_conn *q, boolean fblock)); +static boolean fsserial_close P((struct ssysdep_conn *q)); +static boolean fsstdin_close P((struct sconnection *qconn, + pointer puuconf, + struct uuconf_dialer *qdialer, + boolean fsuccess)); +static boolean fsmodem_close P((struct sconnection *qconn, + pointer puuconf, + struct uuconf_dialer *qdialer, + boolean fsuccess)); +static boolean fsdirect_close P((struct sconnection *qconn, + pointer puuconf, + struct uuconf_dialer *qdialer, + boolean fsuccess)); +static boolean fsserial_reset P((struct sconnection *qconn)); +static boolean fsstdin_reset P((struct sconnection *qconn)); +static boolean fsstdin_read P((struct sconnection *qconn, + char *zbuf, size_t *pclen, size_t cmin, + int ctimeout, boolean freport)); +static boolean fsstdin_write P((struct sconnection *qconn, + const char *zwrite, size_t cwrite)); +static boolean fsserial_break P((struct sconnection *qconn)); +static boolean fsstdin_break P((struct sconnection *qconn)); +static boolean fsserial_set P((struct sconnection *qconn, + enum tparitysetting tparity, + enum tstripsetting tstrip, + enum txonxoffsetting txonxoff)); +static boolean fsstdin_set P((struct sconnection *qconn, + enum tparitysetting tparity, + enum tstripsetting tstrip, + enum txonxoffsetting txonxoff)); +static boolean fsmodem_carrier P((struct sconnection *qconn, + boolean fcarrier)); +static boolean fsrun_chat P((int oread, int owrite, char **pzprog)); +static boolean fsstdin_chat P((struct sconnection *qconn, + char **pzprog)); +static long isserial_baud P((struct sconnection *qconn)); + +/* The command table for standard input ports. */ + +static const struct sconncmds sstdincmds = +{ + usserial_free, + NULL, /* pflock */ + NULL, /* pfunlock */ + fsstdin_open, + fsstdin_close, + fsstdin_reset, + NULL, /* pfdial */ + fsstdin_read, + fsstdin_write, + fsysdep_conn_io, + fsstdin_break, + fsstdin_set, + NULL, /* pfcarrier */ + fsstdin_chat, + isserial_baud +}; + +/* The command table for modem ports. */ + +static const struct sconncmds smodemcmds = +{ + usserial_free, + fsserial_lock, + fsserial_unlock, + fsmodem_open, + fsmodem_close, + fsserial_reset, + fmodem_dial, + fsysdep_conn_read, + fsysdep_conn_write, + fsysdep_conn_io, + fsserial_break, + fsserial_set, + fsmodem_carrier, + fsysdep_conn_chat, + isserial_baud +}; + +/* The command table for direct ports. */ + +static const struct sconncmds sdirectcmds = +{ + usserial_free, + fsserial_lock, + fsserial_unlock, + fsdirect_open, + fsdirect_close, + fsserial_reset, + NULL, /* pfdial */ + fsysdep_conn_read, + fsysdep_conn_write, + fsysdep_conn_io, + fsserial_break, + fsserial_set, + NULL, /* pfcarrier */ + fsysdep_conn_chat, + isserial_baud +}; + +/* If the system will let us set both O_NDELAY and O_NONBLOCK, we do + so. This is because some ancient drivers on some systems appear to + look for one but not the other. Some other systems will give an + EINVAL error if we attempt to set both, so we use a static global + to hold the value we want to set. If we get EINVAL, we change the + global and try again (if some system gives an error other than + EINVAL, the code will have to be modified). */ +static int iSunblock = O_NDELAY | O_NONBLOCK; + +/* This code handles SIGALRM. See the discussion above + fsysdep_conn_read. Normally we ignore SIGALRM, but the handler + will temporarily be set to this function, which should set fSalarm + and then either longjmp or schedule another SIGALRM. fSalarm is + never referred to outside of this file, but we don't make it static + to try to fool compilers which don't understand volatile. */ + +volatile sig_atomic_t fSalarm; + +static RETSIGTYPE +usalarm (isig) + int isig; +{ +#if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET + (void) signal (isig, usalarm); +#endif + + fSalarm = TRUE; + +#if HAVE_RESTARTABLE_SYSCALLS + longjmp (sSjmp_buf, 1); +#else + alarm (1); +#endif +} + +/* We need a simple routine to block SIGINT, SIGQUIT, SIGTERM and + SIGPIPE and another to restore the original state. When these + functions are called (in fsysdep_modem_close) SIGHUP is being + ignored. The routines are isblocksigs, which returns a value of + type HELD_SIG_MASK and usunblocksigs which takes a single argument + of type HELD_SIG_MASK. */ + +#if HAVE_SIGPROCMASK + +/* Use the POSIX sigprocmask call. */ + +#define HELD_SIG_MASK sigset_t + +static sigset_t isblocksigs P((void)); + +static sigset_t +isblocksigs () +{ + sigset_t sblock, sold; + + /* These expressions need an extra set of parentheses to avoid a bug + in SCO 3.2.2. */ + (void) (sigemptyset (&sblock)); + (void) (sigaddset (&sblock, SIGINT)); + (void) (sigaddset (&sblock, SIGQUIT)); + (void) (sigaddset (&sblock, SIGTERM)); + (void) (sigaddset (&sblock, SIGPIPE)); + + (void) sigprocmask (SIG_BLOCK, &sblock, &sold); + return sold; +} + +#define usunblocksigs(s) \ + ((void) sigprocmask (SIG_SETMASK, &(s), (sigset_t *) NULL)) + +#else /* ! HAVE_SIGPROCMASK */ +#if HAVE_SIGBLOCK + +/* Use the BSD sigblock and sigsetmask calls. */ + +#define HELD_SIG_MASK int + +#ifndef sigmask +#define sigmask(i) (1 << ((i) - 1)) +#endif + +#define isblocksigs() \ + sigblock (sigmask (SIGINT) | sigmask (SIGQUIT) \ + | sigmask (SIGTERM) | sigmask (SIGPIPE)) + +#define usunblocksigs(i) ((void) sigsetmask (i)) + +#else /* ! HAVE_SIGBLOCK */ + +#if HAVE_SIGHOLD + +/* Use the SVR3 sighold and sigrelse calls. */ + +#define HELD_SIG_MASK int + +static int isblocksigs P((void)); + +static int +isblocksigs () +{ + sighold (SIGINT); + sighold (SIGQUIT); + sighold (SIGTERM); + sighold (SIGPIPE); + return 0; +} + +static void usunblocksigs P((int)); + +/*ARGSUSED*/ +static void +usunblocksigs (i) + int i; +{ + sigrelse (SIGINT); + sigrelse (SIGQUIT); + sigrelse (SIGTERM); + sigrelse (SIGPIPE); +} + +#else /* ! HAVE_SIGHOLD */ + +/* We have no way to block signals. This system will suffer from a + race condition in fsysdep_modem_close. */ + +#define HELD_SIG_MASK int + +#define isblocksigs() 0 + +#define usunblocksigs(i) + +#endif /* ! HAVE_SIGHOLD */ +#endif /* ! HAVE_SIGBLOCK */ +#endif /* ! HAVE_SIGPROCMASK */ + +/* Initialize a connection for use on a serial port. */ + +static boolean +fsserial_init (qconn, qcmds, zdevice) + struct sconnection *qconn; + const struct sconncmds *qcmds; + const char *zdevice; +{ + struct ssysdep_conn *q; + + q = (struct ssysdep_conn *) xmalloc (sizeof (struct ssysdep_conn)); + if (zdevice == NULL + && qconn->qport != NULL + && qconn->qport->uuconf_ttype != UUCONF_PORTTYPE_STDIN) + zdevice = qconn->qport->uuconf_zname; + if (zdevice == NULL) + q->zdevice = NULL; + else if (*zdevice == '/') + q->zdevice = zbufcpy (zdevice); + else + { + size_t clen; + + clen = strlen (zdevice); + q->zdevice = zbufalc (sizeof "/dev/" + clen); + memcpy (q->zdevice, "/dev/", sizeof "/dev/" - 1); + memcpy (q->zdevice + sizeof "/dev/" - 1, zdevice, clen); + q->zdevice[sizeof "/dev/" + clen - 1] = '\0'; + } + q->o = -1; + q->ftli = FALSE; + qconn->psysdep = (pointer) q; + qconn->qcmds = qcmds; + return TRUE; +} + +/* Initialize a connection for use on standard input. */ + +boolean +fsysdep_stdin_init (qconn) + struct sconnection *qconn; +{ + return fsserial_init (qconn, &sstdincmds, (const char *) NULL); +} + +/* Initialize a connection for use on a modem port. */ + +boolean +fsysdep_modem_init (qconn) + struct sconnection *qconn; +{ + return fsserial_init (qconn, &smodemcmds, + qconn->qport->uuconf_u.uuconf_smodem.uuconf_zdevice); +} + +/* Initialize a connection for use on a direct port. */ + +boolean +fsysdep_direct_init (qconn) + struct sconnection *qconn; +{ + return fsserial_init (qconn, &sdirectcmds, + qconn->qport->uuconf_u.uuconf_sdirect.uuconf_zdevice); +} + +/* Free up a serial port. */ + +static void +usserial_free (qconn) + struct sconnection *qconn; +{ + struct ssysdep_conn *qsysdep; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + ubuffree (qsysdep->zdevice); + xfree ((pointer) qsysdep); + qconn->psysdep = NULL; +} + +/* This routine is used for both locking and unlocking. It is the + only routine which knows how to translate a device name into the + name of a lock file. If it can't figure out a name, it does + nothing and returns TRUE. */ + +static boolean +fsserial_lockfile (flok, qconn) + boolean flok; + const struct sconnection *qconn; +{ + struct ssysdep_conn *qsysdep; + const char *z; + char *zalc; + boolean fret; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + if (qconn->qport == NULL) + z = NULL; + else + z = qconn->qport->uuconf_zlockname; + zalc = NULL; + if (z == NULL) + { +#if ! HAVE_SVR4_LOCKFILES + { + const char *zbase; + size_t clen; + + zbase = strrchr (qsysdep->zdevice, '/') + 1; + clen = strlen (zbase); + zalc = zbufalc (sizeof "LCK.." + clen); + memcpy (zalc, "LCK..", sizeof "LCK.." - 1); + memcpy (zalc + sizeof "LCK.." - 1, zbase, clen + 1); +#if HAVE_SCO_LOCKFILES + { + char *zl; + + for (zl = zalc + sizeof "LCK.." - 1; *zl != '\0'; zl++) + if (isupper (*zl)) + *zl = tolower (*zl); + } +#endif + z = zalc; + } +#else /* ! HAVE_SVR4_LOCKFILES */ +#if HAVE_SVR4_LOCKFILES + { + struct stat s; + + if (stat (qsysdep->zdevice, &s) != 0) + { + ulog (LOG_ERROR, "stat (%s): %s", qsysdep->zdevice, + strerror (errno)); + return FALSE; + } + zalc = zbufalc (sizeof "LK.123.123.123"); + sprintf (zalc, "LK.%03d.%03d.%03d", major (s.st_dev), + major (s.st_rdev), minor (s.st_rdev)); + z = zalc; + } +#else /* ! HAVE_SVR4_LOCKFILES */ + z = strrchr (qsysdep->zdevice, '/') + 1; +#endif /* ! HAVE_SVR4_LOCKFILES */ +#endif /* ! HAVE_SVR4_LOCKFILES */ + } + + if (flok) + fret = fsdo_lock (z, FALSE, (boolean *) NULL); + else + fret = fsdo_unlock (z, FALSE); + +#if HAVE_COHERENT_LOCKFILES + if (fret) + { + if (flok) + { + if (lockttyexist (z)) + { + ulog (LOG_NORMAL, "%s: port already locked", z+5); + fret = FALSE; + } + else + fret = !(fscoherent_disable_tty (z, &qsysdep->zenable)); + } + else + { + fret = TRUE; + if (qsysdep->zenable != NULL) + { + const char *azargs[3]; + int aidescs[3]; + pid_t ipid; + + azargs[0] = "/etc/enable"; + azargs[1] = qsysdep->zenable; + azargs[2] = NULL; + aidescs[0] = SPAWN_NULL; + aidescs[1] = SPAWN_NULL; + aidescs[2] = SPAWN_NULL; + + ipid = ixsspawn (azargs, aidescs, TRUE, FALSE, + (const char *) NULL, TRUE, TRUE, + (const char *) NULL, (const char *) NULL, + (const char *) NULL); + if (ipid < 0) + { + ulog (LOG_ERROR, "ixsspawn (/etc/enable %s): %s", + qsysdep->zenable, strerror (errno)); + fret = FALSE; + } + else + { + if (ixswait ((unsigned long) ipid, (const char *) NULL) + == 0) + fret = TRUE; + else + fret = FALSE; + } + ubuffree (qsysdep->zenable); + qsysdep->zenable = NULL; + } + } + } +#endif /* HAVE_COHERENT_LOCKFILES */ + + ubuffree (zalc); + return fret; +} + +/* If we can mark a modem line in use, then when we lock a port we + must open it and mark it in use. We can't wait until the actual + open because we can't fail out if it is locked then. */ + +static boolean +fsserial_lock (qconn, fin) + struct sconnection *qconn; + boolean fin; +{ + if (! fsserial_lockfile (TRUE, qconn)) + return FALSE; + +#if HAVE_TIOCSINUSE || HAVE_TIOCEXCL + /* Open the line and try to mark it in use. */ + { + struct ssysdep_conn *qsysdep; + int iflag; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + + if (fin) + iflag = 0; + else + iflag = iSunblock; + + qsysdep->o = open (qsysdep->zdevice, O_RDWR | iflag); + if (qsysdep->o < 0) + { +#if O_NONBLOCK != 0 + if (! fin && iSunblock != O_NONBLOCK && errno == EINVAL) + { + iSunblock = O_NONBLOCK; + qsysdep->o = open (qsysdep->zdevice, + O_RDWR | O_NONBLOCK); + } +#endif + if (qsysdep->o < 0) + { + if (errno != EBUSY) + ulog (LOG_ERROR, "open (%s): %s", qsysdep->zdevice, + strerror (errno)); + (void) fsserial_lockfile (FALSE, qconn); + return FALSE; + } + } + +#if HAVE_TIOCSINUSE + /* If we can't mark it in use, return FALSE to indicate that the + lock failed. */ + if (ioctl (qsysdep->o, TIOCSINUSE, 0) < 0) + { + if (errno != EALREADY) + ulog (LOG_ERROR, "ioctl (TIOCSINUSE): %s", strerror (errno)); +#ifdef TIOCNOTTY + (void) ioctl (qsysdep->o, TIOCNOTTY, (char *) NULL); +#endif + (void) close (qsysdep->o); + qsysdep->o = -1; + (void) fsserial_lockfile (FALSE, qconn); + return FALSE; + } +#endif + + if (fcntl (qsysdep->o, F_SETFD, + fcntl (qsysdep->o, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); +#ifdef TIOCNOTTY + (void) ioctl (qsysdep->o, TIOCNOTTY, (char *) NULL); +#endif + (void) close (qsysdep->o); + qsysdep->o = -1; + (void) fsserial_lockfile (FALSE, qconn); + return FALSE; + } + +#ifdef TIOCSCTTY + /* On BSD 4.4, make it our controlling terminal. */ + (void) ioctl (qsysdep->o, TIOCSCTTY, 0); +#endif + } +#endif /* HAVE_TIOCSINUSE || HAVE_TIOCEXCL */ + + return TRUE; +} + +/* Unlock a modem or direct port. */ + +static boolean +fsserial_unlock (qconn) + struct sconnection *qconn; +{ + boolean fret; + struct ssysdep_conn *qsysdep; + + fret = TRUE; + + /* The file may have been opened by fsserial_lock, so close it here + if necessary. */ + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + if (qsysdep->o >= 0) + { +#ifdef TIOCNOTTY + (void) ioctl (qsysdep->o, TIOCNOTTY, (char *) NULL); +#endif + if (close (qsysdep->o) < 0) + { + ulog (LOG_ERROR, "close: %s", strerror (errno)); + fret = FALSE; + } + qsysdep->o = -1; + } + + if (! fsserial_lockfile (FALSE, qconn)) + fret = FALSE; + + return fret; +} + +/* Open a serial line. This sets the terminal settings. We begin in + seven bit mode and let the protocol change if necessary. */ + +#if HAVE_POSIX_TERMIOS +typedef speed_t baud_code; +#else +typedef int baud_code; +#endif + +static struct sbaud_table +{ + baud_code icode; + long ibaud; +} asSbaud_table[] = +{ + { B50, 50 }, + { B75, 75 }, + { B110, 110 }, + { B134, 134 }, + { B150, 150 }, + { B200, 200 }, + { B300, 300 }, + { B600, 600 }, + { B1200, 1200 }, + { B1800, 1800 }, + { B2400, 2400 }, + { B4800, 4800 }, + { B9600, 9600 }, +#ifdef B19200 + { B19200, 19200 }, +#else /* ! defined (B19200) */ +#ifdef EXTA + { EXTA, 19200 }, +#endif /* EXTA */ +#endif /* ! defined (B19200) */ +#ifdef B38400 + { B38400, 38400 }, +#else /* ! defined (B38400) */ +#ifdef EXTB + { EXTB, 38400 }, +#endif /* EXTB */ +#endif /* ! defined (B38400) */ +#ifdef B57600 + { B57600, 57600 }, +#endif +#ifdef B76800 + { B76800, 76800 }, +#endif +#ifdef B115200 + { B115200, 115200 }, +#endif + { B0, 0 } +}; + +#define CBAUD_TABLE (sizeof asSbaud_table / sizeof asSbaud_table[0]) + +#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS +/* Hold the MIN value for the terminal to avoid setting it + unnecessarily. */ +static int cSmin; +#endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */ + +static boolean +fsserial_open (qconn, ibaud, fwait) + struct sconnection *qconn; + long ibaud; + boolean fwait; +{ + struct ssysdep_conn *q; + baud_code ib; + + q = (struct ssysdep_conn *) qconn->psysdep; + + if (q->zdevice != NULL) + ulog_device (strrchr (q->zdevice, '/') + 1); + else + { + const char *zport; + boolean fdummy; + +#if DEBUG > 0 + if (qconn->qport != NULL && + qconn->qport->uuconf_ttype != UUCONF_PORTTYPE_STDIN) + ulog (LOG_FATAL, "fsserial_open: Can't happen"); +#endif + zport = zsysdep_port_name (&fdummy); + if (zport != NULL) + ulog_device (zport); + } + + ib = B0; + if (ibaud != 0) + { + int i; + + for (i = 0; i < CBAUD_TABLE; i++) + if (asSbaud_table[i].ibaud == ibaud) + break; + if (i >= CBAUD_TABLE) + { + ulog (LOG_ERROR, "Unsupported baud rate %ld", ibaud); + return FALSE; + } + ib = asSbaud_table[i].icode; + } + + /* The port may have already been opened by the locking routine. */ + if (q->o < 0) + { + int iflag; + + if (fwait) + iflag = 0; + else + iflag = iSunblock; + + q->o = open (q->zdevice, O_RDWR | iflag); + if (q->o < 0) + { +#if O_NONBLOCK != 0 + if (! fwait && iSunblock != O_NONBLOCK && errno == EINVAL) + { + iSunblock = O_NONBLOCK; + q->o = open (q->zdevice, O_RDWR | O_NONBLOCK); + } +#endif + if (q->o < 0) + { + ulog (LOG_ERROR, "open (%s): %s", q->zdevice, + strerror (errno)); + return FALSE; + } + } + + if (fcntl (q->o, F_SETFD, fcntl (q->o, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + return FALSE; + } + } + + /* Get the port flags, and make sure the ports are blocking. */ + + q->iflags = fcntl (q->o, F_GETFL, 0); + if (q->iflags < 0) + { + ulog (LOG_ERROR, "fcntl: %s", strerror (errno)); + return FALSE; + } + q->istdout_flags = -1; + + if (! fgetterminfo (q->o, &q->sorig)) + { + q->fterminal = FALSE; + return TRUE; + } + + q->fterminal = TRUE; + + q->snew = q->sorig; + +#if HAVE_BSD_TTY + + q->snew.stty.sg_flags = RAW | ANYP; + if (ibaud == 0) + ib = q->snew.stty.sg_ospeed; + else + { + q->snew.stty.sg_ispeed = ib; + q->snew.stty.sg_ospeed = ib; + } + + /* We don't want to receive any interrupt characters. */ + q->snew.stchars.t_intrc = -1; + q->snew.stchars.t_quitc = -1; + q->snew.stchars.t_eofc = -1; + q->snew.stchars.t_brkc = -1; + q->snew.sltchars.t_suspc = -1; + q->snew.sltchars.t_rprntc = -1; + q->snew.sltchars.t_dsuspc = -1; + q->snew.sltchars.t_flushc = -1; + q->snew.sltchars.t_werasc = -1; + q->snew.sltchars.t_lnextc = -1; + +#ifdef NTTYDISC + /* We want to use the ``new'' terminal driver so that we can use the + local mode bits to control XON/XOFF. */ + { + int iparam; + + if (ioctl (q->o, TIOCGETD, &iparam) >= 0 + && iparam != NTTYDISC) + { + iparam = NTTYDISC; + (void) ioctl (q->o, TIOCSETD, &iparam); + } + } +#endif + +#ifdef TIOCHPCL + /* When the file is closed, hang up the line. This is a safety + measure in case the program crashes. */ + (void) ioctl (q->o, TIOCHPCL, 0); +#endif + +#ifdef TIOCFLUSH + { + int iparam; + + /* Flush pending input. */ +#ifdef FREAD + iparam = FREAD; +#else + iparam = 0; +#endif + (void) ioctl (q->o, TIOCFLUSH, &iparam); + } +#endif /* TIOCFLUSH */ + +#endif /* HAVE_BSD_TTY */ + +#if HAVE_SYSV_TERMIO + + if (ibaud == 0) + ib = q->snew.c_cflag & CBAUD; + + q->snew.c_iflag &=~ ICLEAR_IFLAG; + q->snew.c_oflag &=~ ICLEAR_OFLAG; + q->snew.c_cflag &=~ ICLEAR_CFLAG; + if (!fwait) + q->snew.c_cflag |= CLOCAL; + q->snew.c_cflag |= (ib | ISET_CFLAG); + q->snew.c_lflag &=~ ICLEAR_LFLAG; + cSmin = 1; + q->snew.c_cc[VMIN] = cSmin; + q->snew.c_cc[VTIME] = 0; + +#ifdef TCFLSH + /* Flush pending input. */ + (void) ioctl (q->o, TCFLSH, 0); +#endif + +#endif /* HAVE_SYSV_TERMIO */ + +#if HAVE_POSIX_TERMIOS + + if (ibaud == 0) + ib = cfgetospeed (&q->snew); + + q->snew.c_iflag &=~ ICLEAR_IFLAG; + q->snew.c_oflag &=~ ICLEAR_OFLAG; + q->snew.c_cflag &=~ ICLEAR_CFLAG; + if (!fwait) + q->snew.c_cflag |= CLOCAL; + q->snew.c_cflag |= ISET_CFLAG; + q->snew.c_lflag &=~ ICLEAR_LFLAG; + cSmin = 1; + q->snew.c_cc[VMIN] = cSmin; + q->snew.c_cc[VTIME] = 0; + + (void) cfsetospeed (&q->snew, ib); + (void) cfsetispeed (&q->snew, ib); + + /* Flush pending input. */ + (void) tcflush (q->o, TCIFLUSH); + +#endif /* HAVE_POSIX_TERMIOS */ + + if (! fsetterminfo (q->o, &q->snew)) + { + ulog (LOG_ERROR, "Can't set terminal settings: %s", strerror (errno)); + return FALSE; + } + +#ifdef TIOCSCTTY + /* On BSD 4.4, make it our controlling terminal. */ + (void) ioctl (q->o, TIOCSCTTY, 0); +#endif + + if (ibaud != 0) + q->ibaud = ibaud; + else + { + int i; + + q->ibaud = (long) 1200; + for (i = 0; i < CBAUD_TABLE; i++) + { + if (asSbaud_table[i].icode == ib) + { + q->ibaud = asSbaud_table[i].ibaud; + break; + } + } + + DEBUG_MESSAGE1 (DEBUG_PORT, + "fsserial_open: Baud rate is %ld", q->ibaud); + } + + return TRUE; +} + +/* Open a standard input port. The code alternates q->o between 0 and + 1 as appropriate. It is always 0 before any call to fsblock. */ + +static boolean +fsstdin_open (qconn, ibaud, fwait) + struct sconnection *qconn; + long ibaud; + boolean fwait; +{ + struct ssysdep_conn *q; + + q = (struct ssysdep_conn *) qconn->psysdep; + q->o = 0; + if (! fsserial_open (qconn, ibaud, fwait)) + return FALSE; + q->istdout_flags = fcntl (1, F_GETFL, 0); + if (q->istdout_flags < 0) + { + ulog (LOG_ERROR, "fcntl: %s", strerror (errno)); + return FALSE; + } + return TRUE; +} + +/* Open a modem port. */ + +static boolean +fsmodem_open (qconn, ibaud, fwait) + struct sconnection *qconn; + long ibaud; + boolean fwait; +{ + if (ibaud == (long) 0) + ibaud = qconn->qport->uuconf_u.uuconf_smodem.uuconf_ibaud; + return fsserial_open (qconn, ibaud, fwait); +} + +/* Open a direct port. */ + +static boolean +fsdirect_open (qconn, ibaud, fwait) + struct sconnection *qconn; + long ibaud; + boolean fwait; +{ + if (ibaud == (long) 0) + ibaud = qconn->qport->uuconf_u.uuconf_sdirect.uuconf_ibaud; + return fsserial_open (qconn, ibaud, fwait); +} + +/* Change the blocking status of the port. We keep track of the + current blocking status to avoid calling fcntl unnecessarily; fcntl + turns out to be surprisingly expensive, at least on Ultrix. */ + +static boolean +fsblock (qs, fblock) + struct ssysdep_conn *qs; + boolean fblock; +{ + int iwant; + int isys; + + if (fblock) + iwant = qs->iflags &~ (O_NDELAY | O_NONBLOCK); + else + iwant = qs->iflags | iSunblock; + + if (iwant == qs->iflags) + return TRUE; + + isys = fcntl (qs->o, F_SETFL, iwant); + if (isys < 0) + { +#if O_NONBLOCK != 0 + if (! fblock && iSunblock != O_NONBLOCK && errno == EINVAL) + { + iSunblock = O_NONBLOCK; + iwant = qs->iflags | O_NONBLOCK; + isys = fcntl (qs->o, F_SETFL, iwant); + } +#endif + if (isys < 0) + { + ulog (LOG_ERROR, "fcntl: %s", strerror (errno)); + return FALSE; + } + } + + qs->iflags = iwant; + + if (qs->istdout_flags >= 0) + { + if (fblock) + iwant = qs->istdout_flags &~ (O_NDELAY | O_NONBLOCK); + else + iwant = qs->istdout_flags | iSunblock; + + if (fcntl (1, F_SETFL, iwant) < 0) + { + /* We don't bother to fix up iSunblock here, since we + succeeded above. */ + ulog (LOG_ERROR, "fcntl: %s", strerror (errno)); + return FALSE; + } + + qs->istdout_flags = iwant; + } + + return TRUE; +} + +/* Close a serial port. */ + +static boolean +fsserial_close (q) + struct ssysdep_conn *q; +{ + if (q->o >= 0) + { + /* Use a 30 second timeout to avoid hanging while draining + output. */ + if (q->fterminal) + { + fSalarm = FALSE; + + if (fsysdep_catch ()) + { + usysdep_start_catch (); + usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL); + (void) alarm (30); + + (void) fsetterminfodrain (q->o, &q->sorig); + } + + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + (void) alarm (0); + usysdep_end_catch (); + + /* If we timed out, use the non draining call. Hopefully + this can't hang. */ + if (fSalarm) + (void) fsetterminfo (q->o, &q->sorig); + } + +#ifdef TIOCNOTTY + /* We don't want this as our controlling terminal any more, so + get rid of it. This is necessary because we don't want to + open /dev/tty, since that can confuse the serial port locking + on some computers. */ + (void) ioctl (q->o, TIOCNOTTY, (char *) NULL); +#endif + + (void) close (q->o); + q->o = -1; + + /* Sleep to give the terminal a chance to settle, in case we are + about to call out again. */ + sleep (2); + } + + return TRUE; +} + +/* Close a stdin port. */ + +/*ARGSUSED*/ +static boolean +fsstdin_close (qconn, puuconf, qdialer, fsuccess) + struct sconnection *qconn; + pointer puuconf; + struct uuconf_dialer *qdialer; + boolean fsuccess; +{ + struct ssysdep_conn *qsysdep; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + (void) close (1); + (void) close (2); + qsysdep->o = 0; + return fsserial_close (qsysdep); +} + +/* Close a modem port. */ + +static boolean +fsmodem_close (qconn, puuconf, qdialer, fsuccess) + struct sconnection *qconn; + pointer puuconf; + struct uuconf_dialer *qdialer; + boolean fsuccess; +{ + struct ssysdep_conn *qsysdep; + boolean fret; + struct uuconf_dialer sdialer; + const struct uuconf_chat *qchat; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + + fret = TRUE; + + /* Figure out the dialer so that we can run the complete or abort + chat scripts. */ + if (qdialer == NULL) + { + if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_pzdialer != NULL) + { + const char *zdialer; + int iuuconf; + + zdialer = qconn->qport->uuconf_u.uuconf_smodem.uuconf_pzdialer[0]; + iuuconf = uuconf_dialer_info (puuconf, zdialer, &sdialer); + if (iuuconf == UUCONF_SUCCESS) + qdialer = &sdialer; + else + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + fret = FALSE; + } + } + else + qdialer = qconn->qport->uuconf_u.uuconf_smodem.uuconf_qdialer; + } + + /* Get the complete or abort chat script to use. */ + qchat = NULL; + if (qdialer != NULL) + { + if (fsuccess) + qchat = &qdialer->uuconf_scomplete; + else + qchat = &qdialer->uuconf_sabort; + } + + if (qchat != NULL + && (qchat->uuconf_pzprogram != NULL + || qchat->uuconf_pzchat != NULL)) + { + boolean fsighup_ignored; + HELD_SIG_MASK smask; + int i; + sig_atomic_t afhold[INDEXSIG_COUNT]; + + /* We're no longer interested in carrier. */ + (void) fsmodem_carrier (qconn, FALSE); + + /* The port I/O routines check whether any signal has been + received, and abort if one has. While we are closing down + the modem, we don't care if we received a signal in the past, + but we do care if we receive a new signal (otherwise it would + be difficult to kill a uucico which was closing down a + modem). We never care if we get SIGHUP at this point. So we + turn off SIGHUP, remember what signals we've already seen, + and clear our notion of what signals we've seen. We have to + block the signals while we remember and clear the array, + since we might otherwise miss a signal which occurred between + the copy and the clear (old systems can't block signals; they + will just have to suffer the race). */ + usset_signal (SIGHUP, SIG_IGN, FALSE, &fsighup_ignored); + smask = isblocksigs (); + for (i = 0; i < INDEXSIG_COUNT; i++) + { + afhold[i] = afSignal[i]; + afSignal[i] = FALSE; + } + usunblocksigs (smask); + + if (! fchat (qconn, puuconf, qchat, (const struct uuconf_system *) NULL, + (const struct uuconf_dialer *) NULL, (const char *) NULL, + FALSE, qconn->qport->uuconf_zname, + qsysdep->ibaud)) + fret = FALSE; + + /* Restore the old signal array and the SIGHUP handler. It is + not necessary to block signals here, since all we are doing + is exactly what the signal handler itself would do if the + signal occurred. */ + for (i = 0; i < INDEXSIG_COUNT; i++) + if (afhold[i]) + afSignal[i] = TRUE; + if (! fsighup_ignored) + usset_signal (SIGHUP, ussignal, TRUE, (boolean *) NULL); + } + + if (qdialer != NULL + && qdialer == &sdialer) + (void) uuconf_dialer_free (puuconf, &sdialer); + +#if ! HAVE_RESET_BUG + /* Reset the terminal to make sure we drop DTR. It should be + dropped when we close the descriptor, but that doesn't seem to + happen on some systems. Use a 30 second timeout to avoid hanging + while draining output. */ + if (qsysdep->fterminal) + { +#if HAVE_BSD_TTY + qsysdep->snew.stty.sg_ispeed = B0; + qsysdep->snew.stty.sg_ospeed = B0; +#endif +#if HAVE_SYSV_TERMIO + qsysdep->snew.c_cflag = (qsysdep->snew.c_cflag &~ CBAUD) | B0; +#endif +#if HAVE_POSIX_TERMIOS + (void) cfsetospeed (&qsysdep->snew, B0); +#endif + + fSalarm = FALSE; + + if (fsysdep_catch ()) + { + usysdep_start_catch (); + usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL); + (void) alarm (30); + + (void) fsetterminfodrain (qsysdep->o, &qsysdep->snew); + } + + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + (void) alarm (0); + usysdep_end_catch (); + + /* Let the port settle. */ + sleep (2); + } +#endif /* ! HAVE_RESET_BUG */ + + if (! fsserial_close (qsysdep)) + fret = FALSE; + + return fret; +} + +/* Close a direct port. */ + +/*ARGSUSED*/ +static boolean +fsdirect_close (qconn, puuconf, qdialer, fsuccess) + struct sconnection *qconn; + pointer puuconf; + struct uuconf_dialer *qdialer; + boolean fsuccess; +{ + return fsserial_close ((struct ssysdep_conn *) qconn->psysdep); +} + +/* Reset a serial port by hanging up. */ + +static boolean +fsserial_reset (qconn) + struct sconnection *qconn; +{ + struct ssysdep_conn *q; + sterminal sbaud; + + q = (struct ssysdep_conn *) qconn->psysdep; + + if (! q->fterminal) + return TRUE; + + sbaud = q->snew; + +#if HAVE_BSD_TTY + sbaud.stty.sg_ispeed = B0; + sbaud.stty.sg_ospeed = B0; +#endif +#if HAVE_SYSV_TERMIO + sbaud.c_cflag = (sbaud.c_cflag &~ CBAUD) | B0; +#endif +#if HAVE_POSIX_TERMIOS + if (cfsetospeed (&sbaud, B0) < 0) + { + ulog (LOG_ERROR, "Can't set baud rate: %s", strerror (errno)); + return FALSE; + } +#endif + + if (! fsetterminfodrain (q->o, &sbaud)) + { + ulog (LOG_ERROR, "Can't hangup terminal: %s", strerror (errno)); + return FALSE; + } + + /* Give the terminal a chance to settle. */ + sleep (2); + + if (! fsetterminfo (q->o, &q->snew)) + { + ulog (LOG_ERROR, "Can't reopen terminal: %s", strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/* Reset a standard input port. */ + +static boolean +fsstdin_reset (qconn) + struct sconnection *qconn; +{ + struct ssysdep_conn *qsysdep; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + qsysdep->o = 0; + return fsserial_reset (qconn); +} + +/* Begin dialing out on a modem port. This opens the dialer device if + there is one. */ + +boolean +fsysdep_modem_begin_dial (qconn, qdial) + struct sconnection *qconn; + struct uuconf_dialer *qdial; +{ + struct ssysdep_conn *qsysdep; + const char *z; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + +#ifdef TIOCMODEM + /* If we can tell the modem to obey modem control, do so. */ + { + int iperm; + + iperm = 0; + (void) ioctl (qsysdep->o, TIOCMODEM, &iperm); + } +#endif /* TIOCMODEM */ + + /* If we supposed to toggle DTR, do so. */ + + if (qdial->uuconf_fdtr_toggle) + { +#ifdef TIOCCDTR + (void) ioctl (qsysdep->o, TIOCCDTR, 0); + sleep (2); + (void) ioctl (qsysdep->o, TIOCSDTR, 0); +#else /* ! defined (TIOCCDTR) */ + (void) fconn_reset (qconn); +#endif /* ! defined (TIOCCDTR) */ + + if (qdial->uuconf_fdtr_toggle_wait) + sleep (2); + } + + if (! fsmodem_carrier (qconn, FALSE)) + return FALSE; + + /* Open the dial device if there is one. */ + z = qconn->qport->uuconf_u.uuconf_smodem.uuconf_zdial_device; + if (z != NULL) + { + char *zfree; + int o; + + qsysdep->ohold = qsysdep->o; + + zfree = NULL; + if (*z != '/') + { + zfree = zbufalc (sizeof "/dev/" + strlen (z)); + sprintf (zfree, "/dev/%s", z); + z = zfree; + } + + o = open ((char *) z, O_RDWR | O_NOCTTY); + if (o < 0) + { + ulog (LOG_ERROR, "open (%s): %s", z, strerror (errno)); + ubuffree (zfree); + return FALSE; + } + ubuffree (zfree); + + if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + (void) close (o); + return FALSE; + } + + qsysdep->o = o; + } + + return TRUE; +} + +/* Tell the port to require or not require carrier. On BSD this uses + TIOCCAR and TIOCNCAR, which I assume are generally supported (it + can also use the LNOMDM bit supported by IS68K Unix). On System V + it resets or sets CLOCAL. We only require carrier if the port + supports it. This will only be called with fcarrier TRUE if the + dialer supports carrier. */ + +static boolean +fsmodem_carrier (qconn, fcarrier) + struct sconnection *qconn; + boolean fcarrier; +{ + register struct ssysdep_conn *q; + + q = (struct ssysdep_conn *) qconn->psysdep; + + if (! q->fterminal) + return TRUE; + + if (fcarrier) + { + if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_fcarrier) + { +#ifdef TIOCCAR + /* Tell the modem to pay attention to carrier. */ + if (ioctl (q->o, TIOCCAR, 0) < 0) + { + ulog (LOG_ERROR, "ioctl (TIOCCAR): %s", strerror (errno)); + return FALSE; + } +#endif /* TIOCCAR */ + +#if HAVE_BSD_TTY +#ifdef LNOMDM + /* IS68K Unix uses a local LNOMDM bit. */ + { + int iparam; + + iparam = LNOMDM; + if (ioctl (q->o, TIOCLBIC, &iparam) < 0) + { + ulog (LOG_ERROR, "ioctl (TIOCLBIC, LNOMDM): %s", + strerror (errno)); + return FALSE; + } + } +#endif /* LNOMDM */ +#endif /* HAVE_BSD_TTY */ + +#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS + /* Put the modem into nonlocal mode. */ + q->snew.c_cflag &=~ CLOCAL; + if (! fsetterminfo (q->o, &q->snew)) + { + ulog (LOG_ERROR, "Can't clear CLOCAL: %s", strerror (errno)); + return FALSE; + } +#endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */ + } + } + else + { +#ifdef TIOCNCAR + /* Tell the modem to ignore carrier. */ + if (ioctl (q->o, TIOCNCAR, 0) < 0) + { + ulog (LOG_ERROR, "ioctl (TIOCNCAR): %s", strerror (errno)); + return FALSE; + } +#endif /* TIOCNCAR */ + +#if HAVE_BSD_TTY +#ifdef LNOMDM + /* IS68K Unix uses a local LNOMDM bit. */ + { + int iparam; + + iparam = LNOMDM; + if (ioctl (q->o, TIOCLBIS, &iparam) < 0) + { + ulog (LOG_ERROR, "ioctl (TIOCLBIS, LNOMDM): %s", + strerror (errno)); + return FALSE; + } + } +#endif /* LNOMDM */ +#endif /* HAVE_BSD_TTY */ + +#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS + /* Put the modem into local mode (ignore carrier) to start the chat + script. */ + q->snew.c_cflag |= CLOCAL; + if (! fsetterminfo (q->o, &q->snew)) + { + ulog (LOG_ERROR, "Can't set CLOCAL: %s", strerror (errno)); + return FALSE; + } + +#if HAVE_CLOCAL_BUG + /* On SCO and AT&T UNIX PC you have to reopen the port. */ + { + int onew; + + onew = open (q->zdevice, O_RDWR); + if (onew < 0) + { + ulog (LOG_ERROR, "open (%s): %s", q->zdevice, strerror (errno)); + return FALSE; + } + + if (fcntl (onew, F_SETFD, + fcntl (onew, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + (void) close (onew); + return FALSE; + } + + (void) close (q->o); + q->o = onew; + } +#endif /* HAVE_CLOCAL_BUG */ + +#endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */ + } + + return TRUE; +} + +/* Finish dialing out on a modem by closing any dialer device and waiting + for carrier. */ + +boolean +fsysdep_modem_end_dial (qconn, qdial) + struct sconnection *qconn; + struct uuconf_dialer *qdial; +{ + struct ssysdep_conn *q; + + q = (struct ssysdep_conn *) qconn->psysdep; + + if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_zdial_device != NULL) + { + (void) close (q->o); + q->o = q->ohold; + } + + if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_fcarrier + && qdial->uuconf_fcarrier) + { + /* Tell the port that we need carrier. */ + if (! fsmodem_carrier (qconn, TRUE)) + return FALSE; + +#ifdef TIOCWONLINE + + /* We know how to wait for carrier, so do so. */ + + /* If we already got a signal, just quit now. */ + if (FGOT_QUIT_SIGNAL ()) + return FALSE; + + /* This bit of code handles signals just like fsysdep_conn_read + does. See that function for a longer explanation. */ + + /* Use fsysdep_catch to handle a longjmp from the signal + handler. */ + + fSalarm = FALSE; + + if (fsysdep_catch ()) + { + /* Start catching SIGALRM; normally we ignore it. */ + usysdep_start_catch (); + usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL); + (void) alarm (qdial->uuconf_ccarrier_wait); + + /* We really don't care if we get an error, since that will + probably just mean that TIOCWONLINE isn't supported in + which case there's nothing we can do anyhow. If we get + SIGINT we want to keep waiting for carrier, because + SIGINT just means don't start any new sessions. We don't + handle SIGINT correctly if we do a longjmp in the signal + handler; too bad. */ + while (ioctl (q->o, TIOCWONLINE, 0) < 0 + && errno == EINTR) + { + /* Log the signal. */ + ulog (LOG_ERROR, (const char *) NULL); + if (FGOT_QUIT_SIGNAL () || fSalarm) + break; + } + } + + /* Turn off the pending SIGALRM and ignore SIGALARM again. */ + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + (void) alarm (0); + usysdep_end_catch (); + + /* If we got a random signal, just return FALSE. */ + if (FGOT_QUIT_SIGNAL ()) + return FALSE; + + /* If we timed out, give an error. */ + if (fSalarm) + { + ulog (LOG_ERROR, "Timed out waiting for carrier"); + return FALSE; + } + +#endif /* TIOCWONLINE */ + } + + return TRUE; +} + +/* Read data from a connection, with a timeout. This routine handles + all types of connections, including TLI. + + This function should return when we have read cmin characters or + the timeout has occurred. We have to work a bit to get Unix to do + this efficiently on a terminal. The simple implementation + schedules a SIGALRM signal and then calls read; if there is a + single character available, the call to read will return + immediately, so there must be a loop which terminates when the + SIGALRM is delivered or the correct number of characters has been + read. This can be very inefficient with a fast CPU or a low baud + rate (or both!), since each call to read may return only one or two + characters. + + Under POSIX or System V, we can specify a minimum number of + characters to read, so there is no serious trouble. + + Under BSD, we figure out how many characters we have left to read, + how long it will take for them to arrive at the current baud rate, + and sleep that long. + + Doing this with a timeout and avoiding all possible race conditions + get very hairy, though. Basically, we're going to schedule a + SIGALRM for when the timeout expires. I don't really want to do a + longjmp in the SIGALRM handler, though, because that may lose data. + Therefore, I have the signal handler set a variable. However, this + means that there will be a span of time between the time the code + checks the variable and the time it calls the read system call; if + the SIGALRM occurs during that time, the read might hang forever. + To avoid this, the SIGALRM handler not only sets a global variable, + it also schedules another SIGALRM for one second in the future + (POSIX specifies that a signal handler is permitted to safely call + alarm). To avoid getting a continual sequence of SIGALRM + interrupts, we change the signal handler to ignore SIGALRM when + we're about to exit the function. This means that every time we + execute fsysdep_conn_read we make at least five system calls. It's + the best I've been able to come up with, though. + + When fsysdep_conn_read finishes, there will be no SIGALRM scheduled + and SIGALRM will be ignored. */ + +boolean +fsysdep_conn_read (qconn, zbuf, pclen, cmin, ctimeout, freport) + struct sconnection *qconn; + char *zbuf; + size_t *pclen; + size_t cmin; + int ctimeout; + boolean freport; +{ + CATCH_PROTECT size_t cwant; + boolean fret; + register struct ssysdep_conn * const q + = (struct ssysdep_conn *) qconn->psysdep; + + cwant = *pclen; + *pclen = 0; + + /* Guard against a bad timeout. We return TRUE when a timeout + expires. It is possible to get a negative timeout here because + the calling code does not check user supplied timeouts for + plausibility. */ + if (ctimeout <= 0) + return TRUE; + + /* We want to do a blocking read. */ + if (! fsblock (q, TRUE)) + return FALSE; + + fSalarm = FALSE; + + /* We're going to set up an alarm signal to last for the entire + read. If the read system call cannot be interrupted, the signal + handler will do a longjmp causing fsysdep_catch (a macro) to + return FALSE. We handle that here. If read can be interrupted, + fsysdep_catch will be defined to TRUE. */ + if (fsysdep_catch ()) + { + /* Prepare to catch SIGALRM and schedule the signal. */ + usysdep_start_catch (); + usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL); + alarm (ctimeout); + } + else + { + /* We caught a signal. We don't actually have to do anything, + as all the appropriate checks are made at the start of the + following loop. */ + } + + fret = FALSE; + + while (TRUE) + { + int cgot; + +#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS + /* If we can tell the terminal not to return until we have a + certain number of characters, do so. */ + if (q->fterminal) + { + int csetmin; + + /* I'm not that confident about setting MIN to values larger + than 127, although up to 255 would probably work. */ + if (cmin < 127) + csetmin = cmin; + else + csetmin = 127; + + if (csetmin != cSmin) + { + q->snew.c_cc[VMIN] = csetmin; + while (! fsetterminfo (q->o, &q->snew)) + { + if (errno != EINTR + || FGOT_QUIT_SIGNAL ()) + { + int ierr; + + /* We turn off the signal before reporting the + error to minimize any problems with + interrupted system calls. */ + ierr = errno; + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + alarm (0); + usysdep_end_catch (); + ulog (LOG_ERROR, "Can't set MIN for terminal: %s", + strerror (ierr)); + return FALSE; + } + + if (fSalarm) + { + ulog (LOG_ERROR, + "Timed out when setting MIN to %d; retrying", + csetmin); + fSalarm = FALSE; + alarm (ctimeout); + } + } + cSmin = csetmin; + } + } +#endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */ + + /* If we've received a signal, get out now. */ + if (FGOT_QUIT_SIGNAL ()) + break; + + /* If we've already gotten a SIGALRM, get out with whatever + we've accumulated. */ + if (fSalarm) + { + fret = TRUE; + break; + } + + /* Right here is the race condition which we avoid by having the + SIGALRM handler schedule another SIGALRM. */ +#if HAVE_TLI + if (q->ftli) + { + int iflags; + + cgot = t_rcv (q->o, zbuf, cwant, &iflags); + if (cgot < 0 && t_errno != TSYSERR) + { + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + alarm (0); + usysdep_end_catch (); + + if (freport) + ulog (LOG_ERROR, "t_rcv: %s", + (t_errno >= 0 && t_errno < t_nerr + ? t_errlist[t_errno] + : "unknown TLI error")); + + return FALSE; + } + } + else +#endif + cgot = read (q->o, zbuf, cwant); + + /* If the read returned an error, check for signals. */ + if (cgot < 0) + { + if (errno == EINTR) + { + /* Log the signal. */ + ulog (LOG_ERROR, (const char *) NULL); + } + if (fSalarm) + { + fret = TRUE; + break; + } + if (FGOT_QUIT_SIGNAL ()) + break; + } + + /* If read returned an error, get out. We just ignore EINTR + here, since it must be from some signal we don't care about. + If the read returned 0 then the line must have been hung up + (normally we would have received SIGHUP, but we can't count + on that). We turn off the signals before calling ulog to + reduce problems with interrupted system calls. */ + if (cgot <= 0) + { + if (cgot < 0 && errno == EINTR) + cgot = 0; + else + { + int ierr; + + ierr = errno; + + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + alarm (0); + usysdep_end_catch (); + + if (freport) + { + if (cgot == 0) + ulog (LOG_ERROR, "Line disconnected"); + else + ulog (LOG_ERROR, "read: %s", strerror (ierr)); + } + + return FALSE; + } + } + + cwant -= cgot; + if (cgot >= cmin) + cmin = 0; + else + cmin -= cgot; + zbuf += cgot; + *pclen += cgot; + + /* If we have enough data, get out now. */ + if (cmin == 0) + { + fret = TRUE; + break; + } + +#if HAVE_BSD_TTY + /* We still want more data, so sleep long enough for the rest of + it to arrive. We don't this for System V or POSIX because + setting MIN is good enough (we can't sleep longer than it + takes to get MAX_INPUT characters anyhow). + + The baud rate is approximately 10 times the number of + characters which will arrive in one second, so the number of + milliseconds to sleep == + characters * (milliseconds / character) == + characters * (1000 * (seconds / character)) == + characters * (1000 * (1 / (baud / 10))) == + characters * (10000 / baud) + + We arbitrarily reduce the sleep amount by 10 milliseconds to + attempt to account for the amount of time it takes to set up + the sleep. This is how long it takes to get half a character + at 19200 baud. We then don't bother to sleep for less than + 10 milliseconds. We don't sleep if the read was interrupted. + + We use select to sleep. It would be easy to use poll as + well, but it's unlikely that any system with BSD ttys would + have poll but not select. Using select avoids hassles with + the pending SIGALRM; if it hits the select will be + interrupted, and otherwise the select will not affect it. */ + +#if ! HAVE_SELECT + #error This code requires select; feel free to extend it +#endif + + if (q->fterminal && cmin > 1 && cgot > 0) + { + int csleepchars; + int isleep; + + /* We don't try to read all the way up to MAX_INPUT, + since that might drop a character. */ + if (cmin <= MAX_INPUT - 10) + csleepchars = cmin; + else + csleepchars = MAX_INPUT - 10; + + isleep = (int) (((long) csleepchars * 10000L) / q->ibaud); + isleep -= 10; + + if (isleep > 10) + { + struct timeval s; + + s.tv_sec = isleep / 1000; + s.tv_usec = (isleep % 1000) * 1000; + + /* Some versions of select take a pointer to an int, + while some take a pointer to an fd_set. I just cast + the arguments to a generic pointer, and assume that + any machine which distinguishes int * from fd_set * + (I would be amazed if there are any such machines) + have an appropriate prototype somewhere or other. */ + (void) select (0, (pointer) NULL, (pointer) NULL, + (pointer) NULL, &s); + + /* Here either the select finished sleeping or we got a + SIGALRM. If the latter occurred, fSalarm was set to + TRUE; it will be checked at the top of the loop. */ + } + } +#endif /* HAVE_BSD_TTY */ + } + + /* Turn off the pending SIGALRM and return. */ + + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + alarm (0); + usysdep_end_catch (); + + return fret; +} + +/* Read from a stdin port. */ + +static boolean +fsstdin_read (qconn, zbuf, pclen, cmin, ctimeout, freport) + struct sconnection *qconn; + char *zbuf; + size_t *pclen; + size_t cmin; + int ctimeout; + boolean freport; +{ + struct ssysdep_conn *qsysdep; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + qsysdep->o = 0; + return fsysdep_conn_read (qconn, zbuf, pclen, cmin, ctimeout, freport); +} + +/* Write data to a connection. This routine handles all types of + connections, including TLI. */ + +boolean +fsysdep_conn_write (qconn, zwrite, cwrite) + struct sconnection *qconn; + const char *zwrite; + size_t cwrite; +{ + struct ssysdep_conn *q; + int czero; + + q = (struct ssysdep_conn *) qconn->psysdep; + + /* We want blocking writes here. */ + if (! fsblock (q, TRUE)) + return FALSE; + + czero = 0; + + while (cwrite > 0) + { + int cdid; + + /* Loop until we don't get an interrupt. */ + while (TRUE) + { + /* If we've received a signal, don't continue. */ + if (FGOT_QUIT_SIGNAL ()) + return FALSE; + +#if HAVE_TLI + if (q->ftli) + { + cdid = t_snd (q->o, zwrite, cwrite, 0); + if (cdid < 0 && t_errno != TSYSERR) + { + ulog (LOG_ERROR, "t_snd: %s", + (t_errno >= 0 && t_errno < t_nerr + ? t_errlist[t_errno] + : "unknown TLI error")); + return FALSE; + } + } + else +#endif + cdid = write (q->o, zwrite, cwrite); + + if (cdid >= 0) + break; + if (errno != EINTR) + break; + + /* We were interrupted by a signal. Log it. */ + ulog (LOG_ERROR, (const char *) NULL); + } + + if (cdid < 0) + { + if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENODATA) + { + ulog (LOG_ERROR, "write: %s", strerror (errno)); + return FALSE; + } + cdid = 0; + } + + if (cdid == 0) + { + /* On some systems write will return 0 if carrier is lost. + If we fail to write anything ten times in a row, we + assume that this has happened. This is hacked in like + this because there seems to be no reliable way to tell + exactly why the write returned 0. */ + ++czero; + if (czero >= 10) + { + ulog (LOG_ERROR, "Line disconnected"); + return FALSE; + } + } + else + { + czero = 0; + + cwrite -= cdid; + zwrite += cdid; + } + } + + return TRUE; +} + +/* Write to a stdin port. */ + +static boolean +fsstdin_write (qconn, zwrite, cwrite) + struct sconnection *qconn; + const char *zwrite; + size_t cwrite; +{ + struct ssysdep_conn *qsysdep; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + qsysdep->o = 0; + if (! fsblock (qsysdep, TRUE)) + return FALSE; + qsysdep->o = 1; + return fsysdep_conn_write (qconn, zwrite, cwrite); +} + +/* The fsysdep_conn_io routine is supposed to both read and write data + until it has either filled its read buffer or written out all the + data it was given. This lets us write out large packets without + losing incoming data. It handles all types of connections, + including TLI. */ + +boolean +fsysdep_conn_io (qconn, zwrite, pcwrite, zread, pcread) + struct sconnection *qconn; + const char *zwrite; + size_t *pcwrite; + char *zread; + size_t *pcread; +{ + struct ssysdep_conn *q; + size_t cwrite, cread; + int czero; + + q = (struct ssysdep_conn *) qconn->psysdep; + + cwrite = *pcwrite; + *pcwrite = 0; + cread = *pcread; + *pcread = 0; + + czero = 0; + + while (TRUE) + { + int cgot, cdid; + size_t cdo; + + /* This used to always use nonblocking writes, but it turns out + that some systems don't support them on terminals. + + The current algorithm is: + loop: + unblocked read + if read buffer full, return + if nothing to write, return + if HAVE_UNBLOCKED_WRITES + write all data + else + write up to SINGLE_WRITE bytes + if all data written, return + if no data written + blocked write of up to SINGLE_WRITE bytes + + This algorithm should work whether the system supports + unblocked writes on terminals or not. If the system supports + unblocked writes but HAVE_UNBLOCKED_WRITES is 0, then it will + call write more often than it needs to. If the system does + not support unblocked writes but HAVE_UNBLOCKED_WRITES is 1, + then the write may hang so long that incoming data is lost. + This is actually possible at high baud rates on any system + when a blocking write is done; there is no solution, except + hardware handshaking. */ + + /* If we are running on standard input, we switch the file + descriptors by hand. */ + if (q->istdout_flags >= 0) + q->o = 0; + + /* Do an unblocked read. */ + if (! fsblock (q, FALSE)) + return FALSE; + + /* Loop until we get something (error or data) other than an + acceptable EINTR. */ + while (TRUE) + { + /* If we've received a signal, don't continue. */ + if (FGOT_QUIT_SIGNAL ()) + return FALSE; + +#if HAVE_TLI + if (q->ftli) + { + int iflags; + + cgot = t_rcv (q->o, zread, cread, &iflags); + if (cgot < 0) + { + if (t_errno == TNODATA) + errno = EAGAIN; + else if (t_errno != TSYSERR) + { + ulog (LOG_ERROR, "t_rcv: %s", + (t_errno >= 0 && t_errno < t_nerr + ? t_errlist[t_errno] + : "unknown TLI error")); + return FALSE; + } + } + } + else +#endif + cgot = read (q->o, zread, cread); + + if (cgot >= 0) + break; + if (errno != EINTR) + break; + + /* We got interrupted by a signal. Log it. */ + ulog (LOG_ERROR, (const char *) NULL); + } + + if (cgot < 0) + { + if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENODATA) + { + ulog (LOG_ERROR, "read: %s", strerror (errno)); + return FALSE; + } + cgot = 0; + } + + cread -= cgot; + zread += cgot; + *pcread += cgot; + + /* If we've filled the read buffer, or we have nothing left to + write, return out. */ + if (cread == 0 || cwrite == 0) + return TRUE; + + /* The port is currently unblocked. Do a write. */ + cdo = cwrite; + +#if ! HAVE_UNBLOCKED_WRITES + if (q->fterminal && cdo > SINGLE_WRITE) + cdo = SINGLE_WRITE; +#endif + + if (q->istdout_flags >= 0) + q->o = 1; + + /* Loop until we get something besides EINTR. */ + while (TRUE) + { + /* If we've received a signal, don't continue. */ + if (FGOT_QUIT_SIGNAL ()) + return FALSE; + +#if HAVE_TLI + if (q->ftli) + { + cdid = t_snd (q->o, zwrite, cdo, 0); + if (cdid < 0) + { + if (t_errno == TFLOW) + errno = EAGAIN; + else if (t_errno != TSYSERR) + { + ulog (LOG_ERROR, "t_snd: %s", + (t_errno >= 0 && t_errno < t_nerr + ? t_errlist[t_errno] + : "unknown TLI error")); + return FALSE; + } + } + } + else +#endif + cdid = write (q->o, zwrite, cdo); + + if (cdid >= 0) + break; + if (errno != EINTR) + break; + + /* We got interrupted by a signal. Log it. */ + ulog (LOG_ERROR, (const char *) NULL); + } + + if (cdid < 0) + { + if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENODATA) + { + ulog (LOG_ERROR, "write: %s", strerror (errno)); + return FALSE; + } + cdid = 0; + } + + if (cdid > 0) + { + /* We wrote some data. If we wrote everything, return out. + Otherwise loop around and do another read. */ + cwrite -= cdid; + zwrite += cdid; + *pcwrite += cdid; + + if (cwrite == 0) + return TRUE; + + czero = 0; + } + else + { + /* We didn't write any data. Do a blocking write. */ + + if (q->istdout_flags >= 0) + q->o = 0; + + if (! fsblock (q, TRUE)) + return FALSE; + + cdo = cwrite; + if (cdo > SINGLE_WRITE) + cdo = SINGLE_WRITE; + + DEBUG_MESSAGE1 (DEBUG_PORT, + "fsysdep_conn_io: Blocking write of %lud", + (unsigned long) cdo); + + if (q->istdout_flags >= 0) + q->o = 1; + + /* Loop until we get something besides EINTR. */ + while (TRUE) + { + /* If we've received a signal, don't continue. */ + if (FGOT_QUIT_SIGNAL ()) + return FALSE; + +#if HAVE_TLI + if (q->ftli) + { + cdid = t_snd (q->o, zwrite, cdo, 0); + if (cdid < 0 && t_errno != TSYSERR) + { + ulog (LOG_ERROR, "t_snd: %s", + (t_errno >= 0 && t_errno < t_nerr + ? t_errlist[t_errno] + : "unknown TLI error")); + return FALSE; + } + } + else +#endif + cdid = write (q->o, zwrite, cdo); + + if (cdid >= 0) + break; + if (errno != EINTR) + break; + + /* We got interrupted by a signal. Log it. */ + ulog (LOG_ERROR, (const char *) NULL); + } + + if (cdid < 0) + { + ulog (LOG_ERROR, "write: %s", strerror (errno)); + return FALSE; + } + + if (cdid == 0) + { + /* On some systems write will return 0 if carrier is + lost. If we fail to write anything ten times in a + row, we assume that this has happened. This is + hacked in like this because there seems to be no + reliable way to tell exactly why the write returned + 0. */ + ++czero; + if (czero >= 10) + { + ulog (LOG_ERROR, "Line disconnected"); + return FALSE; + } + } + else + { + cwrite -= cdid; + zwrite += cdid; + *pcwrite += cdid; + czero = 0; + } + } + } +} + +/* Send a break character to a serial port. */ + +static boolean +fsserial_break (qconn) + struct sconnection *qconn; +{ + struct ssysdep_conn *q; + + q = (struct ssysdep_conn *) qconn->psysdep; + +#if HAVE_BSD_TTY + (void) ioctl (q->o, TIOCSBRK, 0); + sleep (2); + (void) ioctl (q->o, TIOCCBRK, 0); + return TRUE; +#endif /* HAVE_BSD_TTY */ +#if HAVE_SYSV_TERMIO + (void) ioctl (q->o, TCSBRK, 0); + return TRUE; +#endif /* HAVE_SYSV_TERMIO */ +#if HAVE_POSIX_TERMIOS + return tcsendbreak (q->o, 0) == 0; +#endif /* HAVE_POSIX_TERMIOS */ +} + +/* Send a break character to a stdin port. */ + +static boolean +fsstdin_break (qconn) + struct sconnection *qconn; +{ + struct ssysdep_conn *qsysdep; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + qsysdep->o = 1; + return fsserial_break (qconn); +} + +/* Change the setting of a serial port. */ + +/*ARGSUSED*/ +static boolean +fsserial_set (qconn, tparity, tstrip, txonxoff) + struct sconnection *qconn; + enum tparitysetting tparity; + enum tstripsetting tstrip; + enum txonxoffsetting txonxoff; +{ + register struct ssysdep_conn *q; + boolean fchanged, fdo; + int iset = 0; + int iclear = 0; + + q = (struct ssysdep_conn *) qconn->psysdep; + + if (! q->fterminal) + return TRUE; + + fchanged = FALSE; + + /* Set the parity for output characters. */ + +#if HAVE_BSD_TTY + + /* This will also cause parity detection on input characters. */ + + fdo = FALSE; + switch (tparity) + { + case PARITYSETTING_DEFAULT: + break; + case PARITYSETTING_NONE: +#if HAVE_PARITY_BUG + /* The Sony NEWS mishandles this for some reason. */ + iset = 0; + iclear = ANYP; +#else + iset = ANYP; + iclear = 0; +#endif + fdo = TRUE; + break; + case PARITYSETTING_EVEN: + iset = EVENP; + iclear = ODDP; + fdo = TRUE; + break; + case PARITYSETTING_ODD: + iset = ODDP; + iclear = EVENP; + fdo = TRUE; + break; + case PARITYSETTING_MARK: + case PARITYSETTING_SPACE: + /* Not supported. */ + break; + } + + if (fdo) + { + if ((q->snew.stty.sg_flags & iset) != iset + || (q->snew.stty.sg_flags & iclear) != 0) + { + q->snew.stty.sg_flags |= iset; + q->snew.stty.sg_flags &=~ iclear; + fchanged = TRUE; + } + } + +#else /* ! HAVE_BSD_TTY */ + + fdo = FALSE; + switch (tparity) + { + case PARITYSETTING_DEFAULT: + break; + case PARITYSETTING_NONE: + iset = CS8; + iclear = PARENB | PARODD | (CSIZE &~ CS8); + fdo = TRUE; + break; + case PARITYSETTING_EVEN: + iset = PARENB | CS7; + iclear = PARODD | (CSIZE &~ CS7); + fdo = TRUE; + break; + case PARITYSETTING_ODD: + iset = PARENB | PARODD | CS7; + iclear = CSIZE &~ CS7; + fdo = TRUE; + break; + case PARITYSETTING_MARK: + case PARITYSETTING_SPACE: + /* Not supported. */ + break; + } + + if (fdo) + { + if ((q->snew.c_cflag & iset) != iset + || (q->snew.c_cflag & iclear) != 0) + { + q->snew.c_cflag |= iset; + q->snew.c_cflag &=~ iclear; + fchanged = TRUE; + } + } + +#endif /* ! HAVE_BSD_TTY */ + + /* Set whether input characters are stripped to seven bits. */ + +#if HAVE_BSD_TTY + +#ifdef LPASS8 + { + int i; + + i = LPASS8; + if (tstrip == STRIPSETTING_EIGHTBITS) + { + i = LPASS8; + (void) ioctl (q->o, TIOCLBIS, &i); + } + else if (tstrip == STRIPSETTING_SEVENBITS) + { + i = LPASS8; + (void) ioctl (q->o, TIOCLBIC, &i); + } + } +#endif + +#else /* ! HAVE_BSD_TTY */ + + fdo = FALSE; + switch (tstrip) + { + case STRIPSETTING_DEFAULT: + break; + case STRIPSETTING_EIGHTBITS: + iset = 0; + iclear = ISTRIP; + fdo = TRUE; + break; + case STRIPSETTING_SEVENBITS: + iset = ISTRIP; + iclear = 0; + fdo = TRUE; + break; + } + + if (fdo) + { + if ((q->snew.c_iflag & iset) != iset + || (q->snew.c_iflag & iclear) != 0) + { + q->snew.c_iflag |= iset; + q->snew.c_iflag &=~ iclear; + fchanged = TRUE; + } + } + +#endif /* ! HAVE_BSD_TTY */ + + /* Set XON/XOFF handshaking. */ + +#if HAVE_BSD_TTY + + fdo = FALSE; + switch (txonxoff) + { + case XONXOFF_DEFAULT: + break; + case XONXOFF_OFF: + iset = RAW; + iclear = TANDEM | CBREAK; + fdo = TRUE; + break; + case XONXOFF_ON: + iset = CBREAK | TANDEM; + iclear = RAW; + fdo = TRUE; + break; + } + + if (fdo) + { + if ((q->snew.stty.sg_flags & iset) != iset + || (q->snew.stty.sg_flags & iclear) != 0) + { + q->snew.stty.sg_flags |= iset; + q->snew.stty.sg_flags &=~ iclear; + fchanged = TRUE; + } + } + +#else /* ! HAVE_BSD_TTY */ + + fdo = FALSE; + switch (txonxoff) + { + case XONXOFF_DEFAULT: + break; + case XONXOFF_OFF: + iset = 0; + iclear = IXON | IXOFF; + fdo = TRUE; + break; + case XONXOFF_ON: +#ifdef CRTSCTS +#if HAVE_POSIX_TERMIOS + /* This is system dependent, but I haven't figured out a good + way around it yet. If we are doing hardware flow control, we + don't send XON/XOFF characters but we do recognize them. */ + if ((q->snew.c_cflag & CRTSCTS) != 0) + { + iset = IXON; + iclear = IXOFF; + fdo = TRUE; + break; + } +#endif /* HAVE_POSIX_TERMIOS */ +#endif /* defined (CRTSCTS) */ + iset = IXON | IXOFF; + iclear = 0; + fdo = TRUE; + break; + } + + if (fdo) + { + if ((q->snew.c_iflag & iset) != iset + || (q->snew.c_iflag & iclear) != 0) + { + q->snew.c_iflag |= iset; + q->snew.c_iflag &=~ iclear; + fchanged = TRUE; + } + } + +#endif /* ! HAVE_BSD_TTY */ + + if (fchanged) + { + if (! fsetterminfodrain (q->o, &q->snew)) + { + ulog (LOG_ERROR, "Can't change terminal settings: %s", + strerror (errno)); + return FALSE; + } + } + +#if HAVE_BSD_TTY + if (txonxoff == XONXOFF_ON + && (q->snew.stty.sg_flags & ANYP) == ANYP) + { + int i; + + /* At least on Ultrix, we seem to have to set LLITOUT and + LPASS8. This shouldn't foul things up anywhere else. As far + as I can tell, this has to be done after setting the terminal + into cbreak mode, not before. */ +#ifndef LLITOUT +#define LLITOUT 0 +#endif +#ifndef LPASS8 +#define LPASS8 0 +#endif +#ifndef LAUTOFLOW +#define LAUTOFLOW 0 +#endif + i = LLITOUT | LPASS8 | LAUTOFLOW; + (void) ioctl (q->o, TIOCLBIS, &i); + +#if HAVE_STRIP_BUG + /* Ultrix 4.0 has a peculiar problem: setting CBREAK always + causes input characters to be stripped. I hope this does not + apply to other BSD systems. It is possible to work around + this by using the termio call. I wish this sort of stuff was + not necessary!!! */ + { + struct termio s; + + if (ioctl (q->o, TCGETA, &s) >= 0) + { + s.c_iflag &=~ ISTRIP; + (void) ioctl (q->o, TCSETA, &s); + } + } +#endif /* HAVE_STRIP_BUG */ + } +#endif /* HAVE_BSD_TTY */ + + return TRUE; +} + +/* Change settings of a stdin port. */ + +static boolean +fsstdin_set (qconn, tparity, tstrip, txonxoff) + struct sconnection *qconn; + enum tparitysetting tparity; + enum tstripsetting tstrip; + enum txonxoffsetting txonxoff; +{ + struct ssysdep_conn *qsysdep; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + qsysdep->o = 0; + return fsserial_set (qconn, tparity, tstrip, txonxoff); +} + +/* Run a chat program. */ + +static boolean +fsrun_chat (oread, owrite, pzprog) + int oread; + int owrite; + char **pzprog; +{ + int aidescs[3]; + FILE *e; + pid_t ipid; + char *z; + size_t c; + + aidescs[0] = oread; + aidescs[1] = owrite; + aidescs[2] = SPAWN_READ_PIPE; + + /* Pass fkeepuid, fkeepenv and fshell as TRUE. This puts the + responsibility of maintaing security on the chat program. */ + ipid = ixsspawn ((const char **) pzprog, aidescs, TRUE, TRUE, + (const char *) NULL, FALSE, TRUE, (const char *) NULL, + (const char *) NULL, (const char *) NULL); + if (ipid < 0) + { + ulog (LOG_ERROR, "ixsspawn (%s): %s", pzprog[0], strerror (errno)); + return FALSE; + } + + e = fdopen (aidescs[2], (char *) "r"); + if (e == NULL) + { + ulog (LOG_ERROR, "fdopen: %s", strerror (errno)); + (void) close (aidescs[2]); + (void) kill (ipid, SIGKILL); + (void) ixswait ((unsigned long) ipid, (const char *) NULL); + return FALSE; + } + + /* The FILE e now is attached to stderr of the program. Forward + every line the program outputs to the log file. */ + z = NULL; + c = 0; + while (getline (&z, &c, e) > 0) + { + size_t clen; + + clen = strlen (z); + if (z[clen - 1] == '\n') + z[clen - 1] = '\0'; + if (*z != '\0') + ulog (LOG_NORMAL, "chat: %s", z); + } + + xfree ((pointer) z); + (void) fclose (e); + + return ixswait ((unsigned long) ipid, "Chat program") == 0; +} + +/* Run a chat program on a stdin port. */ + +/*ARGSUSED*/ +static boolean +fsstdin_chat (qconn, pzprog) + struct sconnection *qconn; + char **pzprog; +{ + return fsrun_chat (0, 1, pzprog); +} + +/* Run a chat program on any general type of connection. */ + +boolean +fsysdep_conn_chat (qconn, pzprog) + struct sconnection *qconn; + char **pzprog; +{ + struct ssysdep_conn *qsysdep; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + return fsrun_chat (qsysdep->o, qsysdep->o, pzprog); +} + +/* Return baud rate of a serial port. */ + +static long +isserial_baud (qconn) + struct sconnection *qconn; +{ + struct ssysdep_conn *qsysdep; + + qsysdep = (struct ssysdep_conn *) qconn->psysdep; + return qsysdep->ibaud; +} diff --git a/gnu/libexec/uucp/libunix/signal.c b/gnu/libexec/uucp/libunix/signal.c new file mode 100644 index 000000000000..33e24a724574 --- /dev/null +++ b/gnu/libexec/uucp/libunix/signal.c @@ -0,0 +1,208 @@ +/* signal.c + Signal handling routines. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +/* Signal handling routines. When we catch a signal, we want to set + the appropriate elements of afSignal and afLog_signal to TRUE. If + we are on a system which restarts system calls, we may also want to + longjmp out. On a system which does not restart system calls, + these signal handling routines are well-defined by ANSI C. */ + +#if HAVE_RESTARTABLE_SYSCALLS +volatile sig_atomic_t fSjmp; +volatile jmp_buf sSjmp_buf; +#endif /* HAVE_RESTARTABLE_SYSCALLS */ + +/* Some systems, such as SunOS, have a SA_INTERRUPT bit that must be + set in the sigaction structure to force system calls to be + interrupted. */ +#ifndef SA_INTERRUPT +#define SA_INTERRUPT 0 +#endif + +/* The SVR3 sigset function can be called just like signal, unless + system calls are restarted which is extremely unlikely; we prevent + this case in sysh.unx. */ +#if HAVE_SIGSET && ! HAVE_SIGACTION && ! HAVE_SIGVEC +#define signal sigset +#endif + +/* The sigvec structure changed from 4.2BSD to 4.3BSD. These macros + make the 4.3 code backward compatible. */ +#ifndef SV_INTERRUPT +#define SV_INTERRUPT 0 +#endif +#if ! HAVE_SIGVEC_SV_FLAGS +#define sv_flags sv_onstack +#endif + +/* Catch a signal. Reinstall the signal handler if necessary, set the + appropriate variables, and do a longjmp if necessary. */ + +RETSIGTYPE +ussignal (isig) + int isig; +{ + int iindex; + +#if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET + (void) signal (isig, ussignal); +#endif + + switch (isig) + { + default: iindex = INDEXSIG_SIGHUP; break; +#ifdef SIGINT + case SIGINT: iindex = INDEXSIG_SIGINT; break; +#endif +#ifdef SIGQUIT + case SIGQUIT: iindex = INDEXSIG_SIGQUIT; break; +#endif +#ifdef SIGTERM + case SIGTERM: iindex = INDEXSIG_SIGTERM; break; +#endif +#ifdef SIGPIPE + case SIGPIPE: iindex = INDEXSIG_SIGPIPE; break; +#endif + } + + afSignal[iindex] = TRUE; + afLog_signal[iindex] = TRUE; + +#if HAVE_RESTARTABLE_SYSCALLS + if (fSjmp) + longjmp (sSjmp_buf, 1); +#endif /* HAVE_RESTARTABLE_SYSCALLS */ +} + +/* Prepare to catch a signal. This is basically the ANSI C routine + signal, but it uses sigaction or sigvec instead if they are + available. If fforce is FALSE, we do not set the signal if it is + currently being ignored. If pfignored is not NULL and fforce is + FALSE, then *pfignored will be set to TRUE if the signal was + previously being ignored (if fforce is TRUE the value returned in + *pfignored is meaningless). If we can't change the signal handler + we give a fatal error. */ + +void +usset_signal (isig, pfn, fforce, pfignored) + int isig; + RETSIGTYPE (*pfn) P((int)); + boolean fforce; + boolean *pfignored; +{ +#if HAVE_SIGACTION + + struct sigaction s; + + if (! fforce) + { + (void) (sigemptyset (&s.sa_mask)); + if (sigaction (isig, (struct sigaction *) NULL, &s) != 0) + ulog (LOG_FATAL, "sigaction (%d): %s", isig, strerror (errno)); + + if (s.sa_handler == SIG_IGN) + { + if (pfignored != NULL) + *pfignored = TRUE; + return; + } + + if (pfignored != NULL) + *pfignored = FALSE; + } + + s.sa_handler = pfn; + (void) (sigemptyset (&s.sa_mask)); + s.sa_flags = SA_INTERRUPT; + + if (sigaction (isig, &s, (struct sigaction *) NULL) != 0) + ulog (LOG_FATAL, "sigaction (%d): %s", isig, strerror (errno)); + +#else /* ! HAVE_SIGACTION */ +#if HAVE_SIGVEC + + struct sigvec s; + + if (! fforce) + { + if (sigvec (isig, (struct sigvec *) NULL, &s) != 0) + ulog (LOG_FATAL, "sigvec (%d): %s", isig, strerror (errno)); + + if (s.sv_handler == SIG_IGN) + { + if (pfignored != NULL) + *pfignored = TRUE; + return; + } + + if (pfignored != NULL) + *pfignored = FALSE; + } + + s.sv_handler = pfn; + s.sv_mask = 0; + s.sv_flags = SV_INTERRUPT; + + if (sigvec (isig, &s, (struct sigvec *) NULL) != 0) + ulog (LOG_FATAL, "sigvec (%d): %s", isig, strerror (errno)); + +#else /* ! HAVE_SIGVEC */ + + if (! fforce) + { + if (signal (isig, SIG_IGN) == SIG_IGN) + { + if (pfignored != NULL) + *pfignored = TRUE; + return; + } + + if (pfignored != NULL) + *pfignored = FALSE; + } + + (void) signal (isig, pfn); + +#endif /* ! HAVE_SIGVEC */ +#endif /* ! HAVE_SIGACTION */ +} + +/* The routine called by the system independent code, which always + uses the same signal handler. */ + +void +usysdep_signal (isig) + int isig; +{ + usset_signal (isig, ussignal, FALSE, (boolean *) NULL); +} diff --git a/gnu/libexec/uucp/libunix/sindir.c b/gnu/libexec/uucp/libunix/sindir.c new file mode 100644 index 000000000000..d98750818915 --- /dev/null +++ b/gnu/libexec/uucp/libunix/sindir.c @@ -0,0 +1,26 @@ +/* sindir.c + Stick a directory and file name together. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +char * +zsysdep_in_dir (zdir, zfile) + const char *zdir; + const char *zfile; +{ + size_t cdir, cfile; + char *zret; + + cdir = strlen (zdir); + cfile = strlen (zfile); + zret = zbufalc (cdir + cfile + 2); + memcpy (zret, zdir, cdir); + memcpy (zret + cdir + 1, zfile, cfile); + zret[cdir] = '/'; + zret[cdir + cfile + 1] = '\0'; + return zret; +} diff --git a/gnu/libexec/uucp/libunix/size.c b/gnu/libexec/uucp/libunix/size.c new file mode 100644 index 000000000000..8d021db3cd5c --- /dev/null +++ b/gnu/libexec/uucp/libunix/size.c @@ -0,0 +1,27 @@ +/* size.c + Get the size in bytes of a file. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +long +csysdep_size (zfile) + const char *zfile; +{ + struct stat s; + + if (stat ((char *) zfile, &s) < 0) + { + if (errno == ENOENT) + return -1; + ulog (LOG_ERROR, "stat (%s): %s", zfile, strerror (errno)); + return -2; + } + + return s.st_size; +} diff --git a/gnu/libexec/uucp/libunix/sleep.c b/gnu/libexec/uucp/libunix/sleep.c new file mode 100644 index 000000000000..b232f9674ff0 --- /dev/null +++ b/gnu/libexec/uucp/libunix/sleep.c @@ -0,0 +1,14 @@ +/* sleep.c + Sleep for a number of seconds. */ + +#include "uucp.h" + +#include "sysdep.h" +#include "system.h" + +void +usysdep_sleep (c) + int c; +{ + (void) sleep (c); +} diff --git a/gnu/libexec/uucp/libunix/spawn.c b/gnu/libexec/uucp/libunix/spawn.c new file mode 100644 index 000000000000..7ab080d1a9ca --- /dev/null +++ b/gnu/libexec/uucp/libunix/spawn.c @@ -0,0 +1,398 @@ +/* spawn.c + Spawn a program securely. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" + +#include <errno.h> + +#if HAVE_FCNTL_H +#include <fcntl.h> +#else +#if HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +#ifndef environ +extern char **environ; +#endif + +/* Spawn a child in a fairly secure fashion. This returns the process + ID of the child or -1 on error. It takes far too many arguments: + + pazargs -- arguments (element 0 is command) + aidescs -- file descriptors for stdin, stdout and stderr + fkeepuid -- TRUE if euid should be left unchanged + fkeepenv -- TRUE if environment should be left unmodified + zchdir -- directory to chdir to + fnosigs -- TRUE if child should ignore SIGHUP, SIGINT and SIGQUIT + fshell -- TRUE if should try /bin/sh if execve gets ENOEXEC + zpath -- value for environment variable PATH + zuu_machine -- value for environment variable UU_MACHINE + zuu_user -- value for environment variable UU_USER + + The aidescs array is three elements long. 0 is stdin, 1 is stdout + and 2 is stderr. The array may contain either file descriptor + numbers to dup appropriately, or one of the following: + + SPAWN_NULL -- set descriptor to /dev/null + SPAWN_READ_PIPE -- set aidescs element to pipe for parent to read + SPAWN_WRITE_PIPE -- set aidescs element to pipe for parent to write + + If fkeepenv is FALSE, a standard environment is created. The + environment arguments (zpath, zuu_machine and zuu_user) are only + used if fkeepenv is FALSE; any of them may be NULL. + + This routine expects that all file descriptors have been set to + close-on-exec, so it doesn't have to worry about closing them + explicitly. It sets the close-on-exec flag for the new pipe + descriptors it returns. */ + +pid_t +ixsspawn (pazargs, aidescs, fkeepuid, fkeepenv, zchdir, fnosigs, fshell, + zpath, zuu_machine, zuu_user) + const char **pazargs; + int aidescs[3]; + boolean fkeepuid; + boolean fkeepenv; + const char *zchdir; + boolean fnosigs; + boolean fshell; + const char *zpath; + const char *zuu_machine; + const char *zuu_user; +{ + char *zshcmd; + int i; + char *azenv[9]; + char **pazenv; + boolean ferr; + int ierr = 0; + int onull; + int aichild_descs[3]; + int cpar_close; + int aipar_close[4]; + int cchild_close; + int aichild_close[3]; + pid_t iret = 0; + const char *zcmd; + + /* If we might have to use the shell, allocate enough space for the + quoted command before forking. Otherwise the allocation would + modify the data segment and we could not safely use vfork. */ + zshcmd = NULL; + if (fshell) + { + size_t clen; + + clen = 0; + for (i = 0; pazargs[i] != NULL; i++) + clen += strlen (pazargs[i]); + zshcmd = zbufalc (2 * clen + i); + } + + /* Set up a standard environment. This is again done before forking + because it will modify the data segment. */ + if (fkeepenv) + pazenv = environ; + else + { + const char *zterm, *ztz; + char *zspace; + int ienv; + + if (zpath == NULL) + zpath = CMDPATH; + + azenv[0] = zbufalc (sizeof "PATH=" + strlen (zpath)); + sprintf (azenv[0], "PATH=%s", zpath); + zspace = azenv[0] + sizeof "PATH=" - 1; + while ((zspace = strchr (zspace, ' ')) != NULL) + *zspace = ':'; + + azenv[1] = zbufalc (sizeof "HOME=" + strlen (zSspooldir)); + sprintf (azenv[1], "HOME=%s", zSspooldir); + + zterm = getenv ("TERM"); + if (zterm == NULL) + zterm = "unknown"; + azenv[2] = zbufalc (sizeof "TERM=" + strlen (zterm)); + sprintf (azenv[2], "TERM=%s", zterm); + + azenv[3] = zbufcpy ("SHELL=/bin/sh"); + + azenv[4] = zbufalc (sizeof "USER=" + strlen (OWNER)); + sprintf (azenv[4], "USER=%s", OWNER); + + ienv = 5; + + ztz = getenv ("TZ"); + if (ztz != NULL) + { + azenv[ienv] = zbufalc (sizeof "TZ=" + strlen (ztz)); + sprintf (azenv[ienv], "TZ=%s", ztz); + ++ienv; + } + + if (zuu_machine != NULL) + { + azenv[ienv] = zbufalc (sizeof "UU_MACHINE=" + + strlen (zuu_machine)); + sprintf (azenv[ienv], "UU_MACHINE=%s", zuu_machine); + ++ienv; + } + + if (zuu_user != NULL) + { + azenv[ienv] = zbufalc (sizeof "UU_USER=" + + strlen (zuu_user)); + sprintf (azenv[ienv], "UU_USER=%s", zuu_user); + ++ienv; + } + + azenv[ienv] = NULL; + pazenv = azenv; + } + + /* Set up any needed pipes. */ + + ferr = FALSE; + onull = -1; + cpar_close = 0; + cchild_close = 0; + + for (i = 0; i < 3; i++) + { + if (aidescs[i] == SPAWN_NULL) + { + if (onull < 0) + { + onull = open ((char *) "/dev/null", O_RDWR); + if (onull < 0 + || fcntl (onull, F_SETFD, + fcntl (onull, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ierr = errno; + (void) close (onull); + ferr = TRUE; + break; + } + aipar_close[cpar_close] = onull; + ++cpar_close; + } + aichild_descs[i] = onull; + } + else if (aidescs[i] != SPAWN_READ_PIPE + && aidescs[i] != SPAWN_WRITE_PIPE) + aichild_descs[i] = aidescs[i]; + else + { + int aipipe[2]; + + if (pipe (aipipe) < 0) + { + ierr = errno; + ferr = TRUE; + break; + } + + if (aidescs[i] == SPAWN_READ_PIPE) + { + aidescs[i] = aipipe[0]; + aichild_close[cchild_close] = aipipe[0]; + aichild_descs[i] = aipipe[1]; + aipar_close[cpar_close] = aipipe[1]; + } + else + { + aidescs[i] = aipipe[1]; + aichild_close[cchild_close] = aipipe[1]; + aichild_descs[i] = aipipe[0]; + aipar_close[cpar_close] = aipipe[0]; + } + + ++cpar_close; + ++cchild_close; + + if (fcntl (aidescs[i], F_SETFD, + fcntl (aidescs[i], F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ierr = errno; + ferr = TRUE; + break; + } + } + } + +#if DEBUG > 1 + if (! ferr && FDEBUGGING (DEBUG_EXECUTE)) + { + ulog (LOG_DEBUG_START, "Forking %s", pazargs[0]); + for (i = 1; pazargs[i] != NULL; i++) + ulog (LOG_DEBUG_CONTINUE, " %s", pazargs[i]); + ulog (LOG_DEBUG_END, "%s", ""); + } +#endif + + if (! ferr) + { + /* This should really be vfork if available. */ + iret = ixsfork (); + if (iret < 0) + { + ferr = TRUE; + ierr = errno; + } + } + + if (ferr) + { + for (i = 0; i < cchild_close; i++) + (void) close (aichild_close[i]); + iret = -1; + } + + if (iret != 0) + { + /* The parent. Close the child's ends of the pipes and return + the process ID, or an error. */ + for (i = 0; i < cpar_close; i++) + (void) close (aipar_close[i]); + ubuffree (zshcmd); + if (! fkeepenv) + { + char **pz; + + for (pz = azenv; *pz != NULL; pz++) + ubuffree (*pz); + } + errno = ierr; + return iret; + } + + /* The child. */ + +#ifdef STDIN_FILENO +#if STDIN_FILENO != 0 || STDOUT_FILENO != 1 || STDERR_FILENO != 2 + #error The following code makes invalid assumptions +#endif +#endif + + for (i = 0; i < 3; i++) + { + if (aichild_descs[i] != i) + (void) dup2 (aichild_descs[i], i); + /* This should only be necessary if aichild_descs[i] == i, but + some systems copy the close-on-exec flag for a dupped + descriptor, which is wrong according to POSIX. */ + (void) fcntl (i, F_SETFD, fcntl (i, F_GETFD, 0) &~ FD_CLOEXEC); + } + + zcmd = pazargs[0]; + pazargs[0] = strrchr (zcmd, '/'); + if (pazargs[0] == NULL) + pazargs[0] = zcmd; + else + ++pazargs[0]; + + if (! fkeepuid) + { + (void) setuid (getuid ()); + (void) setgid (getgid ()); + } + + if (zchdir != NULL) + (void) chdir (zchdir); + + if (fnosigs) + { +#ifdef SIGHUP + (void) signal (SIGHUP, SIG_IGN); +#endif +#ifdef SIGINT + (void) signal (SIGINT, SIG_IGN); +#endif +#ifdef SIGQUIT + (void) signal (SIGQUIT, SIG_IGN); +#endif + } + + (void) execve ((char *) zcmd, (char **) pazargs, pazenv); + + /* The exec failed. If permitted, try using /bin/sh to execute a + shell script. */ + + if (errno == ENOEXEC && fshell) + { + char *zto; + const char *azshargs[4]; + + pazargs[0] = zcmd; + zto = zshcmd; + for (i = 0; pazargs[i] != NULL; i++) + { + const char *zfrom; + + for (zfrom = pazargs[i]; *zfrom != '\0'; zfrom++) + { + /* Some versions of /bin/sh appear to have a bug such + that quoting a '/' sometimes causes an error. I + don't know exactly when this happens (I can recreate + it on Ultrix 4.0), but in any case it is harmless to + not quote a '/'. */ + if (*zfrom != '/') + *zto++ = '\\'; + *zto++ = *zfrom; + } + *zto++ = ' '; + } + *(zto - 1) = '\0'; + + azshargs[0] = "sh"; + azshargs[1] = "-c"; + azshargs[2] = zshcmd; + azshargs[3] = NULL; + + (void) execve ((char *) "/bin/sh", (char **) azshargs, pazenv); + } + + _exit (EXIT_FAILURE); + + /* Avoid compiler warning. */ + return -1; +} diff --git a/gnu/libexec/uucp/libunix/splcmd.c b/gnu/libexec/uucp/libunix/splcmd.c new file mode 100644 index 000000000000..9f6616a36dd0 --- /dev/null +++ b/gnu/libexec/uucp/libunix/splcmd.c @@ -0,0 +1,115 @@ +/* splcmd.c + Spool a command. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> +#include <ctype.h> + +/* Given a set of commands to execute for a remote system, create a + command file holding them. This creates a single command file + holding all the commands passed in. It returns a jobid. */ + +char * +zsysdep_spool_commands (qsys, bgrade, ccmds, pascmds) + const struct uuconf_system *qsys; + int bgrade; + int ccmds; + const struct scmd *pascmds; +{ + char *z; + FILE *e; + int i; + const struct scmd *q; + char *zjobid; + +#if DEBUG > 0 + if (! UUCONF_GRADE_LEGAL (bgrade)) + ulog (LOG_FATAL, "Bad grade %d", bgrade); +#endif + + z = zscmd_file (qsys, bgrade); + if (z == NULL) + return NULL; + + e = esysdep_fopen (z, FALSE, FALSE, TRUE); + if (e == NULL) + { + ubuffree (z); + return NULL; + } + + for (i = 0, q = pascmds; i < ccmds; i++, q++) + { + switch (q->bcmd) + { + case 'S': + fprintf (e, "S %s %s %s -%s %s 0%o %s\n", q->zfrom, q->zto, + q->zuser, q->zoptions, q->ztemp, q->imode, + q->znotify == NULL ? (const char *) "" : q->znotify); + break; + case 'R': + fprintf (e, "R %s %s %s -%s\n", q->zfrom, q->zto, q->zuser, + q->zoptions); + break; + case 'X': + fprintf (e, "X %s %s %s -%s\n", q->zfrom, q->zto, q->zuser, + q->zoptions); + break; + case 'E': + fprintf (e, "E %s %s %s -%s %s 0%o %s 0 %s\n", q->zfrom, q->zto, + q->zuser, q->zoptions, q->ztemp, q->imode, + q->znotify, q->zcmd); + break; + default: + ulog (LOG_ERROR, + "zsysdep_spool_commands: Unrecognized type %d", + q->bcmd); + (void) fclose (e); + (void) remove (z); + ubuffree (z); + return NULL; + } + } + + if (fclose (e) != 0) + { + ulog (LOG_ERROR, "fclose: %s", strerror (errno)); + (void) remove (z); + ubuffree (z); + return NULL; + } + + zjobid = zsfile_to_jobid (qsys, z, bgrade); + if (zjobid == NULL) + (void) remove (z); + ubuffree (z); + return zjobid; +} diff --git a/gnu/libexec/uucp/libunix/splnam.c b/gnu/libexec/uucp/libunix/splnam.c new file mode 100644 index 000000000000..06ce3605ce5f --- /dev/null +++ b/gnu/libexec/uucp/libunix/splnam.c @@ -0,0 +1,19 @@ +/* splnam.c + Get the full name of a file in the spool directory. */ + +#include "uucp.h" + +#include "uuconf.h" +#include "sysdep.h" +#include "system.h" + +/* Get the real name of a spool file. */ + +char * +zsysdep_spool_file_name (qsys, zfile, pseq) + const struct uuconf_system *qsys; + const char *zfile; + pointer pseq; +{ + return zsfind_file (zfile, qsys->uuconf_zname, bsgrade (pseq)); +} diff --git a/gnu/libexec/uucp/libunix/spool.c b/gnu/libexec/uucp/libunix/spool.c new file mode 100644 index 000000000000..a3e50f7747f1 --- /dev/null +++ b/gnu/libexec/uucp/libunix/spool.c @@ -0,0 +1,420 @@ +/* spool.c + Find a file in the spool directory. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char spool_rcsid[] = "$Id: spool.c,v 1.1 1993/08/05 18:24:31 conklin Exp $"; +#endif + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +/* There are several types of files that go in the spool directory, + and they go into various different subdirectories. Whenever the + system name LOCAL appears below, it means whatever the local system + name is. + + Command files + These contain instructions for uucico indicating what files to transfer + to and from what systems. Each line of a work file is a command + beginning with S, R or X. + #if ! SPOOLDIR_TAYLOR + They are named C.ssssssgqqqq, where ssssss is the system name to + transfer to or from, g is the grade and qqqq is the sequence number. + #if SPOOLDIR_V2 + They are put in the spool directory. + #elif SPOOLDIR_BSD42 || SPOOLDIR_BSD43 + They are put in the directory "C.". + #elif SPOOLDIR_HDB + They are put in a directory named for the system for which they were + created. + #elif SPOOLDIR_ULTRIX + If the directory sys/ssssss exists, they are put in the directory + sys/ssssss/C; otherwise, they are put in the directory sys/DEFAULT/C. + #endif + #elif SPOOLDIR_SVR4 + They are put in the directory sys/g, where sys is the system name + and g is the grade. + #endif + #else SPOOLDIR_TAYLOR + They are named C.gqqqq, where g is the grade and qqqq is the sequence + number, and are placed in the directory ssssss/C. where ssssss is + the system name to transfer to or from. + #endif + + Data files + There are files to be transferred to other systems. Some files to + be transferred may not be in the spool directory, depending on how + uucp was invoked. Data files are named in work files, so it is + never necessary to look at them directly (except to remove old ones); + it is only necessary to create them. These means that the many + variations in naming are inconsequential. + #if ! SPOOLDIR_TAYLOR + They are named D.ssssssgqqqq where ssssss is a system name (which + may be LOCAL for locally initiated transfers or a remote system for + remotely initiated transfers, except that HDB appears to use the + system the file is being transferred to), g is the grade and qqqq + is the sequence number. Some systems use a trailing subjob ID + number, but we currently do not. The grade is not important, and + some systems do not use it. If the data file is to become an + execution file on another system the grade (if present) will be + 'X'. Otherwise Ultrix appears to use 'b'; the uux included with + gnuucp 1.0 appears to use 'S'; SCO does not appear to use a grade, + although it does use a subjob ID number. + #if SPOOLDIR_V2 + They are put in the spool directory. + #elif SPOOLDIR_BSD42 + If the name begins with D.LOCAL, the file is put in the directory + D.LOCAL. Otherwise the file is put in the directory D.. + #elif SPOOLDIR_BSD43 + If the name begins with D.LOCALX, the file is put in the directory + D.LOCALX. Otherwise if the name begins with D.LOCAL, the file is + put in the directory D.LOCAL Otherwise the file is put in the + directory "D.". + #elif SPOOLDIR_HDB + They are put in a directory named for the system for which they + were created. + #elif SPOOLDIR_ULTRIX + Say the file is being transferred to system REMOTE. If the + directory sys/REMOTE exists, then if the file begins with D.LOCALX + it is put in sys/REMOTE/D.LOCALX, if the file begins with D.LOCAL + it is put in sys/REMOTE/D.LOCAL, and otherwise it is put in + "sys/REMOTE/D.". If the directory sys/REMOTE does not exist, the + same applies except that DEFAULT is used instead of REMOTE. + #elif SPOOLDIR_SVR4 + They are put in the directory sys/g, where sys is the system name + and g is the grade. + #endif + #else SPOOLDIR_TAYLOR + If the file is to become an executable file on another system it is + named D.Xqqqq, otherwise it is named D.qqqq where in both cases + qqqq is a sequence number. If the corresponding C. file is in + directory ssssss/C., a D.X file is placed in ssssss/D.X and a D. + file is placed in "ssssss/D.". + #endif + + Execute files + These are files that specify programs to be executed. They are + created by uux, perhaps as run on another system. These names are + important, because a file transfer done to an execute file name + causes an execution to occur. The name is X.ssssssgqqqq, where + ssssss is the requesting system, g is the grade, and qqqq is a + sequence number. + #if SPOOLDIR_V2 || SPOOLDIR_BSD42 + These files are placed in the spool directory. + #elif SPOOLDIR_BSD43 + These files are placed in the directory X.. + #elif SPOOLDIR_HDB || SPOOLDIR_SVR4 + These files are put in a directory named for the system for which + the files were created. + #elif SPOOLDIR_ULTRIX + If there is a spool directory (sys/ssssss) for the requesting + system, the files are placed in sys/ssssss/X.; otherwise, the files + are placed in "sys/DEFAULT/X.". + #elif SPOOLDIR_TAYLOR + The system name is automatically truncated to seven characters when + a file is created. The files are placed in the subdirectory X. of + a directory named for the system for which the files were created. + #endif + + Temporary receive files + These are used when receiving files from another system. They are + later renamed to the final name. The actual name is unimportant, + although it generally begins with TM.. + #if SPOOLDIR_V2 || SPOOLDIR_BSD42 + These files are placed in the spool directory. + #elif SPOOLDIR_BSD43 || SPOOLDIR_ULTRIX || SPOOLDIR_TAYLOR + These files are placed in the directory .Temp. + #elif SPOOLDIR_HDB || SPOOLDIR_SVR4 + These files are placed in a directory named for the system for + which they were created. + #endif + + System status files + These are used to record when the last call was made to the system + and what the status is. They are used to prevent frequent recalls + to a system which is not responding. I will not attempt to + recreate the format of these exactly, since they are not all that + important. They will be put in the directory .Status, as in HDB, + and they use the system name as the name of the file. + + Sequence file + This is used to generate a unique sequence number. It contains an + ASCII number. + #if SPOOLDIR_V2 || SPOOLDIR_BSD42 || SPOOLDIR_BSD43 + The file is named SEQF and is kept in the spool directory. + #elif SPOOLDIR_HDB || SPOOLDIR_SVR4 + A separate sequence file is kept for each system in the directory + .Sequence with the name of the system. + #elif SPOOLDIR_ULTRIX + Each system with a file sys/ssssss has a sequence file in + sys/ssssss/.SEQF. Other systems use sys/DEFAULT/.SEQF. + #else SPOOLDIR_TAYLOR + A sequence file named SEQF is kept in the directory ssssss for each + system. + #endif + */ + +/* Given the name of a file as specified in a UUCP command, and the + system for which this file has been created, return where to find + it in the spool directory. The file will begin with C. (a command + file), D. (a data file) or X. (an execution file). Under + SPOOLDIR_SVR4 we need to know the grade of the file created by the + local system; this is the bgrade argument, which is -1 for a file + from a remote system. */ + +/*ARGSUSED*/ +char * +zsfind_file (zsimple, zsystem, bgrade) + const char *zsimple; + const char *zsystem; + int bgrade; +{ + if (! fspool_file (zsimple)) + { + ulog (LOG_ERROR, "Unrecognized file name %s", zsimple); + return NULL; + } + +#if ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 && ! SPOOLDIR_TAYLOR + if (*zsimple == 'X') + { + size_t clen; + + /* Files beginning with X. are execute files. It is important + for security reasons that we know the system which created + the X. file. This is easy under SPOOLDIR_HDB or + SPOOLDIR_SVR4 SPOOLDIR_TAYLOR, because the file will be in a + directory named for the system. Under other schemes, we must + get the system name from the X. file name. To prevent + security violations, we set the system name directly here; + this will cause problems if the maximum file name length is + too short, but hopefully no problem will occur since any + System V systems will be using HDB or SVR4 or TAYLOR. */ + clen = strlen (zsimple); + if (clen <= 7 || strncmp (zsimple + 2, zsystem, clen - 7) != 0) + { + static char *zbuf; + static size_t cbuf; + size_t cwant; + + cwant = strlen (zsystem) + 8; + if (cwant > cbuf) + { + zbuf = (char *) xrealloc ((pointer) zbuf, cwant); + cbuf = cwant; + } + sprintf (zbuf, "X.%s%s", zsystem, + clen < 5 ? zsimple : zsimple + clen - 5); + zsimple = zbuf; + } + } +#endif /* ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 && ! SPOOLDIR_TAYLOR */ + +#if SPOOLDIR_V2 + /* V2 never uses subdirectories. */ + return zbufcpy (zsimple); +#endif /* SPOOLDIR_V2 */ + +#if SPOOLDIR_HDB + /* HDB always uses the system name as a directory. */ + return zsysdep_in_dir (zsystem, zsimple); +#endif /* SPOOLDIR_HDB */ + +#if SPOOLDIR_SVR4 + /* SVR4 uses grade directories within the system directory for local + command and data files. */ + if (bgrade < 0 || *zsimple == 'X') + return zsysdep_in_dir (zsystem, zsimple); + else + { + char abgrade[2]; + + abgrade[0] = bgrade; + abgrade[1] = '\0'; + return zsappend3 (zsystem, abgrade, zsimple); + } +#endif /* SPOOLDIR_SVR4 */ + +#if ! SPOOLDIR_V2 && ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 + switch (*zsimple) + { + case 'C': +#if SPOOLDIR_BSD42 || SPOOLDIR_BSD43 + return zsysdep_in_dir ("C.", zsimple); +#endif /* SPOOLDIR_BSD42 || SPOOLDIR_BSD43 */ +#if SPOOLDIR_ULTRIX + if (fsultrix_has_spool (zsystem)) + return zsappend4 ("sys", zsystem, "C.", zsimple); + else + return zsappend4 ("sys", "DEFAULT", "C.", zsimple); +#endif /* SPOOLDIR_ULTRIX */ +#if SPOOLDIR_TAYLOR + return zsappend3 (zsystem, "C.", zsimple); +#endif /* SPOOLDIR_TAYLOR */ + + case 'D': +#if SPOOLDIR_BSD42 || SPOOLDIR_BSD43 + { + size_t c; + boolean ftruncated; + + /* D.LOCAL in D.LOCAL/, others in D./. If BSD43, D.LOCALX in + D.LOCALX/. */ + ftruncated = TRUE; + if (strncmp (zsimple + 2, zSlocalname, strlen (zSlocalname)) == 0) + { + c = strlen (zSlocalname); + ftruncated = FALSE; + } + else if (strncmp (zsimple + 2, zSlocalname, 7) == 0) + c = 7; + else if (strncmp (zsimple + 2, zSlocalname, 6) == 0) + c = 6; + else + c = 0; +#if SPOOLDIR_BSD43 + if (c > 0 && zsimple[c + 2] == 'X') + c++; +#endif /* SPOOLDIR_BSD43 */ + if (c > 0) + { + char *zalloc; + + zalloc = zbufalc (c + 3); + memcpy (zalloc, zsimple, c + 2); + zalloc[c + 2] = '\0'; + + /* If we truncated the system name, and there is no existing + directory with the truncated name, then just use D.. */ + if (! ftruncated || fsysdep_directory (zalloc)) + { + char *zret; + + zret = zsysdep_in_dir (zalloc, zsimple); + ubuffree (zalloc); + return zret; + } + ubuffree (zalloc); + } + return zsysdep_in_dir ("D.", zsimple); + } +#endif /* SPOOLDIR_BSD42 || SPOOLDIR_BSD43 */ +#if SPOOLDIR_ULTRIX + { + size_t c; + boolean ftruncated; + char *zfree; + const char *zdir; + char *zret; + + /* D.LOCALX in D.LOCALX/, D.LOCAL in D.LOCAL/, others in D./. */ + ftruncated = TRUE; + if (strncmp (zsimple + 2, zSlocalname, strlen (zSlocalname)) == 0) + { + c = strlen (zSlocalname); + ftruncated = FALSE; + } + else if (strncmp (zsimple + 2, zSlocalname, 7) == 0) + c = 7; + else if (strncmp (zsimple + 2, zSlocalname, 6) == 0) + c = 6; + else + c = 0; + if (c > 0 && zsimple[c + 2] == 'X') + ++c; + if (c > 0) + { + zfree = zbufalc (c + 3); + memcpy (zfree, zsimple, c + 2); + zfree[c + 2] = '\0'; + zdir = zfree; + + /* If we truncated the name, and there is no directory for + the truncated name, then don't use it. */ + if (ftruncated) + { + char *zlook; + + zlook = zsappend3 ("sys", + (fsultrix_has_spool (zsystem) + ? zsystem + : "DEFAULT"), + zdir); + if (! fsysdep_directory (zlook)) + zdir = "D."; + ubuffree (zlook); + } + } + else + { + zfree = NULL; + zdir = "D."; + } + + zret = zsappend4 ("sys", + (fsultrix_has_spool (zsystem) + ? zsystem + : "DEFAULT"), + zdir, + zsimple); + ubuffree (zfree); + return zret; + } +#endif /* SPOOLDIR_ULTRIX */ +#if SPOOLDIR_TAYLOR + if (zsimple[2] == 'X') + return zsappend3 (zsystem, "D.X", zsimple); + else + return zsappend3 (zsystem, "D.", zsimple); +#endif /* SPOOLDIR_TAYLOR */ + + + case 'X': +#if SPOOLDIR_BSD42 + return zbufcpy (zsimple); +#endif +#if SPOOLDIR_BSD43 + return zsysdep_in_dir ("X.", zsimple); +#endif +#if SPOOLDIR_ULTRIX + return zsappend4 ("sys", + (fsultrix_has_spool (zsystem) + ? zsystem + : "DEFAULT"), + "X.", + zsimple); +#endif +#if SPOOLDIR_TAYLOR + return zsappend3 (zsystem, "X.", zsimple); +#endif + } + + /* This is just to avoid warnings; it will never be executed. */ + return NULL; +#endif /* ! SPOOLDIR_V2 && ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 */ +} diff --git a/gnu/libexec/uucp/libunix/srmdir.c b/gnu/libexec/uucp/libunix/srmdir.c new file mode 100644 index 000000000000..28487ef30970 --- /dev/null +++ b/gnu/libexec/uucp/libunix/srmdir.c @@ -0,0 +1,112 @@ +/* srmdir.c + Remove a directory and all its contents. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +#if HAVE_FTW_H +#include <ftw.h> +#endif + +static int isremove_dir P((const char *, const struct stat *, int)); + +/* Keep a list of directories to be removed. */ + +struct sdirlist +{ + struct sdirlist *qnext; + char *zdir; +}; + +static struct sdirlist *qSdirlist; + +/* Remove a directory and all files in it. */ + +boolean +fsysdep_rmdir (zdir) + const char *zdir; +{ + boolean fret; + struct sdirlist *q; + + qSdirlist = NULL; + + fret = TRUE; + if (ftw ((char *) zdir, isremove_dir, 5) != 0) + { + ulog (LOG_ERROR, "ftw: %s", strerror (errno)); + fret = FALSE; + } + + q = qSdirlist; + while (q != NULL) + { + struct sdirlist *qnext; + + if (rmdir (q->zdir) != 0) + { + ulog (LOG_ERROR, "rmdir (%s): %s", q->zdir, strerror (errno)); + fret = FALSE; + } + ubuffree (q->zdir); + qnext = q->qnext; + xfree ((pointer) q); + q = qnext; + } + + return fret; +} + +/* Remove a file in a directory. */ + +/*ARGSUSED*/ +static int +isremove_dir (zfile, qstat, iflag) + const char *zfile; + const struct stat *qstat; + int iflag; +{ + if (iflag == FTW_D || iflag == FTW_DNR) + { + struct sdirlist *q; + + q = (struct sdirlist *) xmalloc (sizeof (struct sdirlist)); + q->qnext = qSdirlist; + q->zdir = zbufcpy (zfile); + qSdirlist = q; + } + else + { + if (remove (zfile) != 0) + ulog (LOG_ERROR, "remove (%s): %s", zfile, strerror (errno)); + } + + return 0; +} diff --git a/gnu/libexec/uucp/libunix/statsb.c b/gnu/libexec/uucp/libunix/statsb.c new file mode 100644 index 000000000000..aa17f9e9df07 --- /dev/null +++ b/gnu/libexec/uucp/libunix/statsb.c @@ -0,0 +1,572 @@ +/* statsb.c + System dependent routines for uustat. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char statsb_rcsid[] = "$Id: statsb.c,v 1.1 1993/08/05 18:24:34 conklin Exp $"; +#endif + +#include "uudefs.h" +#include "uuconf.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +#if HAVE_FCNTL_H +#include <fcntl.h> +#else +#if HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#if HAVE_OPENDIR +#if HAVE_DIRENT_H +#include <dirent.h> +#else /* ! HAVE_DIRENT_H */ +#include <sys/dir.h> +#define dirent direct +#endif /* ! HAVE_DIRENT_H */ +#endif /* HAVE_OPENDIR */ + +#if HAVE_TIME_H +#include <time.h> +#endif + +#if HAVE_UTIME_H +#include <utime.h> +#endif + +/* Local functions. */ + +static int ussettime P((const char *z, time_t inow)); +static boolean fskill_or_rejuv P((pointer puuconf, const char *zid, + boolean fkill)); + +/* See whether the user is permitted to kill arbitrary jobs. This is + true only for root and uucp. We check for uucp by seeing if the + real user ID and the effective user ID are the same; this works + because we should be suid to uucp, so our effective user ID will + always be uucp while our real user ID will be whoever ran the + program. */ + +boolean +fsysdep_privileged () +{ + uid_t iuid; + + iuid = getuid (); + return iuid == 0 || iuid == geteuid (); +} + +/* Set file access time to the present. On many systems this could be + done by passing NULL to utime, but on some that doesn't work. This + routine is not time critical, so we never rely on NULL. */ + +static int +ussettime(z, inow) + const char *z; + time_t inow; +{ +#if HAVE_UTIME_H + struct utimbuf s; + + s.actime = inow; + s.modtime = inow; + return utime ((char *) z, &s); +#else + time_t ai[2]; + + ai[0] = inow; + ai[1] = inow; + return utime ((char *) z, ai); +#endif +} + +/* Kill a job, given the jobid. */ + +boolean +fsysdep_kill_job (puuconf, zid) + pointer puuconf; + const char *zid; +{ + return fskill_or_rejuv (puuconf, zid, TRUE); +} + +/* Rejuvenate a job, given the jobid. */ + +boolean +fsysdep_rejuvenate_job (puuconf, zid) + pointer puuconf; + const char *zid; +{ + return fskill_or_rejuv (puuconf, zid, FALSE); +} + +/* Kill or rejuvenate a job, given the jobid. */ + +static boolean +fskill_or_rejuv (puuconf, zid, fkill) + pointer puuconf; + const char *zid; + boolean fkill; +{ + char *zfile; + char *zsys; + char bgrade; + time_t inow = 0; + int iuuconf; + struct uuconf_system ssys; + FILE *e; + boolean fret; + char *zline; + size_t cline; + int isys; + + zfile = zsjobid_to_file (zid, &zsys, &bgrade); + if (zfile == NULL) + return FALSE; + + if (! fkill) + inow = time ((time_t *) NULL); + + iuuconf = uuconf_system_info (puuconf, zsys, &ssys); + if (iuuconf == UUCONF_NOT_FOUND) + { + if (! funknown_system (puuconf, zsys, &ssys)) + { + ulog (LOG_ERROR, "%s: Bad job id", zid); + ubuffree (zfile); + ubuffree (zsys); + return FALSE; + } + } + else if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + ubuffree (zfile); + ubuffree (zsys); + return FALSE; + } + + e = fopen (zfile, "r"); + if (e == NULL) + { + if (errno == ENOENT) + ulog (LOG_ERROR, "%s: Job not found", zid); + else + ulog (LOG_ERROR, "fopen (%s): %s", zfile, strerror (errno)); + (void) uuconf_system_free (puuconf, &ssys); + ubuffree (zfile); + ubuffree (zsys); + return FALSE; + } + + /* Now we have to read through the file to identify any temporary + files. */ + fret = TRUE; + zline = NULL; + cline = 0; + while (getline (&zline, &cline, e) > 0) + { + struct scmd s; + + if (! fparse_cmd (zline, &s)) + { + ulog (LOG_ERROR, "Bad line in command file %s", zfile); + fret = FALSE; + continue; + } + + /* You are only permitted to delete a job if you submitted it or + if you are root or uucp. */ + if (strcmp (s.zuser, zsysdep_login_name ()) != 0 + && ! fsysdep_privileged ()) + { + ulog (LOG_ERROR, "%s: Not submitted by you", zid); + xfree ((pointer) zline); + (void) fclose (e); + (void) uuconf_system_free (puuconf, &ssys); + ubuffree (zfile); + ubuffree (zsys); + return FALSE; + } + + if (s.bcmd == 'S' || s.bcmd == 'E') + { + char *ztemp; + + ztemp = zsfind_file (s.ztemp, ssys.uuconf_zname, bgrade); + if (ztemp == NULL) + fret = FALSE; + else + { + if (fkill) + isys = remove (ztemp); + else + isys = ussettime (ztemp, inow); + + if (isys != 0 && errno != ENOENT) + { + ulog (LOG_ERROR, "%s (%s): %s", + fkill ? "remove" : "utime", ztemp, + strerror (errno)); + fret = FALSE; + } + + ubuffree (ztemp); + } + } + } + + xfree ((pointer) zline); + (void) fclose (e); + (void) uuconf_system_free (puuconf, &ssys); + ubuffree (zsys); + + if (fkill) + isys = remove (zfile); + else + isys = ussettime (zfile, inow); + + if (isys != 0 && errno != ENOENT) + { + ulog (LOG_ERROR, "%s (%s): %s", fkill ? "remove" : "utime", + zfile, strerror (errno)); + fret = FALSE; + } + + ubuffree (zfile); + + return fret; +} + +/* Get the time a work job was queued. */ + +long +ixsysdep_work_time (qsys, pseq) + const struct uuconf_system *qsys; + pointer pseq; +{ + char *zjobid, *zfile; + long iret; + + zjobid = zsysdep_jobid (qsys, pseq); + zfile = zsjobid_to_file (zjobid, (char **) NULL, (char *) NULL); + if (zfile == NULL) + return 0; + ubuffree (zjobid); + iret = ixsysdep_file_time (zfile); + ubuffree (zfile); + return iret; +} + +/* Get the time a file was created (actually, the time it was last + modified). */ + +long +ixsysdep_file_time (zfile) + const char *zfile; +{ + struct stat s; + + if (stat ((char *) zfile, &s) < 0) + { + if (errno != ENOENT) + ulog (LOG_ERROR, "stat (%s): %s", zfile, strerror (errno)); + return ixsysdep_time ((long *) NULL); + } + + return (long) s.st_mtime; +} + +/* Start getting the status files. */ + +boolean +fsysdep_all_status_init (phold) + pointer *phold; +{ + DIR *qdir; + + qdir = opendir ((char *) ".Status"); + if (qdir == NULL) + { + ulog (LOG_ERROR, "opendir (.Status): %s", strerror (errno)); + return FALSE; + } + + *phold = (pointer) qdir; + return TRUE; +} + +/* Get the next status file. */ + +char * +zsysdep_all_status (phold, pferr, qstat) + pointer phold; + boolean *pferr; + struct sstatus *qstat; +{ + DIR *qdir = (DIR *) phold; + struct dirent *qentry; + + while (TRUE) + { + errno = 0; + qentry = readdir (qdir); + if (qentry == NULL) + { + if (errno == 0) + *pferr = FALSE; + else + { + ulog (LOG_ERROR, "readdir: %s", strerror (errno)); + *pferr = TRUE; + } + return NULL; + } + + if (qentry->d_name[0] != '.') + { + struct uuconf_system ssys; + + /* Hack seriously; fsysdep_get_status only looks at the + zname element of the qsys argument, so if we fake that we + can read the status file. This should really be done + differently. */ + ssys.uuconf_zname = qentry->d_name; + if (fsysdep_get_status (&ssys, qstat, (boolean *) NULL)) + return zbufcpy (qentry->d_name); + + /* If fsysdep_get_status fails, it will output an error + message. We just continue with the next entry, so that + most of the status files will be displayed. */ + } + } +} + +/* Finish getting the status file. */ + +void +usysdep_all_status_free (phold) + pointer phold; +{ + DIR *qdir = (DIR *) phold; + + (void) closedir (qdir); +} + +/* Get the status of all processes holding lock files. We do this by + invoking ps after we've figured out the process entries to use. */ + +boolean +fsysdep_lock_status () +{ + DIR *qdir; + struct dirent *qentry; + int calc; + int *pai; + int cgot; + int aidescs[3]; + char *zcopy, *ztok; + int cargs, iarg; + char **pazargs; + + qdir = opendir ((char *) zSlockdir); + if (qdir == NULL) + { + ulog (LOG_ERROR, "opendir (%s): %s", zSlockdir, strerror (errno)); + return FALSE; + } + + /* We look for entries that start with "LCK.." and ignore everything + else. This won't find all possible lock files, but it should + find all the locks on terminals and systems. */ + + calc = 0; + pai = NULL; + cgot = 0; + while ((qentry = readdir (qdir)) != NULL) + { + char *zname; + int o; +#if HAVE_V2_LOCKFILES + int i; +#else + char ab[12]; +#endif + int cread; + int ierr; + int ipid; + + if (strncmp (qentry->d_name, "LCK..", sizeof "LCK.." - 1) != 0) + continue; + + zname = zsysdep_in_dir (zSlockdir, qentry->d_name); + o = open ((char *) zname, O_RDONLY | O_NOCTTY, 0); + if (o < 0) + { + if (errno != ENOENT) + ulog (LOG_ERROR, "open (%s): %s", zname, strerror (errno)); + ubuffree (zname); + continue; + } + +#if HAVE_V2_LOCKFILES + cread = read (o, &i, sizeof i); +#else + cread = read (o, ab, sizeof ab - 1); +#endif + + ierr = errno; + (void) close (o); + + if (cread < 0) + { + ulog (LOG_ERROR, "read %s: %s", zname, strerror (ierr)); + ubuffree (zname); + continue; + } + + ubuffree (zname); + +#if HAVE_V2_LOCKFILES + ipid = i; +#else + ab[cread] = '\0'; + ipid = strtol (ab, (char **) NULL, 10); +#endif + + printf ("%s: %d\n", qentry->d_name, ipid); + + if (cgot >= calc) + { + calc += 10; + pai = (int *) xrealloc ((pointer) pai, calc * sizeof (int)); + } + + pai[cgot] = ipid; + ++cgot; + } + + if (cgot == 0) + return TRUE; + + aidescs[0] = SPAWN_NULL; + aidescs[1] = 1; + aidescs[2] = 2; + + /* Parse PS_PROGRAM into an array of arguments. */ + zcopy = zbufcpy (PS_PROGRAM); + + cargs = 0; + for (ztok = strtok (zcopy, " \t"); + ztok != NULL; + ztok = strtok ((char *) NULL, " \t")) + ++cargs; + + pazargs = (char **) xmalloc ((cargs + 1) * sizeof (char *)); + + memcpy (zcopy, PS_PROGRAM, sizeof PS_PROGRAM); + for (ztok = strtok (zcopy, " \t"), iarg = 0; + ztok != NULL; + ztok = strtok ((char *) NULL, " \t"), ++iarg) + pazargs[iarg] = ztok; + pazargs[iarg] = NULL; + +#if ! HAVE_PS_MULTIPLE + /* We have to invoke ps multiple times. */ + { + int i; + char *zlast, *zset; + + zlast = pazargs[cargs - 1]; + zset = zbufalc (strlen (zlast) + 20); + for (i = 0; i < cgot; i++) + { + pid_t ipid; + + sprintf (zset, "%s%d", zlast, pai[i]); + pazargs[cargs - 1] = zset; + + ipid = ixsspawn ((const char **) pazargs, aidescs, FALSE, FALSE, + (const char *) NULL, FALSE, TRUE, + (const char *) NULL, (const char *) NULL, + (const char *) NULL); + if (ipid < 0) + ulog (LOG_ERROR, "ixsspawn: %s", strerror (errno)); + else + (void) ixswait ((unsigned long) ipid, PS_PROGRAM); + } + ubuffree (zset); + } +#else + { + char *zlast; + int i; + pid_t ipid; + + zlast = zbufalc (strlen (pazargs[cargs - 1]) + cgot * 20 + 1); + strcpy (zlast, pazargs[cargs - 1]); + for (i = 0; i < cgot; i++) + { + char ab[20]; + + sprintf (ab, "%d", pai[i]); + strcat (zlast, ab); + if (i + 1 < cgot) + strcat (zlast, ","); + } + pazargs[cargs - 1] = zlast; + + ipid = ixsspawn ((const char **) pazargs, aidescs, FALSE, FALSE, + (const char *) NULL, FALSE, TRUE, + (const char *) NULL, (const char *) NULL, + (const char *) NULL); + if (ipid < 0) + ulog (LOG_ERROR, "ixsspawn: %s", strerror (errno)); + else + (void) ixswait ((unsigned long) ipid, PS_PROGRAM); + ubuffree (zlast); + } +#endif + + ubuffree (zcopy); + xfree ((pointer) pazargs); + + return TRUE; +} diff --git a/gnu/libexec/uucp/libunix/status.c b/gnu/libexec/uucp/libunix/status.c new file mode 100644 index 000000000000..f403068a7094 --- /dev/null +++ b/gnu/libexec/uucp/libunix/status.c @@ -0,0 +1,212 @@ +/* status.c + Routines to get and set the status for a system. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "uuconf.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 + +/* If we are using HDB spool layout, store status using HDB status + values. SVR4 is a variant of HDB. */ + +#define MAP_STATUS 1 + +static const int aiMapstatus[] = +{ + 0, 13, 7, 6, 4, 20, 3, 2 +}; +#define CMAPENTRIES (sizeof (aiMapstatus) / sizeof (aiMapstatus[0])) + +#else /* ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 */ + +#define MAP_STATUS 0 + +#endif /* ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 */ + +/* Get the status of a system. This assumes that we are in the spool + directory. */ + +boolean +fsysdep_get_status (qsys, qret, pfnone) + const struct uuconf_system *qsys; + struct sstatus *qret; + boolean *pfnone; +{ + char *zname; + FILE *e; + char *zline; + char *zend, *znext; + boolean fbad; + int istat; + + if (pfnone != NULL) + *pfnone = FALSE; + + zname = zsysdep_in_dir (".Status", qsys->uuconf_zname); + e = fopen (zname, "r"); + if (e == NULL) + { + if (errno != ENOENT) + { + ulog (LOG_ERROR, "fopen (%s): %s", zname, strerror (errno)); + ubuffree (zname); + return FALSE; + } + zline = NULL; + } + else + { + size_t cline; + + zline = NULL; + cline = 0; + if (getline (&zline, &cline, e) <= 0) + { + xfree ((pointer) zline); + zline = NULL; + } + (void) fclose (e); + } + + if (zline == NULL) + { + /* There is either no status file for this system, or it's been + truncated, so fake a good status. */ + qret->ttype = STATUS_COMPLETE; + qret->cretries = 0; + qret->ilast = 0; + qret->cwait = 0; + if (pfnone != NULL) + *pfnone = TRUE; + ubuffree (zname); + return TRUE; + } + + /* It turns out that scanf is not used much in this program, so for + the benefit of small computers we avoid linking it in. This is + basically + + sscanf (zline, "%d %d %ld %d", &qret->ttype, &qret->cretries, + &qret->ilast, &qret->cwait); + + except that it's done with strtol. */ + + fbad = FALSE; + istat = (int) strtol (zline, &zend, 10); + if (zend == zline) + fbad = TRUE; + +#if MAP_STATUS + /* On some systems it may be appropriate to map system dependent status + values on to our status values. */ + { + int i; + + for (i = 0; i < CMAPENTRIES; ++i) + { + if (aiMapstatus[i] == istat) + { + istat = i; + break; + } + } + } +#endif /* MAP_STATUS */ + + if (istat < 0 || istat >= (int) STATUS_VALUES) + istat = (int) STATUS_COMPLETE; + qret->ttype = (enum tstatus_type) istat; + znext = zend; + qret->cretries = (int) strtol (znext, &zend, 10); + if (zend == znext) + fbad = TRUE; + znext = zend; + qret->ilast = strtol (znext, &zend, 10); + if (zend == znext) + fbad = TRUE; + znext = zend; + qret->cwait = (int) strtol (znext, &zend, 10); + if (zend == znext) + fbad = TRUE; + + xfree ((pointer) zline); + + if (fbad) + { + ulog (LOG_ERROR, "%s: Bad status file format", zname); + ubuffree (zname); + return FALSE; + } + + ubuffree (zname); + + return TRUE; +} + +/* Set the status of a remote system. This assumes the system is + locked when this is called, and that the program is in the spool + directory. */ + +boolean +fsysdep_set_status (qsys, qset) + const struct uuconf_system *qsys; + const struct sstatus *qset; +{ + char *zname; + FILE *e; + int istat; + + zname = zsysdep_in_dir (".Status", qsys->uuconf_zname); + + e = esysdep_fopen (zname, TRUE, FALSE, TRUE); + ubuffree (zname); + if (e == NULL) + return FALSE; + istat = (int) qset->ttype; + +#if MAP_STATUS + /* On some systems it may be appropriate to map istat onto a system + dependent number. */ + if (istat >= 0 && istat < CMAPENTRIES) + istat = aiMapstatus[istat]; +#endif /* MAP_STATUS */ + + fprintf (e, "%d %d %ld %d %s %s\n", istat, qset->cretries, + qset->ilast, qset->cwait, azStatus[(int) qset->ttype], + qsys->uuconf_zname); + if (fclose (e) != 0) + { + ulog (LOG_ERROR, "fclose: %s", strerror (errno)); + return FALSE; + } + + return TRUE; +} diff --git a/gnu/libexec/uucp/libunix/strerr.c b/gnu/libexec/uucp/libunix/strerr.c new file mode 100644 index 000000000000..d2a6c2128d04 --- /dev/null +++ b/gnu/libexec/uucp/libunix/strerr.c @@ -0,0 +1,22 @@ +/* strerr.c + Return a string for a Unix errno value. */ + +#include "uucp.h" + +#include <errno.h> + +#ifndef sys_nerr +extern int sys_nerr; +#endif +#ifndef sys_errlist +extern char *sys_errlist[]; +#endif + +char * +strerror (ierr) + int ierr; +{ + if (ierr >= 0 && ierr < sys_nerr) + return sys_errlist[ierr]; + return (char *) "unknown error"; +} diff --git a/gnu/libexec/uucp/libunix/time.c b/gnu/libexec/uucp/libunix/time.c new file mode 100644 index 000000000000..d0462433a7dd --- /dev/null +++ b/gnu/libexec/uucp/libunix/time.c @@ -0,0 +1,32 @@ +/* time.c + Get the current time. */ + +#include "uucp.h" + +#if HAVE_TIME_H +#include <time.h> +#endif + +#include "system.h" + +#ifndef time +extern time_t time (); +#endif + +/* Get the time in seconds since the epoch, with optional + microseconds. We use ixsysdep_process_time to get the microseconds + if it will work (it won't if it uses times, since that returns a + time based only on the process). */ + +long +ixsysdep_time (pimicros) + long *pimicros; +{ +#if HAVE_GETTIMEOFDAY || HAVE_FTIME + return ixsysdep_process_time (pimicros); +#else + if (pimicros != NULL) + *pimicros = 0; + return (long) time ((time_t *) NULL); +#endif +} diff --git a/gnu/libexec/uucp/libunix/tmpfil.c b/gnu/libexec/uucp/libunix/tmpfil.c new file mode 100644 index 000000000000..2dac0024388a --- /dev/null +++ b/gnu/libexec/uucp/libunix/tmpfil.c @@ -0,0 +1,83 @@ +/* tmpfil.c + Get a temporary file name. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uuconf.h" +#include "system.h" +#include "sysdep.h" + +#define ZDIGS \ + "0123456789abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-" +#define CDIGS (sizeof ZDIGS - 1) + +/*ARGSUSED*/ +char * +zstemp_file (qsys) + const struct uuconf_system *qsys; +{ + static int icount; + const char *const zdigs = ZDIGS; + char ab[14]; + pid_t ime; + int iset; + + ab[0] = 'T'; + ab[1] = 'M'; + ab[2] = '.'; + + ime = getpid (); + iset = 3; + while (ime > 0 && iset < 10) + { + ab[iset] = zdigs[ime % CDIGS]; + ime /= CDIGS; + ++iset; + } + + ab[iset] = '.'; + ++iset; + + ab[iset] = zdigs[icount / CDIGS]; + ++iset; + ab[iset] = zdigs[icount % CDIGS]; + ++iset; + + ab[iset] = '\0'; + + ++icount; + if (icount >= CDIGS * CDIGS) + icount = 0; + +#if SPOOLDIR_V2 || SPOOLDIR_BSD42 + return zbufcpy (ab); +#endif +#if SPOOLDIR_BSD43 || SPOOLDIR_ULTRIX || SPOOLDIR_TAYLOR + return zsysdep_in_dir (".Temp", ab); +#endif +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 + return zsysdep_in_dir (qsys->uuconf_zname, ab); +#endif +} diff --git a/gnu/libexec/uucp/libunix/trunc.c b/gnu/libexec/uucp/libunix/trunc.c new file mode 100644 index 000000000000..c93e82e39403 --- /dev/null +++ b/gnu/libexec/uucp/libunix/trunc.c @@ -0,0 +1,157 @@ +/* trunc.c + Truncate a file to zero length. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +#if HAVE_FCNTL_H +#include <fcntl.h> +#else +#if HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +/* External functions. */ +#ifndef lseek +extern off_t lseek (); +#endif + +/* Truncate a file to zero length. If this fails, it closes and + removes the file. We support a number of different means of + truncation, which is probably a waste of time since this function + is currently only called when the 'f' protocol resends a file. */ + +#if HAVE_FTRUNCATE +#undef HAVE_LTRUNC +#define HAVE_LTRUNC 0 +#endif + +#if ! HAVE_FTRUNCATE && ! HAVE_LTRUNC +#ifdef F_CHSIZE +#define HAVE_F_CHSIZE 1 +#else /* ! defined (F_CHSIZE) */ +#ifdef F_FREESP +#define HAVE_F_FREESP 1 +#endif /* defined (F_FREESP) */ +#endif /* ! defined (F_CHSIZE) */ +#endif /* ! HAVE_FTRUNCATE && ! HAVE_LTRUNC */ + +openfile_t +esysdep_truncate (e, zname) + openfile_t e; + const char *zname; +{ + int o; + +#if HAVE_FTRUNCATE || HAVE_LTRUNC || HAVE_F_CHSIZE || HAVE_F_FREESP + int itrunc; + + if (! ffilerewind (e)) + { + ulog (LOG_ERROR, "rewind: %s", strerror (errno)); + (void) ffileclose (e); + (void) remove (zname); + return EFILECLOSED; + } + +#if USE_STDIO + o = fileno (e); +#else + o = e; +#endif + +#if HAVE_FTRUNCATE + itrunc = ftruncate (o, 0); +#endif +#if HAVE_LTRUNC + itrunc = ltrunc (o, (long) 0, SEEK_SET); +#endif +#if HAVE_F_CHSIZE + itrunc = fcntl (o, F_CHSIZE, (off_t) 0); +#endif +#if HAVE_F_FREESP + /* This selection is based on an implementation of ftruncate by + kucharsk@Solbourne.com (William Kucharski). */ + { + struct flock fl; + + fl.l_whence = 0; + fl.l_len = 0; + fl.l_start = 0; + fl.l_type = F_WRLCK; + + itrunc = fcntl (o, F_FREESP, &fl); + } +#endif + + if (itrunc != 0) + { +#if HAVE_FTRUNCATE + ulog (LOG_ERROR, "ftruncate: %s", strerror (errno)); +#endif +#ifdef HAVE_LTRUNC + ulog (LOG_ERROR, "ltrunc: %s", strerror (errno)); +#endif +#ifdef HAVE_F_CHSIZE + ulog (LOG_ERROR, "fcntl (F_CHSIZE): %s", strerror (errno)); +#endif +#ifdef HAVE_F_FREESP + ulog (LOG_ERROR, "fcntl (F_FREESP): %s", strerror (errno)); +#endif + + (void) ffileclose (e); + (void) remove (zname); + return EFILECLOSED; + } + + return e; +#else /* ! (HAVE_FTRUNCATE || HAVE_LTRUNC || HAVE_F_CHSIZE || HAVE_F_FREESP) */ + (void) ffileclose (e); + (void) remove (zname); + + o = creat ((char *) zname, IPRIVATE_FILE_MODE); + + if (o == -1) + { + ulog (LOG_ERROR, "creat (%s): %s", zname, strerror (errno)); + return EFILECLOSED; + } + + if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + (void) close (o); + return EFILECLOSED; + } + +#if USE_STDIO + e = fdopen (o, (char *) BINWRITE); + + if (e == NULL) + { + ulog (LOG_ERROR, "fdopen (%s): %s", zname, strerror (errno)); + (void) close (o); + (void) remove (zname); + return NULL; + } +#else /* ! USE_STDIO */ + e = o; +#endif /* ! USE_STDIO */ + + return e; +#endif /* ! (HAVE_FTRUNCATE || HAVE_LTRUNC || HAVE_F_CHSIZE || HAVE_F_FREESP) */ +} diff --git a/gnu/libexec/uucp/libunix/uacces.c b/gnu/libexec/uucp/libunix/uacces.c new file mode 100644 index 000000000000..c92c78eae354 --- /dev/null +++ b/gnu/libexec/uucp/libunix/uacces.c @@ -0,0 +1,205 @@ +/* uacces.c + Check access to a file by user name. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" + +#include <pwd.h> +#include <errno.h> + +#if HAVE_GETGRENT +#include <grp.h> +#if GETGRENT_DECLARATION_OK +#ifndef getgrent +extern struct group *getgrent (); +#endif +#endif +#endif /* HAVE_GETGRENT */ + +#if GETPWNAM_DECLARATION_OK +#ifndef getpwnam +extern struct passwd *getpwnam (); +#endif +#endif + +/* Do access(2) on a stat structure, except that the user name is + provided. If the user name in zuser is NULL, require the file to + be accessible to the world. Return TRUE if access is permitted, + FALSE otherwise. This does not log an error message. */ + +boolean +fsuser_access (q, imode, zuser) + const struct stat *q; + int imode; + const char *zuser; +{ + static char *zuser_hold; + static uid_t iuid_hold; + static gid_t igid_hold; + static int cgroups_hold; + static gid_t *paigroups_hold; + int ir, iw, ix, iand; + + if (imode == F_OK) + return TRUE; + + if (zuser != NULL) + { + /* We keep static variables around for the last user we did, to + avoid looking up a user multiple times. */ + if (zuser_hold == NULL || strcmp (zuser_hold, zuser) != 0) + { + struct passwd *qpwd; + + if (zuser_hold != NULL) + { + ubuffree (zuser_hold); + zuser_hold = NULL; + cgroups_hold = 0; + xfree ((pointer) paigroups_hold); + paigroups_hold = NULL; + } + + qpwd = getpwnam ((char *) zuser); + if (qpwd == NULL) + { + /* Check this as a remote request. */ + zuser = NULL; + } + else + { +#if HAVE_GETGRENT + struct group *qg; +#endif + + zuser_hold = zbufcpy (zuser); + + iuid_hold = qpwd->pw_uid; + igid_hold = qpwd->pw_gid; + +#if HAVE_GETGRENT + /* Get the list of groups for this user. This is + definitely more appropriate for BSD than for System + V. It may just be a waste of time, and perhaps it + should be configurable. */ + setgrent (); + while ((qg = getgrent ()) != NULL) + { + const char **pz; + + if (qg->gr_gid == igid_hold) + continue; + for (pz = (const char **) qg->gr_mem; *pz != NULL; pz++) + { + if ((*pz)[0] == *zuser + && strcmp (*pz, zuser) == 0) + { + paigroups_hold = ((gid_t *) + (xrealloc + ((pointer) paigroups_hold, + ((cgroups_hold + 1) + * sizeof (gid_t))))); + paigroups_hold[cgroups_hold] = qg->gr_gid; + ++cgroups_hold; + break; + } + } + } + endgrent (); +#endif + } + } + } + + + /* Now do the actual access check. */ + + if (zuser != NULL) + { + /* The superuser can do anything. */ + if (iuid_hold == 0) + return TRUE; + + /* If this is the uid we're running under, there's no point to + checking access further, because when we actually try the + operation the system will do the checking for us. */ + if (iuid_hold == geteuid ()) + return TRUE; + } + + ir = S_IROTH; + iw = S_IWOTH; + ix = S_IXOTH; + + if (zuser != NULL) + { + if (iuid_hold == q->st_uid) + { + ir = S_IRUSR; + iw = S_IWUSR; + ix = S_IXUSR; + } + else + { + boolean fgroup; + + fgroup = FALSE; + if (igid_hold == q->st_gid) + fgroup = TRUE; + else + { + int i; + + for (i = 0; i < cgroups_hold; i++) + { + if (paigroups_hold[i] == q->st_gid) + { + fgroup = TRUE; + break; + } + } + } + + if (fgroup) + { + ir = S_IRGRP; + iw = S_IWGRP; + ix = S_IXGRP; + } + } + } + + iand = 0; + if ((imode & R_OK) != 0) + iand |= ir; + if ((imode & W_OK) != 0) + iand |= iw; + if ((imode & X_OK) != 0) + iand |= ix; + + return (q->st_mode & iand) == iand; +} diff --git a/gnu/libexec/uucp/libunix/ufopen.c b/gnu/libexec/uucp/libunix/ufopen.c new file mode 100644 index 000000000000..5a7b6f22b0d0 --- /dev/null +++ b/gnu/libexec/uucp/libunix/ufopen.c @@ -0,0 +1,218 @@ +/* ufopen.c + Open a file with the permissions of the invoking user. + + Copyright (C) 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +#if HAVE_FCNTL_H +#include <fcntl.h> +#else +#if HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +/* Local functions. */ + +static boolean fsuser_perms P((uid_t *pieuid)); +static boolean fsuucp_perms P((long ieuid)); + +/* Switch to permissions of the invoking user. */ + +static boolean +fsuser_perms (pieuid) + uid_t *pieuid; +{ + uid_t ieuid, iuid; + + ieuid = geteuid (); + iuid = getuid (); + if (pieuid != NULL) + *pieuid = ieuid; + +#if HAVE_SETREUID + /* Swap the effective user id and the real user id. We can then + swap them back again when we want to return to the uucp user's + permissions. */ + if (setreuid (ieuid, iuid) < 0) + { + ulog (LOG_ERROR, "setreuid (%ld, %ld): %s", + (long) ieuid, (long) iuid, strerror (errno)); + return FALSE; + } +#else /* ! HAVE_SETREUID */ +#if HAVE_SAVED_SETUID + /* Set the effective user id to the real user id. Since the + effective user id is saved (it's the saved setuid) we will able + to set back to it later. If the real user id is root we will not + be able to switch back and forth, so don't even try. */ + if (iuid != 0) + { + if (setuid (iuid) < 0) + { + ulog (LOG_ERROR, "setuid (%ld): %s", (long) iuid, strerror (errno)); + return FALSE; + } + } +#else /* ! HAVE_SAVED_SETUID */ + /* There's no way to switch between real permissions and effective + permissions. Just try to open the file with the uucp + permissions. */ +#endif /* ! HAVE_SAVED_SETUID */ +#endif /* ! HAVE_SETREUID */ + + return TRUE; +} + +/* Restore the uucp permissions. */ + +/*ARGSUSED*/ +static boolean +fsuucp_perms (ieuid) + long ieuid; +{ +#if HAVE_SETREUID + /* Swap effective and real user id's back to what they were. */ + if (! fsuser_perms ((uid_t *) NULL)) + return FALSE; +#else /* ! HAVE_SETREUID */ +#if HAVE_SAVED_SETUID + /* Set ourselves back to our original effective user id. */ + if (setuid ((uid_t) ieuid) < 0) + { + ulog (LOG_ERROR, "setuid (%ld): %s", (long) ieuid, strerror (errno)); + /* Is this error message helpful or confusing? */ + if (errno == EPERM) + ulog (LOG_ERROR, + "Probably HAVE_SAVED_SETUID in policy.h should be set to 0"); + return FALSE; + } +#else /* ! HAVE_SAVED_SETUID */ + /* We didn't switch, no need to switch back. */ +#endif /* ! HAVE_SAVED_SETUID */ +#endif /* ! HAVE_SETREUID */ + + return TRUE; +} + +/* Open a file with the permissions of the invoking user. Ignore the + fbinary argument since Unix has no distinction between text and + binary files. */ + +/*ARGSUSED*/ +openfile_t +esysdep_user_fopen (zfile, frd, fbinary) + const char *zfile; + boolean frd; + boolean fbinary; +{ + uid_t ieuid; + openfile_t e; + const char *zerr; + int o = 0; + + if (! fsuser_perms (&ieuid)) + return EFILECLOSED; + + zerr = NULL; + +#if USE_STDIO + e = fopen (zfile, frd ? "r" : "w"); + if (e == NULL) + zerr = "fopen"; + else + o = fileno (e); +#else + if (frd) + { + e = open ((char *) zfile, O_RDONLY | O_NOCTTY, 0); + zerr = "open"; + } + else + { + e = creat ((char *) zfile, IPUBLIC_FILE_MODE); + zerr = "creat"; + } + if (e >= 0) + { + o = e; + zerr = NULL; + } +#endif + + if (! fsuucp_perms ((long) ieuid)) + { + if (ffileisopen (e)) + (void) ffileclose (e); + return EFILECLOSED; + } + + if (zerr != NULL) + { + ulog (LOG_ERROR, "%s (%s): %s", zerr, zfile, strerror (errno)); +#if ! HAVE_SETREUID + /* Are these error messages helpful or confusing? */ +#if HAVE_SAVED_SETUID + if (errno == EACCES && getuid () == 0) + ulog (LOG_ERROR, + "The superuser may only transfer files that are readable by %s", + OWNER); +#else + if (errno == EACCES) + ulog (LOG_ERROR, + "You may only transfer files that are readable by %s", OWNER); +#endif +#endif /* ! HAVE_SETREUID */ + return EFILECLOSED; + } + + if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + (void) ffileclose (e); + return EFILECLOSED; + } + + return e; +} diff --git a/gnu/libexec/uucp/libunix/ultspl.c b/gnu/libexec/uucp/libunix/ultspl.c new file mode 100644 index 000000000000..34921d228046 --- /dev/null +++ b/gnu/libexec/uucp/libunix/ultspl.c @@ -0,0 +1,21 @@ +/* ultspl.c + See whether there is an Ultrix spool directory for a system. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +boolean +fsultrix_has_spool (zsystem) + const char *zsystem; +{ + char *z; + boolean fret; + + z = zsysdep_in_dir ("sys", zsystem); + fret = fsysdep_directory (z); + ubuffree (z); + return fret; +} diff --git a/gnu/libexec/uucp/libunix/unknwn.c b/gnu/libexec/uucp/libunix/unknwn.c new file mode 100644 index 000000000000..76f53459475e --- /dev/null +++ b/gnu/libexec/uucp/libunix/unknwn.c @@ -0,0 +1,43 @@ +/* unknwn.c + Check remote.unknown shell script. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +/* Run the remote.unknown shell script. If it succeeds, we return + FALSE because that means that the system is not permitted to log + in. If the execution fails, we return TRUE. */ + +boolean +fsysdep_unknown_caller (zscript, zsystem) + const char *zscript; + const char *zsystem; +{ + const char *azargs[3]; + int aidescs[3]; + pid_t ipid; + + azargs[0] = zscript; + azargs[1] = zsystem; + azargs[2] = NULL; + + aidescs[0] = SPAWN_NULL; + aidescs[1] = SPAWN_NULL; + aidescs[2] = SPAWN_NULL; + + ipid = ixsspawn (azargs, aidescs, TRUE, TRUE, (const char *) NULL, FALSE, + TRUE, (const char *) NULL, (const char *) NULL, + (const char *) NULL); + if (ipid < 0) + { + ulog (LOG_ERROR, "ixsspawn: %s", strerror (errno)); + return FALSE; + } + + return ixswait ((unsigned long) ipid, (const char *) NULL) != 0; +} diff --git a/gnu/libexec/uucp/libunix/uuto.c b/gnu/libexec/uucp/libunix/uuto.c new file mode 100644 index 000000000000..debba9d6fd01 --- /dev/null +++ b/gnu/libexec/uucp/libunix/uuto.c @@ -0,0 +1,31 @@ +/* uuto.c + Translate a destination for uuto. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +/* Translate a uuto destination for Unix. */ + +char * +zsysdep_uuto (zdest, zlocalname) + const char *zdest; + const char *zlocalname; +{ + const char *zexclam; + char *zto; + + zexclam = strrchr (zdest, '!'); + if (zexclam == NULL) + return NULL; + zto = (char *) zbufalc (zexclam - zdest + + sizeof "!~/receive///" + + strlen (zexclam) + + strlen (zlocalname)); + memcpy (zto, zdest, (size_t) (zexclam - zdest)); + sprintf (zto + (zexclam - zdest), "!~/receive/%s/%s/", + zexclam + 1, zlocalname); + return zto; +} diff --git a/gnu/libexec/uucp/libunix/walk.c b/gnu/libexec/uucp/libunix/walk.c new file mode 100644 index 000000000000..ab96123127dd --- /dev/null +++ b/gnu/libexec/uucp/libunix/walk.c @@ -0,0 +1,59 @@ +/* walk.c + Walk a directory tree. */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#if HAVE_FTW_H +#include <ftw.h> +#endif + +static int iswalk_dir P((const char *zname, const struct stat *qstat, + int iflag)); + +/* Walk a directory tree. */ + +static size_t cSlen; +static void (*puSfn) P((const char *zfull, const char *zrelative, + pointer pinfo)); +static pointer pSinfo; + +boolean +usysdep_walk_tree (zdir, pufn, pinfo) + const char *zdir; + void (*pufn) P((const char *zfull, const char *zrelative, + pointer pinfo)); + pointer pinfo; +{ + cSlen = strlen (zdir) + 1; + puSfn = pufn; + pSinfo = pinfo; + return ftw ((char *) zdir, iswalk_dir, 5) == 0; +} + +/* Pass a file found in the directory tree to the system independent + function. */ + +/*ARGSUSED*/ +static int +iswalk_dir (zname, qstat, iflag) + const char *zname; + const struct stat *qstat; + int iflag; +{ + char *zcopy; + + if (iflag != FTW_F) + return 0; + + zcopy = zbufcpy (zname + cSlen); + + (*puSfn) (zname, zcopy, pSinfo); + + ubuffree (zcopy); + + return 0; +} diff --git a/gnu/libexec/uucp/libunix/wldcrd.c b/gnu/libexec/uucp/libunix/wldcrd.c new file mode 100644 index 000000000000..cfbd15eb848a --- /dev/null +++ b/gnu/libexec/uucp/libunix/wldcrd.c @@ -0,0 +1,212 @@ +/* wldcrd.c + Expand wildcards. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <ctype.h> +#include <errno.h> + +#if HAVE_GLOB && ! HAVE_GLOB_H +#undef HAVE_GLOB +#define HAVE_GLOB 0 +#endif + +#if HAVE_GLOB +#include <glob.h> +#endif + +/* Local variables to hold the wildcard in progress. */ + +#if HAVE_GLOB +static glob_t sSglob; +static int iSglob; +#else +static char *zSwildcard_alloc; +static char *zSwildcard; +#endif + +/* Start getting a wildcarded file spec. Use the glob function if it + is available, and otherwise use the shell. */ + +boolean +fsysdep_wildcard_start (zfile) + const char *zfile; +{ +#if HAVE_GLOB + +#if DEBUG > 0 + if (*zfile != '/') + ulog (LOG_FATAL, "fsysdep_wildcard: %s: Can't happen", zfile); +#endif + + if (glob (zfile, 0, (int (*) ()) NULL, &sSglob) != 0) + sSglob.gl_pathc = 0; + iSglob = 0; + return TRUE; + +#else /* ! HAVE_GLOB */ + + char *zcmd, *zto; + const char *zfrom; + size_t c; + const char *azargs[4]; + FILE *e; + pid_t ipid; + +#if DEBUG > 0 + if (*zfile != '/') + ulog (LOG_FATAL, "fsysdep_wildcard: %s: Can't happen", zfile); +#endif + + zSwildcard_alloc = NULL; + zSwildcard = NULL; + + zcmd = zbufalc (sizeof ECHO_PROGRAM + sizeof " " + 2 * strlen (zfile)); + memcpy (zcmd, ECHO_PROGRAM, sizeof ECHO_PROGRAM - 1); + zto = zcmd + sizeof ECHO_PROGRAM - 1; + *zto++ = ' '; + zfrom = zfile; + while (*zfrom != '\0') + { + /* To avoid shell trickery, we quote all characters except + letters, digits, and wildcard specifiers. We don't quote '/' + to avoid an Ultrix sh bug. */ + if (! isalnum (*zfrom) + && *zfrom != '*' + && *zfrom != '?' + && *zfrom != '[' + && *zfrom != ']' + && *zfrom != '/') + *zto++ = '\\'; + *zto++ = *zfrom++; + } + *zto = '\0'; + + azargs[0] = "/bin/sh"; + azargs[1] = "-c"; + azargs[2] = zcmd; + azargs[3] = NULL; + + ubuffree (zcmd); + + e = espopen (azargs, TRUE, &ipid); + if (e == NULL) + { + ulog (LOG_ERROR, "espopen: %s", strerror (errno)); + return FALSE; + } + + zSwildcard_alloc = NULL; + c = 0; + if (getline (&zSwildcard_alloc, &c, e) <= 0) + { + xfree ((pointer) zSwildcard_alloc); + zSwildcard_alloc = NULL; + } + + if (ixswait ((unsigned long) ipid, ECHO_PROGRAM) != 0) + { + xfree ((pointer) zSwildcard_alloc); + return FALSE; + } + + if (zSwildcard_alloc == NULL) + return FALSE; + + DEBUG_MESSAGE1 (DEBUG_EXECUTE, + "fsysdep_wildcard_start: got \"%s\"", + zSwildcard_alloc); + + zSwildcard = zSwildcard_alloc; + + return TRUE; + +#endif /* ! HAVE_GLOB */ +} + +/* Get the next wildcard spec. */ + +/*ARGSUSED*/ +char * +zsysdep_wildcard (zfile) + const char *zfile; +{ +#if HAVE_GLOB + + char *zret; + + if (iSglob >= sSglob.gl_pathc) + return NULL; + zret = zbufcpy (sSglob.gl_pathv[iSglob]); + ++iSglob; + return zret; + +#else /* ! HAVE_GLOB */ + + char *zret; + + if (zSwildcard_alloc == NULL || zSwildcard == NULL) + return NULL; + + zret = zSwildcard; + + while (*zSwildcard != '\0' && ! isspace (BUCHAR (*zSwildcard))) + ++zSwildcard; + + if (*zSwildcard != '\0') + { + *zSwildcard = '\0'; + ++zSwildcard; + while (*zSwildcard != '\0' && isspace (BUCHAR (*zSwildcard))) + ++zSwildcard; + } + + if (*zSwildcard == '\0') + zSwildcard = NULL; + + return zbufcpy (zret); + +#endif /* ! HAVE_GLOB */ +} + +/* Finish up getting wildcard specs. */ + +boolean +fsysdep_wildcard_end () +{ +#if HAVE_GLOB + globfree (&sSglob); + return TRUE; +#else /* ! HAVE_GLOB */ + xfree ((pointer) zSwildcard_alloc); + zSwildcard_alloc = NULL; + zSwildcard = NULL; + return TRUE; +#endif /* ! HAVE_GLOB */ +} diff --git a/gnu/libexec/uucp/libunix/work.c b/gnu/libexec/uucp/libunix/work.c new file mode 100644 index 000000000000..8744347aeb44 --- /dev/null +++ b/gnu/libexec/uucp/libunix/work.c @@ -0,0 +1,765 @@ +/* work.c + Routines to read command files. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char work_rcsid[] = "$Id: work.c,v 1.1 1993/08/05 18:24:45 conklin Exp $"; +#endif + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" +#include "sysdep.h" + +#include <ctype.h> +#include <errno.h> + +#if HAVE_OPENDIR +#if HAVE_DIRENT_H +#include <dirent.h> +#else /* ! HAVE_DIRENT_H */ +#include <sys/dir.h> +#define dirent direct +#endif /* ! HAVE_DIRENT_H */ +#endif /* HAVE_OPENDIR */ + +/* Local functions. */ + +static char *zswork_directory P((const char *zsystem)); +static boolean fswork_file P((const char *zsystem, const char *zfile, + char *pbgrade)); +static int iswork_cmp P((constpointer pkey, constpointer pdatum)); + +/* These functions can support multiple actions going on at once. + This allows the UUCP package to send and receive multiple files at + the same time. This is a very flexible feature, but I'm not sure + it will actually be used all that much. + + The ssfile structure holds a command file name and all the lines + read in from that command file. The union within the ssline + structure initially holds a line from the file and then holds a + pointer back to the ssfile structure; a pointer to this union is + used as a sequence pointer. The ztemp entry of the ssline + structure holds the name of a temporary file to delete, if any. */ + +#define CFILELINES (10) + +struct ssline +{ + char *zline; + struct ssfile *qfile; + char *ztemp; +}; + +struct ssfile +{ + char *zfile; + int clines; + int cdid; + struct ssline aslines[CFILELINES]; +}; + +/* Static variables for the work scan. */ + +static char **azSwork_files; +static size_t cSwork_files; +static size_t iSwork_file; +static struct ssfile *qSwork_file; + +/* Given a system name, return a directory to search for work. */ + +static char * +zswork_directory (zsystem) + const char *zsystem; +{ +#if SPOOLDIR_V2 + return zbufcpy ("."); +#endif /* SPOOLDIR_V2 */ +#if SPOOLDIR_BSD42 || SPOOLDIR_BSD43 + return zbufcpy ("C."); +#endif /* SPOOLDIR_BSD42 || SPOOLDIR_BSD43 */ +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 + return zbufcpy (zsystem); +#endif /* SPOOLDIR_HDB || SPOOLDIR_SVR4 */ +#if SPOOLDIR_ULTRIX + return zsappend3 ("sys", + (fsultrix_has_spool (zsystem) + ? zsystem + : "DEFAULT"), + "C."); +#endif /* SPOOLDIR_ULTRIX */ +#if SPOOLDIR_TAYLOR + return zsysdep_in_dir (zsystem, "C."); +#endif /* SPOOLDIR_TAYLOR */ +} + +/* See whether a file name from the directory returned by + zswork_directory is really a command for a particular system. + Return the command grade. */ + +/*ARGSUSED*/ +static boolean +fswork_file (zsystem, zfile, pbgrade) + const char *zsystem; + const char *zfile; + char *pbgrade; +{ +#if SPOOLDIR_V2 || SPOOLDIR_BSD42 || SPOOLDIR_BSD43 || SPOOLDIR_ULTRIX + int cfilesys, csys; + + /* The file name should be C.ssssssgqqqq, where g is exactly one + letter and qqqq is exactly four numbers. The system name may be + truncated to six or seven characters. The system name of the + file must match the system name we're looking for, since there + could be work files for several systems in one directory. */ + if (zfile[0] != 'C' || zfile[1] != '.') + return FALSE; + csys = strlen (zsystem); + cfilesys = strlen (zfile) - 7; + if (csys != cfilesys + && (csys < 6 || (cfilesys != 6 && cfilesys != 7))) + return FALSE; + *pbgrade = zfile[cfilesys + 2]; + return strncmp (zfile + 2, zsystem, cfilesys) == 0; +#endif /* V2 || BSD42 || BSD43 || ULTRIX */ +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 + int clen; + + /* The HDB file name should be C.ssssssgqqqq where g is exactly one + letter and qqqq is exactly four numbers or letters. We don't + check the system name, because it is guaranteed by the directory + we are looking in and some versions of uucp set it to the local + system rather than the remote one. I'm not sure of the exact + format of the SVR4 file name, but it does not include the grade + at all. */ + if (zfile[0] != 'C' || zfile[1] != '.') + return FALSE; + clen = strlen (zfile); + if (clen < 7) + return FALSE; +#if ! SPOOLDIR_SVR4 + *pbgrade = zfile[clen - 5]; +#endif + return TRUE; +#endif /* SPOOLDIR_HDB || SPOOLDIR_SVR4 */ +#if SPOOLDIR_TAYLOR + /* We don't keep the system name in the file name, since that + forces truncation. Our file names are always C.gqqqq. */ + *pbgrade = zfile[2]; + return (zfile[0] == 'C' + && zfile[1] == '.' + && strlen (zfile) == 7); +#endif /* SPOOLDIR_TAYLOR */ +} + +/* A comparison function to look through the list of file names. */ + +static int +iswork_cmp (pkey, pdatum) + constpointer pkey; + constpointer pdatum; +{ + const char * const *pzkey = (const char * const *) pkey; + const char * const *pzdatum = (const char * const *) pdatum; + + return strcmp (*pzkey, *pzdatum); +} + +/* See whether there is any work to do for a particular system. */ + +boolean +fsysdep_has_work (qsys) + const struct uuconf_system *qsys; +{ + char *zdir; + DIR *qdir; + struct dirent *qentry; +#if SPOOLDIR_SVR4 + DIR *qgdir; + struct dirent *qgentry; +#endif + + zdir = zswork_directory (qsys->uuconf_zname); + if (zdir == NULL) + return FALSE; + qdir = opendir ((char *) zdir); + if (qdir == NULL) + { + ubuffree (zdir); + return FALSE; + } + +#if SPOOLDIR_SVR4 + qgdir = qdir; + while ((qgentry = readdir (qgdir)) != NULL) + { + char *zsub; + + if (qgentry->d_name[0] == '.' + || qgentry->d_name[1] != '\0') + continue; + zsub = zsysdep_in_dir (zdir, qgentry->d_name); + qdir = opendir (zsub); + ubuffree (zsub); + if (qdir == NULL) + continue; +#endif + + while ((qentry = readdir (qdir)) != NULL) + { + char bgrade; + + if (fswork_file (qsys->uuconf_zname, qentry->d_name, &bgrade)) + { + closedir (qdir); +#if SPOOLDIR_SVR4 + closedir (qgdir); +#endif + ubuffree (zdir); + return TRUE; + } + } + +#if SPOOLDIR_SVR4 + closedir (qdir); + } + qdir = qgdir; +#endif + + closedir (qdir); + ubuffree (zdir); + return FALSE; +} + +/* Initialize the work scan. We have to read all the files in the + work directory, so that we can sort them by work grade. The bgrade + argument is the minimum grade to consider. We don't want to return + files that we have already considered; usysdep_get_work_free will + clear the data out when we are done with the system. This returns + FALSE on error. */ + +#define CWORKFILES (10) + +boolean +fsysdep_get_work_init (qsys, bgrade) + const struct uuconf_system *qsys; + int bgrade; +{ + char *zdir; + DIR *qdir; + struct dirent *qentry; + size_t chad; + size_t callocated; +#if SPOOLDIR_SVR4 + DIR *qgdir; + struct dirent *qgentry; +#endif + + zdir = zswork_directory (qsys->uuconf_zname); + if (zdir == NULL) + return FALSE; + + qdir = opendir (zdir); + if (qdir == NULL) + { + boolean fret; + + if (errno == ENOENT) + fret = TRUE; + else + { + ulog (LOG_ERROR, "opendir (%s): %s", zdir, strerror (errno)); + fret = FALSE; + } + ubuffree (zdir); + return fret; + } + + chad = cSwork_files; + callocated = cSwork_files; + + /* Sort the files we already know about so that we can check the new + ones with bsearch. It would be faster to use a hash table, and + the code should be probably be changed. The sort done at the end + of this function does not suffice because it only includes the + files added last time, and does not sort the entire array. Some + (bad) qsort implementations are very slow when given a sorted + array, which causes particularly bad effects here. */ + if (chad > 0) + qsort ((pointer) azSwork_files, chad, sizeof (char *), iswork_cmp); + +#if SPOOLDIR_SVR4 + qgdir = qdir; + while ((qgentry = readdir (qgdir)) != NULL) + { + char *zsub; + + if (qgentry->d_name[0] == '.' + || qgentry->d_name[1] != '\0' + || UUCONF_GRADE_CMP (bgrade, qgentry->d_name[0]) < 0) + continue; + zsub = zsysdep_in_dir (zdir, qgentry->d_name); + qdir = opendir (zsub); + if (qdir == NULL) + { + if (errno != ENOTDIR && errno != ENOENT) + { + ulog (LOG_ERROR, "opendir (%s): %s", zsub, + strerror (errno)); + ubuffree (zsub); + return FALSE; + } + ubuffree (zsub); + continue; + } + ubuffree (zsub); +#endif + + while ((qentry = readdir (qdir)) != NULL) + { + char bfilegrade; + char *zname; + +#if ! SPOOLDIR_SVR4 + zname = zbufcpy (qentry->d_name); +#else + zname = zsysdep_in_dir (qgentry->d_name, qentry->d_name); + bfilegrade = qgentry->d_name[0]; +#endif + + if (! fswork_file (qsys->uuconf_zname, qentry->d_name, + &bfilegrade) + || UUCONF_GRADE_CMP (bgrade, bfilegrade) < 0 + || (azSwork_files != NULL + && bsearch ((pointer) &zname, + (pointer) azSwork_files, + chad, sizeof (char *), + iswork_cmp) != NULL)) + ubuffree (zname); + else + { + DEBUG_MESSAGE1 (DEBUG_SPOOLDIR, + "fsysdep_get_work_init: Found %s", + zname); + + if (cSwork_files >= callocated) + { + callocated += CWORKFILES; + azSwork_files = + (char **) xrealloc ((pointer) azSwork_files, + callocated * sizeof (char *)); + } + + azSwork_files[cSwork_files] = zname; + ++cSwork_files; + } + } + +#if SPOOLDIR_SVR4 + closedir (qdir); + } + qdir = qgdir; +#endif + + closedir (qdir); + ubuffree (zdir); + + /* Sorting the files alphabetically will get the grades in the + right order, since all the file prefixes are the same. */ + + if (cSwork_files > chad) + qsort ((pointer) (azSwork_files + chad), cSwork_files - chad, + sizeof (char *), iswork_cmp); + + return TRUE; +} + +/* Get the next work entry for a system. This must parse the next + line in the next work file. The type of command is set into + qcmd->bcmd; if there are no more commands we call + fsysdep_get_work_init to rescan, in case any came in since the last + call. If there are still no commands, qcmd->bcmd is set to 'H'. + Each field in the structure is set to point to a spot in an + malloced string. The only time we use the grade here is when + calling fsysdep_get_work_init to rescan. */ + +boolean +fsysdep_get_work (qsys, bgrade, qcmd) + const struct uuconf_system *qsys; + int bgrade; + struct scmd *qcmd; +{ + char *zdir; + + if (qSwork_file != NULL && qSwork_file->cdid >= qSwork_file->clines) + qSwork_file = NULL; + + if (azSwork_files == NULL) + { + qcmd->bcmd = 'H'; + return TRUE; + } + + zdir = NULL; + + /* This loop continues until a line is returned. */ + while (TRUE) + { + /* This loop continues until a file is opened and read in. */ + while (qSwork_file == NULL) + { + FILE *e; + struct ssfile *qfile; + int iline, callocated; + char *zline; + size_t cline; + char *zname; + + /* Read all the lines of a command file into memory. */ + do + { + if (iSwork_file >= cSwork_files) + { + /* Rescan the work directory. */ + if (! fsysdep_get_work_init (qsys, bgrade)) + { + ubuffree (zdir); + return FALSE; + } + if (iSwork_file >= cSwork_files) + { + qcmd->bcmd = 'H'; + ubuffree (zdir); + return TRUE; + } + } + + if (zdir == NULL) + { + zdir = zswork_directory (qsys->uuconf_zname); + if (zdir == NULL) + return FALSE; + } + + zname = zsysdep_in_dir (zdir, azSwork_files[iSwork_file]); + + ++iSwork_file; + + e = fopen (zname, "r"); + if (e == NULL) + { + ulog (LOG_ERROR, "fopen (%s): %s", zname, + strerror (errno)); + ubuffree (zname); + } + } + while (e == NULL); + + qfile = (struct ssfile *) xmalloc (sizeof (struct ssfile)); + callocated = CFILELINES; + iline = 0; + + zline = NULL; + cline = 0; + while (getline (&zline, &cline, e) > 0) + { + if (iline >= callocated) + { + /* The sizeof (struct ssfile) includes CFILELINES + entries already, so using callocated * sizeof + (struct ssline) will give us callocated * + CFILELINES entries. */ + qfile = + ((struct ssfile *) + xrealloc ((pointer) qfile, + (sizeof (struct ssfile) + + (callocated * sizeof (struct ssline))))); + callocated += CFILELINES; + } + qfile->aslines[iline].zline = zbufcpy (zline); + qfile->aslines[iline].qfile = NULL; + qfile->aslines[iline].ztemp = NULL; + iline++; + } + + xfree ((pointer) zline); + + if (fclose (e) != 0) + ulog (LOG_ERROR, "fclose: %s", strerror (errno)); + + if (iline == 0) + { + /* There were no lines in the file; this is a poll file, + for which we return a 'P' command. */ + qfile->aslines[0].zline = zbufcpy ("P"); + qfile->aslines[0].qfile = NULL; + qfile->aslines[0].ztemp = NULL; + iline = 1; + } + + qfile->zfile = zname; + qfile->clines = iline; + qfile->cdid = 0; + qSwork_file = qfile; + } + + /* This loop continues until all the lines from the current file + are used up, or a line is returned. */ + while (TRUE) + { + int iline; + + if (qSwork_file->cdid >= qSwork_file->clines) + { + /* We don't want to free qSwork_file here, since it must + remain until all the lines have been completed. It + is freed in fsysdep_did_work. */ + qSwork_file = NULL; + /* Go back to the main loop which finds another file. */ + break; + } + + iline = qSwork_file->cdid; + ++qSwork_file->cdid; + + /* Now parse the line into a command. */ + if (! fparse_cmd (qSwork_file->aslines[iline].zline, qcmd)) + { + ulog (LOG_ERROR, "Bad line in command file %s", + qSwork_file->zfile); + ubuffree (qSwork_file->aslines[iline].zline); + qSwork_file->aslines[iline].zline = NULL; + continue; + } + + qSwork_file->aslines[iline].qfile = qSwork_file; + qcmd->pseq = (pointer) (&qSwork_file->aslines[iline]); + + if (qcmd->bcmd == 'S' || qcmd->bcmd == 'E') + { + char *zreal; + + zreal = zsysdep_spool_file_name (qsys, qcmd->ztemp, + qcmd->pseq); + if (zreal == NULL) + { + ubuffree (qSwork_file->aslines[iline].zline); + qSwork_file->aslines[iline].zline = NULL; + ubuffree (zdir); + return FALSE; + } + qSwork_file->aslines[iline].ztemp = zreal; + } + + ubuffree (zdir); + return TRUE; + } + } +} + +/* When a command has been complete, fsysdep_did_work is called. The + sequence entry was set above to be the address of an aslines + structure whose pfile entry points to the ssfile corresponding to + this file. We can then check whether all the lines have been + completed (they will have been if the pfile entry is NULL) and + remove the file if they have been. This means that we only remove + a command file if we manage to complete every transfer it specifies + in a single UUCP session. I don't know if this is how regular UUCP + works. */ + +boolean +fsysdep_did_work (pseq) + pointer pseq; +{ + struct ssfile *qfile; + struct ssline *qline; + int i; + + qline = (struct ssline *) pseq; + + ubuffree (qline->zline); + qline->zline = NULL; + + qfile = qline->qfile; + qline->qfile = NULL; + + /* Remove the temporary file, if there is one. It really doesn't + matter if this fails, and not checking the return value lets us + attempt to remove D.0 or whatever an unused temporary file is + called without complaining. */ + if (qline->ztemp != NULL) + { + (void) remove (qline->ztemp); + ubuffree (qline->ztemp); + qline->ztemp = NULL; + } + + /* If not all the lines have been returned from fsysdep_get_work, + we can't remove the file yet. */ + if (qfile->cdid < qfile->clines) + return TRUE; + + /* See whether all the commands have been completed. */ + for (i = 0; i < qfile->clines; i++) + if (qfile->aslines[i].qfile != NULL) + return TRUE; + + /* All commands have finished. */ + if (remove (qfile->zfile) != 0) + { + ulog (LOG_ERROR, "remove (%s): %s", qfile->zfile, + strerror (errno)); + return FALSE; + } + + ubuffree (qfile->zfile); + xfree ((pointer) qfile); + + if (qfile == qSwork_file) + qSwork_file = NULL; + + return TRUE; +} + +/* Free up the results of a work scan, when we're done with this + system. */ + +/*ARGSUSED*/ +void +usysdep_get_work_free (qsys) + const struct uuconf_system *qsys; +{ + if (azSwork_files != NULL) + { + size_t i; + + for (i = 0; i < cSwork_files; i++) + ubuffree ((pointer) azSwork_files[i]); + xfree ((pointer) azSwork_files); + azSwork_files = NULL; + cSwork_files = 0; + iSwork_file = 0; + } + if (qSwork_file != NULL) + { + int i; + + ubuffree (qSwork_file->zfile); + for (i = 0; i < qSwork_file->cdid; i++) + { + ubuffree (qSwork_file->aslines[i].zline); + ubuffree (qSwork_file->aslines[i].ztemp); + } + for (i = qSwork_file->cdid; i < qSwork_file->clines; i++) + ubuffree (qSwork_file->aslines[i].zline); + xfree ((pointer) qSwork_file); + qSwork_file = NULL; + } +} + +/* Save the temporary file used by a send command, and return an + informative message to mail to the requestor. This is called when + a file transfer failed, to make sure that the potentially valuable + file is not completely lost. */ + +const char * +zsysdep_save_temp_file (pseq) + pointer pseq; +{ + struct ssline *qline = (struct ssline *) pseq; + char *zto, *zslash; + size_t cwant; + static char *zbuf; + static int cbuf; + + if (! fsysdep_file_exists (qline->ztemp)) + return NULL; + + zslash = strrchr (qline->ztemp, '/'); + if (zslash == NULL) + zslash = qline->ztemp; + else + ++zslash; + + zto = zbufalc (sizeof PRESERVEDIR + sizeof "/" + strlen (zslash)); + sprintf (zto, "%s/%s", PRESERVEDIR, zslash); + + if (! fsysdep_move_file (qline->ztemp, zto, TRUE, FALSE, FALSE, + (const char *) NULL)) + { + ubuffree (zto); + return "Could not move file to preservation directory"; + } + + cwant = sizeof "File saved as\n\t/" + strlen (zSspooldir) + strlen (zto); + if (cwant > cbuf) + { + ubuffree (zbuf); + zbuf = zbufalc (cwant); + cbuf = cwant; + } + + sprintf (zbuf, "File saved as\n\t%s/%s", zSspooldir, zto); + ubuffree (zto); + return zbuf; +} + +/* Get the jobid of a work file. This is needed by uustat. */ + +char * +zsysdep_jobid (qsys, pseq) + const struct uuconf_system *qsys; + pointer pseq; +{ + return zsfile_to_jobid (qsys, ((struct ssline *) pseq)->qfile->zfile, + bsgrade (pseq)); +} + +/* Get the grade of a work file. The pseq argument can be NULL when + this is called from zsysdep_spool_file_name, and simply means that + this is a remote file; returning -1 will cause zsfind_file to do + the right thing. */ + +char +bsgrade (pseq) + pointer pseq; +{ + const char *zfile; + char bgrade; + + if (pseq == NULL) + return -1; + + zfile = ((struct ssline *) pseq)->qfile->zfile; + +#if ! SPOOLDIR_SVR4 + bgrade = zfile[strlen (zfile) - CSEQLEN - 1]; +#else + bgrade = *(strchr (zfile, '/') + 1); +#endif + + return bgrade; +} diff --git a/gnu/libexec/uucp/libunix/xqtfil.c b/gnu/libexec/uucp/libunix/xqtfil.c new file mode 100644 index 000000000000..9edb40f23fef --- /dev/null +++ b/gnu/libexec/uucp/libunix/xqtfil.c @@ -0,0 +1,265 @@ +/* xqtfil.c + Routines to read execute files. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char xqtfil_rcsid[] = "$Id: xqtfil.c,v 1.1 1993/08/05 18:24:46 conklin Exp $"; +#endif + +#include "uudefs.h" +#include "sysdep.h" +#include "system.h" + +#include <errno.h> + +#if HAVE_OPENDIR +#if HAVE_DIRENT_H +#include <dirent.h> +#else /* ! HAVE_DIRENT_H */ +#include <sys/dir.h> +#define dirent direct +#endif /* ! HAVE_DIRENT_H */ +#endif /* HAVE_OPENDIR */ + +/* Under the V2 or BSD42 spool directory scheme, all execute files are + in the main spool directory. Under the BSD43 scheme, they are all + in the directory X.. Under the HDB or SVR4 scheme, they are in + directories named after systems. Under the ULTRIX scheme, they are + in X. subdirectories of subdirectories of sys. Under the TAYLOR + scheme, they are all in the subdirectory X. of a directory named + after the system. + + This means that for HDB, ULTRIX, SVR4 or TAYLOR, we have to search + directories of directories. */ + +#if SPOOLDIR_V2 || SPOOLDIR_BSD42 +#define ZDIR "." +#define SUBDIRS 0 +#endif +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 || SPOOLDIR_TAYLOR +#define ZDIR "." +#define SUBDIRS 1 +#endif +#if SPOOLDIR_ULTRIX +#define ZDIR "sys" +#define SUBDIRS 1 +#endif +#if SPOOLDIR_BSD43 +#define ZDIR "X." +#define SUBDIRS 0 +#endif + +/* Static variables for the execute file scan. */ + +static DIR *qSxqt_topdir; +#if ! SUBDIRS +static const char *zSdir; +#else /* SUBDIRS */ +static char *zSdir; +static DIR *qSxqt_dir; +static char *zSsystem; +#endif /* SUBDIRS */ + +/* Initialize the scan for execute files. The function + usysdep_get_xqt_free will clear the data out when we are done with + the system. This returns FALSE on error. */ + +/*ARGSUSED*/ +boolean +fsysdep_get_xqt_init () +{ + usysdep_get_xqt_free (); + + qSxqt_topdir = opendir ((char *) ZDIR); + if (qSxqt_topdir == NULL) + { + if (errno == ENOENT) + return TRUE; + ulog (LOG_ERROR, "opendir (%s): %s", ZDIR, strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/* Return the name of the next execute file to read and process. If + this returns NULL, *pferr must be checked. If will be TRUE on + error, FALSE if there are no more files. On a successful return + *pzsystem will be set to the system for which the execute file was + created. */ + +char * +zsysdep_get_xqt (pzsystem, pferr) + char **pzsystem; + boolean *pferr; +{ + *pferr = FALSE; + + if (qSxqt_topdir == NULL) + return NULL; + + /* This loop continues until we find a file. */ + while (TRUE) + { + DIR *qdir; + struct dirent *q; + +#if ! SUBDIRS + zSdir = ZDIR; + qdir = qSxqt_topdir; +#else /* SUBDIRS */ + /* This loop continues until we find a subdirectory to read. */ + while (qSxqt_dir == NULL) + { + struct dirent *qtop; + + qtop = readdir (qSxqt_topdir); + if (qtop == NULL) + { + (void) closedir (qSxqt_topdir); + qSxqt_topdir = NULL; + return NULL; + } + + /* No system name may start with a dot (this is enforced by + tisystem in sysinf.c). This allows us to quickly skip + impossible directories. */ + if (qtop->d_name[0] == '.') + continue; + + DEBUG_MESSAGE1 (DEBUG_SPOOLDIR, + "zsysdep_get_xqt: Found %s in top directory", + qtop->d_name); + + ubuffree (zSdir); + +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 + zSdir = zbufcpy (qtop->d_name); +#endif +#if SPOOLDIR_ULTRIX + zSdir = zsappend3 ("sys", qtop->d_name, "X."); +#endif +#if SPOOLDIR_TAYLOR + zSdir = zsysdep_in_dir (qtop->d_name, "X."); +#endif + + ubuffree (zSsystem); + zSsystem = zbufcpy (qtop->d_name); + + qSxqt_dir = opendir (zSdir); + + if (qSxqt_dir == NULL + && errno != ENOTDIR + && errno != ENOENT) + ulog (LOG_ERROR, "opendir (%s): %s", zSdir, strerror (errno)); + } + + qdir = qSxqt_dir; +#endif /* SUBDIRS */ + + q = readdir (qdir); + +#if DEBUG > 1 + if (q != NULL) + DEBUG_MESSAGE2 (DEBUG_SPOOLDIR, + "zsysdep_get_xqt: Found %s in subdirectory %s", + q->d_name, zSdir); +#endif + + /* If we've found an execute file, return it. We have to get + the system name, which is easy for HDB or TAYLOR. For other + spool directory schemes, we have to pull it out of the X. + file name; this would be insecure, except that zsfind_file + clobbers the file name to include the real system name. */ + if (q != NULL + && q->d_name[0] == 'X' + && q->d_name[1] == '.') + { + char *zret; + +#if SPOOLDIR_HDB || SPOOLDIR_SVR4 || SPOOLDIR_TAYLOR + *pzsystem = zbufcpy (zSsystem); +#else + { + size_t clen; + + clen = strlen (q->d_name) - 7; + *pzsystem = zbufalc (clen + 1); + memcpy (*pzsystem, q->d_name + 2, clen); + (*pzsystem)[clen] = '\0'; + } +#endif + + zret = zsysdep_in_dir (zSdir, q->d_name); +#if DEBUG > 1 + DEBUG_MESSAGE2 (DEBUG_SPOOLDIR, + "zsysdep_get_xqt: Returning %s (system %s)", + zret, *pzsystem); +#endif + return zret; + } + + /* If we've reached the end of the directory, then if we are + using subdirectories loop around to read the next one, + otherwise we are finished. */ + if (q == NULL) + { + (void) closedir (qdir); +#if SUBDIRS + qSxqt_dir = NULL; + continue; +#else + qSxqt_topdir = NULL; + return NULL; +#endif + } + } +} + +/* Free up the results of an execute file scan, when we're done with + this system. */ + +/*ARGSUSED*/ +void +usysdep_get_xqt_free () +{ + if (qSxqt_topdir != NULL) + { + (void) closedir (qSxqt_topdir); + qSxqt_topdir = NULL; + } +#if SUBDIRS + if (qSxqt_dir != NULL) + { + (void) closedir (qSxqt_dir); + qSxqt_dir = NULL; + } + ubuffree (zSdir); + zSdir = NULL; + ubuffree (zSsystem); + zSsystem = NULL; +#endif +} diff --git a/gnu/libexec/uucp/libunix/xqtsub.c b/gnu/libexec/uucp/libunix/xqtsub.c new file mode 100644 index 000000000000..87b558b83086 --- /dev/null +++ b/gnu/libexec/uucp/libunix/xqtsub.c @@ -0,0 +1,698 @@ +/* xqtsub.c + System dependent functions used only by uuxqt. + + Copyright (C) 1991, 1992 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char xqtsub_rcsid[] = "$Id: xqtsub.c,v 1.1 1993/08/05 18:24:47 conklin Exp $"; +#endif + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" +#include "sysdep.h" + +#include <ctype.h> +#include <errno.h> + +#if HAVE_FCNTL_H +#include <fcntl.h> +#else +#if HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +#if HAVE_OPENDIR +#if HAVE_DIRENT_H +#include <dirent.h> +#else /* ! HAVE_DIRENT_H */ +#include <sys/dir.h> +#define dirent direct +#endif /* ! HAVE_DIRENT_H */ +#endif /* HAVE_OPENDIR */ + +/* Get a value for EX_TEMPFAIL. */ + +#if HAVE_SYSEXITS_H +#include <sysexits.h> +#endif + +#ifndef EX_TEMPFAIL +#define EX_TEMPFAIL 75 +#endif + +/* Get the full pathname of the command to execute, given the list of + permitted commands and the allowed path. */ + +char * +zsysdep_find_command (zcmd, pzcmds, pzpath, pferr) + const char *zcmd; + char **pzcmds; + char **pzpath; + boolean *pferr; +{ + char **pz; + + *pferr = FALSE; + + for (pz = pzcmds; *pz != NULL; pz++) + { + char *zslash; + + if (strcmp (*pz, "ALL") == 0) + break; + + zslash = strrchr (*pz, '/'); + if (zslash != NULL) + ++zslash; + else + zslash = *pz; + if (strcmp (zslash, zcmd) == 0 + || strcmp (*pz, zcmd) == 0) + { + /* If we already have an absolute path, we can get out + immediately. */ + if (**pz == '/') + return zbufcpy (*pz); + break; + } + } + + /* If we didn't find this command, get out. */ + if (*pz == NULL) + return NULL; + + /* We didn't find an absolute pathname, so we must look through + the path. */ + for (pz = pzpath; *pz != NULL; pz++) + { + char *zname; + struct stat s; + + zname = zsysdep_in_dir (*pz, zcmd); + if (stat (zname, &s) == 0) + return zname; + } + + *pferr = FALSE; + return NULL; +} + +/* Expand a local filename for uuxqt. This is special because uuxqt + only wants to expand filenames that start with ~ (it does not want + to prepend the current directory to other names) and if the ~ is + double, it is turned into a single ~. This returns NULL to + indicate that no change was required; it has no way to return + error. */ + +char * +zsysdep_xqt_local_file (qsys, zfile) + const struct uuconf_system *qsys; + const char *zfile; +{ + if (*zfile != '~') + return NULL; + if (zfile[1] == '~') + { + size_t clen; + char *zret; + + clen = strlen (zfile); + zret = zbufalc (clen); + memcpy (zret, zfile + 1, clen); + return zret; + } + return zsysdep_local_file (zfile, qsys->uuconf_zpubdir); +} + +#if ! ALLOW_FILENAME_ARGUMENTS + +/* Check to see whether an argument specifies a file name; if it does, + make sure that the file may legally be sent and/or received. For + Unix, we do not permit any occurrence of "/../" in the name, nor + may it start with "../". Otherwise, if it starts with "/" we check + against the list of permitted files. */ + +boolean +fsysdep_xqt_check_file (qsys, zfile) + const struct uuconf_system *qsys; + const char *zfile; +{ + size_t clen; + + clen = strlen (zfile); + if ((clen == sizeof "../" - 1 + && strcmp (zfile, "../") == 0) + || (clen >= sizeof "/.." - 1 + && strcmp (zfile + clen - (sizeof "/.." - 1), "/..") == 0) + || strstr (zfile, "/../") != NULL + || (*zfile == '/' + && (! fin_directory_list (zfile, qsys->uuconf_pzremote_send, + qsys->uuconf_zpubdir, TRUE, FALSE, + (const char *) NULL) + || ! fin_directory_list (zfile, qsys->uuconf_pzremote_receive, + qsys->uuconf_zpubdir, TRUE, FALSE, + (const char *) NULL)))) + { + ulog (LOG_ERROR, "Not permitted to refer to file \"%s\"", zfile); + return FALSE; + } + + return TRUE; +} + +#endif /* ! ALLOW_FILENAME_ARGUMENTS */ + +/* Invoke the command specified by an execute file. */ + +/*ARGSUSED*/ +boolean +fsysdep_execute (qsys, zuser, pazargs, zfullcmd, zinput, zoutput, + fshell, iseq, pzerror, pftemp) + const struct uuconf_system *qsys; + const char *zuser; + const char **pazargs; + const char *zfullcmd; + const char *zinput; + const char *zoutput; + boolean fshell; + int iseq; + char **pzerror; + boolean *pftemp; +{ + int aidescs[3]; + boolean ferr; + pid_t ipid; + int ierr; + char abxqtdir[sizeof XQTDIR + 4]; + const char *zxqtdir; + int istat; + char *zpath; +#if ALLOW_SH_EXECUTION + const char *azshargs[4]; +#endif + + *pzerror = NULL; + *pftemp = FALSE; + + aidescs[0] = SPAWN_NULL; + aidescs[1] = SPAWN_NULL; + aidescs[2] = SPAWN_NULL; + + ferr = FALSE; + + if (zinput != NULL) + { + aidescs[0] = open ((char *) zinput, O_RDONLY | O_NOCTTY, 0); + if (aidescs[0] < 0) + { + ulog (LOG_ERROR, "open (%s): %s", zinput, strerror (errno)); + ferr = TRUE; + } + else if (fcntl (aidescs[0], F_SETFD, + fcntl (aidescs[0], F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + ferr = TRUE; + } + } + + if (! ferr && zoutput != NULL) + { + aidescs[1] = creat ((char *) zoutput, IPRIVATE_FILE_MODE); + if (aidescs[1] < 0) + { + ulog (LOG_ERROR, "creat (%s): %s", zoutput, strerror (errno)); + *pftemp = TRUE; + ferr = TRUE; + } + else if (fcntl (aidescs[1], F_SETFD, + fcntl (aidescs[1], F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + ferr = TRUE; + } + } + + if (! ferr) + { + *pzerror = zstemp_file (qsys); + aidescs[2] = creat (*pzerror, IPRIVATE_FILE_MODE); + if (aidescs[2] < 0) + { + if (errno == ENOENT) + { + if (! fsysdep_make_dirs (*pzerror, FALSE)) + { + *pftemp = TRUE; + ferr = TRUE; + } + else + aidescs[2] = creat (*pzerror, IPRIVATE_FILE_MODE); + } + if (! ferr && aidescs[2] < 0) + { + ulog (LOG_ERROR, "creat (%s): %s", *pzerror, strerror (errno)); + *pftemp = TRUE; + ferr = TRUE; + } + } + if (! ferr + && fcntl (aidescs[2], F_SETFD, + fcntl (aidescs[2], F_GETFD, 0) | FD_CLOEXEC) < 0) + { + ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); + ferr = TRUE; + } + } + + if (iseq == 0) + zxqtdir = XQTDIR; + else + { + sprintf (abxqtdir, "%s%04d", XQTDIR, iseq); + zxqtdir = abxqtdir; + } + + if (ferr) + { + if (aidescs[0] != SPAWN_NULL) + (void) close (aidescs[0]); + if (aidescs[1] != SPAWN_NULL) + (void) close (aidescs[1]); + if (aidescs[2] != SPAWN_NULL) + (void) close (aidescs[2]); + ubuffree (*pzerror); + return FALSE; + } + +#if ALLOW_SH_EXECUTION + if (fshell) + { + azshargs[0] = "/bin/sh"; + azshargs[1] = "-c"; + azshargs[2] = zfullcmd; + azshargs[3] = NULL; + pazargs = azshargs; + } +#else + fshell = FALSE; +#endif + + if (qsys->uuconf_pzpath == NULL) + zpath = NULL; + else + { + size_t c; + char **pz; + + c = 0; + for (pz = qsys->uuconf_pzpath; *pz != NULL; pz++) + c += strlen (*pz) + 1; + zpath = zbufalc (c); + *zpath = '\0'; + for (pz = qsys->uuconf_pzpath; *pz != NULL; pz++) + { + strcat (zpath, *pz); + if (pz[1] != NULL) + strcat (zpath, ":"); + } + } + + /* Pass zchdir as zxqtdir, fnosigs as TRUE, fshell as TRUE if we + aren't already using the shell. */ + ipid = ixsspawn (pazargs, aidescs, FALSE, FALSE, zxqtdir, TRUE, + ! fshell, zpath, qsys->uuconf_zname, zuser); + + ierr = errno; + + ubuffree (zpath); + + if (aidescs[0] != SPAWN_NULL) + (void) close (aidescs[0]); + if (aidescs[1] != SPAWN_NULL) + (void) close (aidescs[1]); + if (aidescs[2] != SPAWN_NULL) + (void) close (aidescs[2]); + + if (ipid < 0) + { + ulog (LOG_ERROR, "ixsspawn: %s", strerror (ierr)); + *pftemp = TRUE; + return FALSE; + } + + istat = ixswait ((unsigned long) ipid, "Execution"); + + if (istat == EX_TEMPFAIL) + *pftemp = TRUE; + + return istat == 0; +} + +/* Lock a uuxqt process. */ + +int +ixsysdep_lock_uuxqt (zcmd, cmaxuuxqts) + const char *zcmd; + int cmaxuuxqts; +{ + char ab[sizeof "LCK.XQT.9999"]; + int i; + + if (cmaxuuxqts <= 0 || cmaxuuxqts >= 10000) + cmaxuuxqts = 9999; + for (i = 0; i < cmaxuuxqts; i++) + { + sprintf (ab, "LCK.XQT.%d", i); + if (fsdo_lock (ab, TRUE, (boolean *) NULL)) + break; + } + if (i >= cmaxuuxqts) + return -1; + + if (zcmd != NULL) + { + char abcmd[sizeof "LXQ.123456789"]; + + sprintf (abcmd, "LXQ.%.9s", zcmd); + abcmd[strcspn (abcmd, " \t/")] = '\0'; + if (! fsdo_lock (abcmd, TRUE, (boolean *) NULL)) + { + (void) fsdo_unlock (ab, TRUE); + return -1; + } + } + + return i; +} + +/* Unlock a uuxqt process. */ + +boolean +fsysdep_unlock_uuxqt (iseq, zcmd, cmaxuuxqts) + int iseq; + const char *zcmd; + int cmaxuuxqts; +{ + char ab[sizeof "LCK.XQT.9999"]; + boolean fret; + + fret = TRUE; + + sprintf (ab, "LCK.XQT.%d", iseq); + if (! fsdo_unlock (ab, TRUE)) + fret = FALSE; + + if (zcmd != NULL) + { + char abcmd[sizeof "LXQ.123456789"]; + + sprintf (abcmd, "LXQ.%.9s", zcmd); + abcmd[strcspn (abcmd, " \t/")] = '\0'; + if (! fsdo_unlock (abcmd, TRUE)) + fret = FALSE; + } + + return fret; +} + +/* See whether a particular uuxqt command is locked (this depends on + the implementation of fsdo_lock). */ + +boolean +fsysdep_uuxqt_locked (zcmd) + const char *zcmd; +{ + char ab[sizeof "LXQ.123456789"]; + struct stat s; + + sprintf (ab, "LXQ.%.9s", zcmd); + return stat (ab, &s) == 0; +} + +/* Lock a particular execute file. */ + +boolean +fsysdep_lock_uuxqt_file (zfile) + const char *zfile; +{ + char *zcopy, *z; + boolean fret; + + zcopy = zbufcpy (zfile); + + z = strrchr (zcopy, '/'); + if (z == NULL) + *zcopy = 'L'; + else + *(z + 1) = 'L'; + + fret = fsdo_lock (zcopy, TRUE, (boolean *) NULL); + ubuffree (zcopy); + return fret; +} + +/* Unlock a particular execute file. */ + +boolean +fsysdep_unlock_uuxqt_file (zfile) + const char *zfile; +{ + char *zcopy, *z; + boolean fret; + + zcopy = zbufcpy (zfile); + + z = strrchr (zcopy, '/'); + if (z == NULL) + *zcopy = 'L'; + else + *(z + 1) = 'L'; + + fret = fsdo_unlock (zcopy, TRUE); + ubuffree (zcopy); + return fret; +} + +/* Lock the execute directory. Since we use a different directory + depending on which LCK.XQT.dddd file we got, there is actually no + need to create a lock file. We do make sure that the directory + exists, though. */ + +boolean +fsysdep_lock_uuxqt_dir (iseq) + int iseq; +{ + const char *zxqtdir; + char abxqtdir[sizeof XQTDIR + 4]; + + if (iseq == 0) + zxqtdir = XQTDIR; + else + { + sprintf (abxqtdir, "%s%04d", XQTDIR, iseq); + zxqtdir = abxqtdir; + } + + if (mkdir (zxqtdir, S_IRWXU) < 0 + && errno != EEXIST) + { + ulog (LOG_ERROR, "mkdir (%s): %s", zxqtdir, strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/* Unlock the execute directory and clear it out. The lock is + actually the LCK.XQT.dddd file, so we don't unlock it, but we do + remove all the files. */ + +boolean +fsysdep_unlock_uuxqt_dir (iseq) + int iseq; +{ + const char *zxqtdir; + char abxqtdir[sizeof XQTDIR + 4]; + DIR *qdir; + + if (iseq == 0) + zxqtdir = XQTDIR; + else + { + sprintf (abxqtdir, "%s%04d", XQTDIR, iseq); + zxqtdir = abxqtdir; + } + + qdir = opendir ((char *) zxqtdir); + if (qdir != NULL) + { + struct dirent *qentry; + + while ((qentry = readdir (qdir)) != NULL) + { + char *z; + + if (strcmp (qentry->d_name, ".") == 0 + || strcmp (qentry->d_name, "..") == 0) + continue; + z = zsysdep_in_dir (zxqtdir, qentry->d_name); + if (remove (z) < 0) + { + int ierr; + + ierr = errno; + if (! fsysdep_directory (z)) + ulog (LOG_ERROR, "remove (%s): %s", z, + strerror (ierr)); + else + (void) fsysdep_rmdir (z); + } + ubuffree (z); + } + + closedir (qdir); + } + + return TRUE; +} + +/* Move files into the execution directory. */ + +boolean +fsysdep_move_uuxqt_files (cfiles, pzfrom, pzto, fto, iseq, pzinput) + int cfiles; + const char *const *pzfrom; + const char *const *pzto; + boolean fto; + int iseq; + char **pzinput; +{ + char *zinput; + const char *zxqtdir; + char abxqtdir[sizeof XQTDIR + 4]; + int i; + + if (pzinput == NULL) + zinput = NULL; + else + zinput = *pzinput; + + if (iseq == 0) + zxqtdir = XQTDIR; + else + { + sprintf (abxqtdir, "%s%04d", XQTDIR, iseq); + zxqtdir = abxqtdir; + } + + for (i = 0; i < cfiles; i++) + { + const char *zfrom, *zto; + char *zfree; + + if (pzto[i] == NULL) + continue; + + zfree = zsysdep_in_dir (zxqtdir, pzto[i]); + + zfrom = pzfrom[i]; + zto = zfree; + + if (zinput != NULL && strcmp (zinput, zfrom) == 0) + { + *pzinput = zbufcpy (zto); + zinput = NULL; + } + + if (! fto) + { + const char *ztemp; + + ztemp = zfrom; + zfrom = zto; + zto = ztemp; + (void) chmod (zfrom, IPRIVATE_FILE_MODE); + } + + if (rename (zfrom, zto) < 0) + { +#if HAVE_RENAME + /* On some systems the system call rename seems to fail for + arbitrary reasons. To get around this, we always try to + copy the file by hand if the rename failed. */ + errno = EXDEV; +#endif + + if (errno != EXDEV) + { + ulog (LOG_ERROR, "rename (%s, %s): %s", zfrom, zto, + strerror (errno)); + ubuffree (zfree); + break; + } + + if (! fcopy_file (zfrom, zto, FALSE, FALSE)) + { + ubuffree (zfree); + break; + } + if (remove (zfrom) < 0) + ulog (LOG_ERROR, "remove (%s): %s", zfrom, + strerror (errno)); + } + + if (fto) + (void) chmod (zto, IPUBLIC_FILE_MODE); + + ubuffree (zfree); + } + + if (i < cfiles) + { + if (fto) + (void) fsysdep_move_uuxqt_files (i, pzfrom, pzto, FALSE, iseq, + (char **) NULL); + return FALSE; + } + + return TRUE; +} |
