1*48664Sbostic /*- 2*48664Sbostic * Copyright (c) 1985 The Regents of the University of California. 3*48664Sbostic * All rights reserved. 4*48664Sbostic * 5*48664Sbostic * %sccs.include.proprietary.c% 6*48664Sbostic */ 7*48664Sbostic 813639Ssam #ifndef lint 9*48664Sbostic static char sccsid[] = "@(#)cico.c 5.21 (Berkeley) 04/24/91"; 10*48664Sbostic #endif /* not lint */ 1113639Ssam 1223590Sbloom #include <signal.h> 1313639Ssam #include "uucp.h" 1413639Ssam #include <setjmp.h> 1517767Sralph #ifdef USG 1613639Ssam #include <termio.h> 1733559Srick #include <fcntl.h> 1813639Ssam #endif 1917767Sralph #ifndef USG 2013639Ssam #include <sgtty.h> 2113639Ssam #endif 2217767Sralph #ifdef BSDTCP 2317767Sralph #include <netdb.h> 2417767Sralph #include <netinet/in.h> 2517767Sralph #include <sys/socket.h> 2617767Sralph #endif BSDTCP 2717767Sralph #include <sys/stat.h> 2833559Srick #ifdef BSD4_2 2933559Srick #include <sys/time.h> 3033559Srick #include <fcntl.h> 3133559Srick #else 3233559Srick #include <time.h> 3333559Srick #endif 3417767Sralph #include "uust.h" 3517767Sralph #include "uusub.h" 3637930Sbostic #include "pathnames.h" 3713639Ssam 3823590Sbloom #if defined(VMS) && defined(BSDTCP) 3923590Sbloom #define NOGETPEER 4023590Sbloom #endif 4123590Sbloom 4217767Sralph jmp_buf Sjbuf; 4317767Sralph jmp_buf Pipebuf; 4413639Ssam 4517767Sralph /* call fail text */ 4613639Ssam char *Stattext[] = { 4713639Ssam "", 4813639Ssam "BAD SYSTEM", 4917767Sralph "WRONG TIME TO CALL", 5013639Ssam "SYSTEM LOCKED", 5113639Ssam "NO DEVICE", 5225703Sbloom "CALL FAILED", 5313639Ssam "LOGIN FAILED", 5413639Ssam "BAD SEQUENCE" 5517767Sralph }; 5613639Ssam 5717767Sralph /* call fail codes */ 5817767Sralph int Stattype[] = { 5917767Sralph 0, 6017767Sralph 0, 6117767Sralph SS_WRONGTIME, 6217767Sralph 0, 6317767Sralph SS_NODEVICE, 6417767Sralph SS_FAIL, 6517767Sralph SS_FAIL, 6617767Sralph SS_BADSEQ 6717767Sralph }; 6813639Ssam 6925703Sbloom /* Arguments to setdebug(): */ 7025703Sbloom #define DBG_TEMP 0 /* Create a temporary audit file */ 7125703Sbloom #define DBG_PERM 1 /* Create a permanent audit file */ 7225703Sbloom #define DBG_CLEAN 2 /* Cleanup, discard temp file */ 7313639Ssam 7417767Sralph int ReverseRole = 0; 7517767Sralph int Role = SLAVE; 7633559Srick int InitialRole = SLAVE; 7733559Srick long StartTime; 7817767Sralph int onesys = 0; 7918616Sralph int turntime = 30 * 60; /* 30 minutes expressed in seconds */ 8025703Sbloom char *ttyn = NULL; 8117767Sralph extern int LocalOnly; 8225703Sbloom extern int errno; 8318616Sralph extern char MaxGrade, DefMaxGrade; 8418616Sralph extern char Myfullname[]; 8517767Sralph 8633559Srick long Bytes_Sent, Bytes_Received; 8733559Srick 8817767Sralph #ifdef USG 8913639Ssam struct termio Savettyb; 9013639Ssam #endif 9117767Sralph #ifndef USG 9213639Ssam struct sgttyb Savettyb; 9313639Ssam #endif 9413639Ssam 9534164Srick #define SETPROCTITLE 9634164Srick #ifdef SETPROCTITLE 9734164Srick char **Argv = NULL; /* pointer to argument vector */ 9834164Srick char *LastArgv = NULL; /* end of argv */ 9934164Srick #endif SETPROCTITLE 10034164Srick 10125703Sbloom /* 10225703Sbloom * this program is used to place a call to a 10313639Ssam * remote machine, login, and copy files between the two machines. 10413639Ssam */ 10534164Srick main(argc, argv, envp) 10625703Sbloom int argc; 10733945Srick char **argv; 10834164Srick char **envp; 10913639Ssam { 11013639Ssam register int ret; 11113639Ssam int seq; 11213639Ssam char wkpre[NAMESIZE], file[NAMESIZE]; 11325703Sbloom char msg[MAXFULLNAME], *q; 11413639Ssam register char *p; 11546879Sbostic static void onintr(), timeout(), dbg_signal(); 11646879Sbostic static char *pskip(); 11733945Srick extern char *optarg; 11833945Srick extern int optind; 11923725Sbloom char rflags[MAXFULLNAME]; 12023590Sbloom #ifdef NOGETPEER 12117767Sralph u_long Hostnumber = 0; 12223590Sbloom #endif NOGETPEER 12313639Ssam 12413639Ssam strcpy(Progname, "uucico"); 12513639Ssam 12626149Sbloom #ifdef BSD4_2 12733945Srick sigsetmask(0L); /* in case we inherit blocked signals */ 12826149Sbloom #endif BSD4_2 12913639Ssam signal(SIGINT, onintr); 13013639Ssam signal(SIGHUP, onintr); 13113639Ssam signal(SIGQUIT, onintr); 13213639Ssam signal(SIGTERM, onintr); 13313639Ssam signal(SIGPIPE, onintr); /* 4.1a tcp-ip stupidity */ 13436449Smarc signal(SIGUSR1, dbg_signal); 13513639Ssam ret = guinfo(getuid(), User, msg); 13613639Ssam strcpy(Loginuser, User); 13723590Sbloom uucpname(Myname); 13833945Srick if (ret == FAIL) { 13933945Srick syslog(LOG_ERR, "can't get uid"); 14033945Srick cleanup(FAIL); 14133945Srick } 14213639Ssam 14325703Sbloom setbuf (stderr, CNULL); 14425703Sbloom 14525703Sbloom rflags[0] = '\0'; 14613639Ssam umask(WFMASK); 14713639Ssam strcpy(Rmtname, Myname); 14813639Ssam Ifn = Ofn = -1; 14933945Srick while ((ret = getopt(argc, argv, "RLd:g:p:r:s:x:t:")) != EOF) 15033945Srick switch(ret){ 15113639Ssam case 'd': 15233945Srick Spool = optarg; 15313639Ssam break; 15413639Ssam case 'g': 15518616Sralph case 'p': 15633945Srick MaxGrade = DefMaxGrade = *optarg; 15713639Ssam break; 15813639Ssam case 'r': 15933945Srick Role = atoi(optarg); 16013639Ssam break; 16117767Sralph case 'R': 16217767Sralph ReverseRole++; 16317767Sralph Role = MASTER; 16417767Sralph break; 16513639Ssam case 's': 16633945Srick strncpy(Rmtname, optarg, MAXBASENAME); 16723590Sbloom Rmtname[MAXBASENAME] = '\0'; 16813639Ssam if (Rmtname[0] != '\0') 16913639Ssam onesys = 1; 17013639Ssam break; 17113639Ssam case 'x': 17233945Srick Debug = atoi(optarg); 17313639Ssam if (Debug <= 0) 17413639Ssam Debug = 1; 17533945Srick strcat(rflags, argv[optind-1]); 17613639Ssam break; 17718616Sralph case 't': 17833945Srick turntime = atoi(optarg)*60;/* minutes to seconds */ 17918616Sralph break; 18017767Sralph case 'L': /* local calls only */ 18117767Sralph LocalOnly++; 18217767Sralph break; 18323590Sbloom #ifdef NOGETPEER 18417767Sralph case 'h': 18517767Sralph Hostnumber = inet_addr(&argv[1][2]); 18617767Sralph break; 18723590Sbloom #endif NOGETPEER 18833945Srick case '?': 18913639Ssam default: 19033945Srick fprintf(stderr, "unknown flag %s (ignored)\n", 19133945Srick argv[optind-1]); 19213639Ssam break; 19313639Ssam } 19413639Ssam 19533945Srick while (optind < argc) 19633945Srick fprintf(stderr, "unknown argument %s (ignored)\n", 19733945Srick argv[optind++]); 19817767Sralph 19927069Sbloom if (Debug && Role == MASTER) 20027069Sbloom chkdebug(); 20127069Sbloom 20234164Srick #ifdef SETPROCTITLE 20334164Srick /* 20434164Srick * Save start and extent of argv for setproctitle. 20534164Srick */ 20634164Srick 20734164Srick Argv = argv; 20834164Srick LastArgv = argv[argc - 1] + strlen(argv[argc - 1]); 20934164Srick #endif SETPROCTITLE 21034164Srick 21125124Sbloom /* Try to run as uucp */ 21217767Sralph setgid(getegid()); 21317767Sralph setuid(geteuid()); 21417767Sralph #ifdef TIOCNOTTY 21517767Sralph /* 21617767Sralph * detach uucico from controlling terminal 21717767Sralph * to defend against rlogind sending us a SIGKILL (!!!) 21817767Sralph */ 21937930Sbostic if (Role == MASTER && (ret = open(_PATH_TTY, 2)) >= 0) { 22017767Sralph ioctl(ret, TIOCNOTTY, STBNULL); 22117767Sralph close(ret); 22217767Sralph } 22317767Sralph #endif TIOCNOTTY 22417767Sralph #ifdef BSD4_2 22525517Stef if (getpgrp(0) == 0) { /* We have no controlling terminal */ 22617767Sralph setpgrp(0, getpid()); 22717767Sralph } 22833945Srick #ifdef USE_SYSLOG 22933945Srick #ifdef BSD4_3 23033945Srick openlog("uucico", LOG_PID, LOG_UUCP); 23133945Srick #else /* !BSD4_3 */ 23233945Srick openlog("uucico", LOG_PID); 23333945Srick #endif /* !BSD4_3 */ 23433945Srick #endif /* USE_SYSLOG */ 23517767Sralph #endif BSD4_2 23617767Sralph 23733945Srick #ifdef BSD4_3 23833945Srick unsetenv("TZ"); /* We don't want him resetting our time zone */ 23933945Srick #endif /* !BSD4_3 */ 24033945Srick 24133945Srick if (subchdir(Spool) < 0) { 24233945Srick syslog(LOG_ERR, "chdir(%s) failed: %m", Spool); 24333945Srick cleanup(FAIL); 24433945Srick } 24533945Srick 24613639Ssam strcpy(Wrkdir, Spool); 24713639Ssam 24825703Sbloom if (Debug) { 24925703Sbloom setdebug ((Role == SLAVE) ? DBG_TEMP : DBG_PERM); 25025703Sbloom if (Debug > 0) 25125703Sbloom logent ("Local Enabled", "DEBUG"); 25225703Sbloom } 25325703Sbloom 25425703Sbloom /* 25525703Sbloom * First time through: If we're the slave, do initial checking. 25625703Sbloom */ 25713639Ssam if (Role == SLAVE) { 25817767Sralph /* check for /etc/nologin */ 25925124Sbloom if (access(NOLOGIN, 0) == 0) { 26017767Sralph logent(NOLOGIN, "UUCICO SHUTDOWN"); 26123590Sbloom if (Debug > 4) 26217767Sralph logent("DEBUGGING", "continuing anyway"); 26317767Sralph else 26417767Sralph cleanup(1); 26517767Sralph } 26625703Sbloom Ifn = 0; 26725703Sbloom Ofn = 1; 26817767Sralph #ifdef TCPIP 26917767Sralph /* 27017767Sralph * Determine if we are on TCPIP 27117767Sralph */ 27233559Srick if (isatty(Ifn) == 0) { 27317767Sralph IsTcpIp = 1; 27417767Sralph DEBUG(4, "TCPIP connection -- ioctl-s disabled\n", CNULL); 27523590Sbloom } else 27623590Sbloom IsTcpIp = 0; 27717767Sralph #endif TCPIP 27813639Ssam /* initial handshake */ 27913639Ssam onesys = 1; 28017767Sralph if (!IsTcpIp) { 28117767Sralph #ifdef USG 28225703Sbloom ret = ioctl(Ifn, TCGETA, &Savettyb); 28313639Ssam Savettyb.c_cflag = (Savettyb.c_cflag & ~CS8) | CS7; 28413639Ssam Savettyb.c_oflag |= OPOST; 28513639Ssam Savettyb.c_lflag |= (ISIG|ICANON|ECHO); 28617767Sralph #else !USG 28725703Sbloom ret = ioctl(Ifn, TIOCGETP, &Savettyb); 28813639Ssam Savettyb.sg_flags |= ECHO; 28913639Ssam Savettyb.sg_flags &= ~RAW; 29017767Sralph #endif !USG 29125703Sbloom ttyn = ttyname(Ifn); 29213639Ssam } 29313639Ssam fixmode(Ifn); 29425703Sbloom 29525703Sbloom /* 29625703Sbloom * Initial Message -- tell them we're here, and who we are. 29725703Sbloom */ 29818616Sralph sprintf(msg, "here=%s", Myfullname); 29917767Sralph omsg('S', msg, Ofn); 30013639Ssam signal(SIGALRM, timeout); 30133559Srick alarm(IsTcpIp?MAXMSGTIME*4:MAXMSGTIME); 30213639Ssam if (setjmp(Sjbuf)) { 30313639Ssam /* timed out */ 30417767Sralph if (!IsTcpIp) { 30517767Sralph #ifdef USG 30625703Sbloom ret = ioctl(Ifn, TCSETA, &Savettyb); 30725703Sbloom 30823590Sbloom #else !USG 30925703Sbloom ret = ioctl(Ifn, TIOCSETP, &Savettyb); 31023590Sbloom #endif !USG 31113639Ssam } 31217767Sralph cleanup(0); 31313639Ssam } 31413639Ssam for (;;) { 31513639Ssam ret = imsg(msg, Ifn); 31625703Sbloom if (ret != SUCCESS) { 31713639Ssam alarm(0); 31817767Sralph if (!IsTcpIp) { 31917767Sralph #ifdef USG 32025703Sbloom ret = ioctl(Ifn, TCSETA, &Savettyb); 32123590Sbloom #else !USG 32225703Sbloom ret = ioctl(Ifn, TIOCSETP, &Savettyb); 32323590Sbloom #endif !USG 32413639Ssam } 32517767Sralph cleanup(0); 32613639Ssam } 32713639Ssam if (msg[0] == 'S') 32813639Ssam break; 32913639Ssam } 33013639Ssam alarm(0); 33113639Ssam q = &msg[1]; 33213639Ssam p = pskip(q); 33323590Sbloom strncpy(Rmtname, q, MAXBASENAME); 33423590Sbloom Rmtname[MAXBASENAME] = '\0'; 33525703Sbloom 33625703Sbloom /* 33725703Sbloom * Now that we know who they are, give the audit file the right 33825703Sbloom * name. 33925703Sbloom */ 34025703Sbloom setdebug (DBG_PERM); 34113639Ssam DEBUG(4, "sys-%s\n", Rmtname); 34223725Sbloom /* The versys will also do an alias on the incoming name */ 34323725Sbloom if (versys(&Rmtname)) { 34433559Srick #ifdef NOSTRANGERS 34523725Sbloom /* If we don't know them, we won't talk to them... */ 34633945Srick syslog(LOG_WARNING, "Unknown host: %s", Rmtname); 34723590Sbloom omsg('R', "You are unknown to me", Ofn); 34823590Sbloom cleanup(0); 34923725Sbloom #endif NOSTRANGERS 35023590Sbloom } 35117767Sralph #ifdef BSDTCP 35217767Sralph /* we must make sure they are really who they say they 35317767Sralph * are. We compare the hostnumber with the number in the hosts 35417767Sralph * table for the site they claim to be. 35517767Sralph */ 35617767Sralph if (IsTcpIp) { 35717767Sralph struct hostent *hp; 35817767Sralph char *cpnt, *inet_ntoa(); 35925703Sbloom int fromlen; 36017767Sralph struct sockaddr_in from; 36125124Sbloom extern char PhoneNumber[]; 36217767Sralph 36323590Sbloom #ifdef NOGETPEER 36423590Sbloom from.sin_addr.s_addr = Hostnumber; 36523590Sbloom from.sin_family = AF_INET; 36623590Sbloom #else !NOGETPEER 36725703Sbloom fromlen = sizeof(from); 36846879Sbostic if (getpeername(Ifn, 36946879Sbostic (struct sockaddr *)&from, &fromlen) < 0) { 37017767Sralph logent(Rmtname, "NOT A TCP CONNECTION"); 37117767Sralph omsg('R', "NOT TCP", Ofn); 37217767Sralph cleanup(0); 37317767Sralph } 37423590Sbloom #endif !NOGETPEER 37546879Sbostic hp = gethostbyaddr((char *)&from.sin_addr, 37617767Sralph sizeof (struct in_addr), from.sin_family); 37725703Sbloom if (hp == NULL) { 37817767Sralph /* security break or just old host table? */ 37917767Sralph logent(Rmtname, "UNKNOWN IP-HOST Name ="); 38017767Sralph cpnt = inet_ntoa(from.sin_addr), 38117767Sralph logent(cpnt, "UNKNOWN IP-HOST Number ="); 38217767Sralph sprintf(wkpre, "%s/%s isn't in my host table", 38317767Sralph Rmtname, cpnt); 38417767Sralph omsg('R' ,wkpre ,Ofn); 38517767Sralph cleanup(0); 38617767Sralph } 38725703Sbloom if (Debug > 99) 38817767Sralph logent(Rmtname,"Request from IP-Host name ="); 38925124Sbloom /* 39025124Sbloom * The following is to determine if the name given us by 39125124Sbloom * the Remote uucico matches any of the names 39217767Sralph * given its network number (remote machine) in our 39317767Sralph * host table. 39425124Sbloom * We could check the aliases, but that won't work in 39525124Sbloom * all cases (like if you are running the domain 39625124Sbloom * server, where you don't get any aliases). The only 39725124Sbloom * reliable way I can think of that works in ALL cases 39825124Sbloom * is too look up the site in L.sys and see if the 39925124Sbloom * sitename matches what we would call him if we 40025124Sbloom * originated the call. 40117767Sralph */ 40225124Sbloom /* PhoneNumber contains the official network name of the host we are checking. (set in versys.c) */ 40325124Sbloom if (sncncmp(PhoneNumber, hp->h_name, SYSNSIZE) == 0) { 40417767Sralph if (Debug > 99) 40517767Sralph logent(q,"Found in host Tables"); 40625124Sbloom } else { 40725124Sbloom logent(hp->h_name, "FORGED HOSTNAME"); 40825124Sbloom logent(inet_ntoa(from.sin_addr), "ORIGINATED AT"); 40925124Sbloom logent(PhoneNumber, "SHOULD BE"); 41025124Sbloom sprintf(wkpre, "You're not who you claim to be: %s != %s", hp->h_name, PhoneNumber); 41125124Sbloom omsg('R', wkpre, Ofn); 41225124Sbloom cleanup(0); 41317767Sralph } 41417767Sralph } 41517767Sralph #endif BSDTCP 41617767Sralph 41725703Sbloom if (mlock(Rmtname)) { 41813639Ssam omsg('R', "LCK", Ofn); 41913639Ssam cleanup(0); 42013639Ssam } 42113639Ssam else if (callback(Loginuser)) { 42213639Ssam signal(SIGINT, SIG_IGN); 42313639Ssam signal(SIGHUP, SIG_IGN); 42413639Ssam omsg('R', "CB", Ofn); 42513639Ssam logent("CALLBACK", "REQUIRED"); 42613639Ssam /* set up for call back */ 42717767Sralph systat(Rmtname, SS_CALLBACK, "CALLING BACK"); 42813639Ssam gename(CMDPRE, Rmtname, 'C', file); 42913639Ssam close(creat(subfile(file), 0666)); 43013639Ssam xuucico(Rmtname); 43113639Ssam cleanup(0); 43213639Ssam } 43313639Ssam seq = 0; 43413639Ssam while (*p == '-') { 43513639Ssam q = pskip(p); 43613639Ssam switch(*(++p)) { 43713639Ssam case 'x': 43825703Sbloom if (Debug == 0) { 43925703Sbloom Debug = atoi(++p); 44025703Sbloom if (Debug <= 0) 44125703Sbloom Debug = 1; 44225703Sbloom setdebug(DBG_PERM); 44325703Sbloom if (Debug > 0) 44425703Sbloom logent("Remote Enabled", "DEBUG"); 44525703Sbloom } else { 44625703Sbloom DEBUG(1, "Remote debug request ignored\n", 44725703Sbloom CNULL); 44825703Sbloom } 44913639Ssam break; 45013639Ssam case 'Q': 45113639Ssam seq = atoi(++p); 45213639Ssam break; 45318616Sralph case 'p': 45418616Sralph MaxGrade = DefMaxGrade = *++p; 45518616Sralph DEBUG(4, "MaxGrade set to %c\n", MaxGrade); 45618616Sralph break; 45723590Sbloom case 'v': 45823590Sbloom if (strncmp(p, "grade", 5) == 0) { 45923590Sbloom p += 6; 46023590Sbloom MaxGrade = DefMaxGrade = *p++; 46123590Sbloom DEBUG(4, "MaxGrade set to %c\n", MaxGrade); 46223590Sbloom } 46323590Sbloom break; 46413639Ssam default: 46513639Ssam break; 46613639Ssam } 46713639Ssam p = q; 46813639Ssam } 46934164Srick setproctitle("%s: startup", Rmtname); 47013639Ssam if (callok(Rmtname) == SS_BADSEQ) { 47113639Ssam logent("BADSEQ", "PREVIOUS"); 47213639Ssam omsg('R', "BADSEQ", Ofn); 47313639Ssam cleanup(0); 47413639Ssam } 47517767Sralph #ifdef GNXSEQ 47613639Ssam if ((ret = gnxseq(Rmtname)) == seq) { 47713639Ssam omsg('R', "OK", Ofn); 47813639Ssam cmtseq(); 47917767Sralph } else { 48017767Sralph #else !GNXSEQ 48117767Sralph if (seq == 0) 48217767Sralph omsg('R', "OK", Ofn); 48313639Ssam else { 48417767Sralph #endif !GNXSEQ 48513639Ssam systat(Rmtname, Stattype[7], Stattext[7]); 48623590Sbloom logent("BAD SEQ", "FAILED HANDSHAKE"); 48717767Sralph #ifdef GNXSEQ 48813639Ssam ulkseq(); 48917767Sralph #endif GNXSEQ 49013639Ssam omsg('R', "BADSEQ", Ofn); 49113639Ssam cleanup(0); 49213639Ssam } 49313639Ssam if (ttyn != NULL) 49413639Ssam chmod(ttyn, 0600); 49513639Ssam } 49617767Sralph 49713639Ssam loop: 49817767Sralph if(setjmp(Pipebuf)) { /* come here on SIGPIPE */ 49917767Sralph clsacu(); 50034164Srick logcls(); 50117767Sralph close(Ofn); 50217767Sralph close(Ifn); 50317767Sralph Ifn = Ofn = -1; 50417767Sralph rmlock(CNULL); 50517767Sralph sleep(3); 50617767Sralph } 50713639Ssam if (!onesys) { 50833559Srick do_connect_accounting(); 50933945Srick #ifdef DIALINOUT 51033945Srick /* reenable logins on dialout */ 51133945Srick reenable(); 51233945Srick #endif DIALINOUT 51333559Srick StartTime = 0; 51434164Srick setproctitle("looking for work"); 51513639Ssam ret = gnsys(Rmtname, Spool, CMDPRE); 51634164Srick setproctitle("%s: startup", Rmtname); 51725703Sbloom setdebug(DBG_PERM); 51813639Ssam if (ret == FAIL) 51913639Ssam cleanup(100); 52033559Srick else if (ret == SUCCESS) 52113639Ssam cleanup(0); 52234164Srick logcls(); 52317767Sralph } else if (Role == MASTER && callok(Rmtname) != 0) { 52413639Ssam logent("SYSTEM STATUS", "CAN NOT CALL"); 52513639Ssam cleanup(0); 52613639Ssam } 52713639Ssam 52823590Sbloom sprintf(wkpre, "%c.%.*s", CMDPRE, SYSNSIZE, Rmtname); 52933559Srick StartTime = 0; 53033559Srick Bytes_Sent = Bytes_Received = 0L; 53113639Ssam 53217767Sralph signal(SIGINT, SIG_IGN); 53317767Sralph signal(SIGQUIT, SIG_IGN); 53413639Ssam if (Role == MASTER) { 53533559Srick extern char LineType[]; 53617767Sralph /* check for /etc/nologin */ 53725124Sbloom if (access(NOLOGIN, 0) == 0) { 53817767Sralph logent(NOLOGIN, "UUCICO SHUTDOWN"); 53923590Sbloom if (Debug > 4) 54017767Sralph logent("DEBUGGING", "continuing anyway"); 54117767Sralph else 54217767Sralph cleanup(1); 54317767Sralph } 54413639Ssam /* master part */ 54513639Ssam signal(SIGHUP, SIG_IGN); 54613639Ssam if (Ifn != -1 && Role == MASTER) { 54713639Ssam write(Ofn, EOTMSG, strlen(EOTMSG)); 54813639Ssam clsacu(); 54913639Ssam close(Ofn); 55013639Ssam close(Ifn); 55113639Ssam Ifn = Ofn = -1; 55213639Ssam rmlock(CNULL); 55313639Ssam sleep(3); 55413639Ssam } 55525124Sbloom if (mlock(Rmtname) != SUCCESS) { 55633559Srick DEBUG(1, "LOCKED: call to %s\n", Rmtname); 55717767Sralph US_SST(us_s_lock); 55813639Ssam goto next; 55913639Ssam } 56034164Srick setproctitle("%s: starting call", Rmtname); 56113639Ssam Ofn = Ifn = conn(Rmtname); 56234164Srick sprintf(msg, "(call to %s via %s)", Rmtname, LineType); 56313639Ssam if (Ofn < 0) { 56417767Sralph if (Ofn != CF_TIME) 56517767Sralph logent(msg, _FAILED); 56617767Sralph /* avoid excessive 'wrong time' info */ 56723590Sbloom if (Stattype[-Ofn] != SS_WRONGTIME){ 56817767Sralph systat(Rmtname, Stattype[-Ofn], Stattext[-Ofn]); 56917767Sralph US_SST(-Ofn); 57017767Sralph UB_SST(-Ofn); 57117767Sralph } 57213639Ssam goto next; 57317767Sralph } else { 57413639Ssam logent(msg, "SUCCEEDED"); 57517767Sralph US_SST(us_s_cok); 57617767Sralph UB_SST(ub_ok); 57713639Ssam } 57833559Srick InitialRole = MASTER; 57917767Sralph #ifdef TCPIP 58017767Sralph /* 58117767Sralph * Determine if we are on TCPIP 58217767Sralph */ 58325703Sbloom if (isatty(Ifn) == 0) { 58417767Sralph IsTcpIp = 1; 58517767Sralph DEBUG(4, "TCPIP connection -- ioctl-s disabled\n", CNULL); 58623590Sbloom } else 58723590Sbloom IsTcpIp = 0; 58817767Sralph #endif 58917767Sralph 59013639Ssam if (setjmp(Sjbuf)) 59113639Ssam goto next; 59213639Ssam signal(SIGALRM, timeout); 59333559Srick alarm(IsTcpIp?MAXMSGTIME*4:MAXMSGTIME*2); 59413639Ssam for (;;) { 59513639Ssam ret = imsg(msg, Ifn); 59633559Srick if (ret != SUCCESS) { 59713639Ssam alarm(0); 59833559Srick DEBUG(4,"\nimsg failed: errno %d\n", errno); 59917767Sralph logent("imsg 1", _FAILED); 60017767Sralph goto Failure; 60113639Ssam } 60213639Ssam if (msg[0] == 'S') 60313639Ssam break; 60413639Ssam } 60533559Srick alarm(IsTcpIp?MAXMSGTIME*4:MAXMSGTIME); 60617767Sralph #ifdef GNXSEQ 60713639Ssam seq = gnxseq(Rmtname); 60817767Sralph #else !GNXSEQ 60917767Sralph seq = 0; 61017767Sralph #endif !GNXSEQ 61118616Sralph if (MaxGrade != '\177') { 61223590Sbloom DEBUG(2, "Max Grade this transfer is %c\n", MaxGrade); 61325963Sbloom sprintf(msg, "%s -Q%d -p%c -vgrade=%c %s", 61425963Sbloom Myname, seq, MaxGrade, MaxGrade, rflags); 61525963Sbloom } else 61625963Sbloom sprintf(msg, "%s -Q%d %s", Myname, seq, rflags); 61713639Ssam omsg('S', msg, Ofn); 61813639Ssam for (;;) { 61913639Ssam ret = imsg(msg, Ifn); 62013639Ssam DEBUG(4, "msg-%s\n", msg); 62117767Sralph if (ret != SUCCESS) { 62213639Ssam alarm(0); 62317767Sralph #ifdef GNXSEQ 62413639Ssam ulkseq(); 62517767Sralph #endif GNXSEQ 62617767Sralph logent("imsg 2", _FAILED); 62717767Sralph goto Failure; 62813639Ssam } 62913639Ssam if (msg[0] == 'R') 63013639Ssam break; 63113639Ssam } 63213639Ssam alarm(0); 63313639Ssam if (msg[1] == 'B') { 63413639Ssam /* bad sequence */ 63523590Sbloom logent("BAD SEQ", "FAILED HANDSHAKE"); 63617767Sralph US_SST(us_s_hand); 63717767Sralph systat(Rmtname, SS_BADSEQ, Stattext[SS_BADSEQ]); 63817767Sralph #ifdef GNXSEQ 63913639Ssam ulkseq(); 64017767Sralph #endif GNXSEQ 64113639Ssam goto next; 64213639Ssam } 64313639Ssam if (strcmp(&msg[1], "OK") != SAME) { 64423590Sbloom logent(&msg[1], "FAILED HANDSHAKE"); 64517767Sralph US_SST(us_s_hand); 64617767Sralph #ifdef GNXSEQ 64713639Ssam ulkseq(); 64817767Sralph #endif GNXSEQ 64917767Sralph systat(Rmtname, SS_INPROGRESS, 65017767Sralph strcmp(&msg[1], "CB") == SAME? 65123590Sbloom "AWAITING CALLBACK": "FAILED HANDSHAKE"); 65213639Ssam goto next; 65313639Ssam } 65417767Sralph #ifdef GNXSEQ 65513639Ssam cmtseq(); 65617767Sralph #endif GNXSEQ 65713639Ssam } 65817767Sralph DEBUG(1, "Rmtname %s, ", Rmtname); 65913639Ssam DEBUG(1, "Role %s, ", Role ? "MASTER" : "SLAVE"); 66013639Ssam DEBUG(1, "Ifn - %d, ", Ifn); 66113639Ssam DEBUG(1, "Loginuser - %s\n", Loginuser); 66234164Srick setproctitle("%s: %s", Rmtname, Role ? "MASTER" : "SLAVE"); 66313639Ssam 66425703Sbloom ttyn = ttyname(Ifn); 66525703Sbloom 66633559Srick alarm(IsTcpIp?MAXMSGTIME*4:MAXMSGTIME); 66718616Sralph if (ret=setjmp(Sjbuf)) 66813639Ssam goto Failure; 66913639Ssam ret = startup(Role); 67013639Ssam alarm(0); 67113639Ssam if (ret != SUCCESS) { 67234164Srick logent("(startup)", _FAILED); 67313639Ssam Failure: 67417767Sralph US_SST(us_s_start); 67518616Sralph systat(Rmtname, SS_FAIL, ret > 0 ? "CONVERSATION FAILED" : 67618616Sralph "STARTUP FAILED"); 67713639Ssam goto next; 67817767Sralph } else { 67933559Srick char smsg[BUFSIZ], gmsg[10], pmsg[20], bpsmsg[20]; 68033559Srick extern char UsingProtocol; 68133559Srick extern int linebaudrate; 68233559Srick if (ttyn != NULL) 68333559Srick sprintf(bpsmsg, " %s %d bps", &ttyn[5], linebaudrate); 68433559Srick else 68533559Srick bpsmsg[0] = '\0'; 68633559Srick if (UsingProtocol != 'g') 68733559Srick sprintf(pmsg, " %c protocol", UsingProtocol); 68833559Srick else 68933559Srick pmsg[0] = '\0'; 69033559Srick if (MaxGrade != '\177') 69133559Srick sprintf(gmsg, " grade %c", MaxGrade); 69233559Srick else 69333559Srick gmsg[0] = '\0'; 69434164Srick sprintf(smsg, "(startup%s%s%s)", bpsmsg, pmsg, gmsg); 69533559Srick logent(smsg, "OK"); 69617767Sralph US_SST(us_s_gress); 69733559Srick StartTime = Now.time; 69813639Ssam systat(Rmtname, SS_INPROGRESS, "TALKING"); 69913639Ssam ret = cntrl(Role, wkpre); 70013639Ssam DEBUG(1, "cntrl - %d\n", ret); 70113639Ssam signal(SIGINT, SIG_IGN); 70213639Ssam signal(SIGHUP, SIG_IGN); 70313639Ssam signal(SIGALRM, timeout); 70434164Srick sprintf(smsg, "(conversation complete %ld sent %ld received)", 70533559Srick Bytes_Sent, Bytes_Received); 70617767Sralph if (ret == SUCCESS) { 70733559Srick logent(smsg, "OK"); 70817767Sralph US_SST(us_s_ok); 70913639Ssam rmstat(Rmtname); 71013639Ssam 71117767Sralph } else { 71233559Srick logent(smsg, _FAILED); 71317767Sralph US_SST(us_s_cf); 71417767Sralph systat(Rmtname, SS_FAIL, "CONVERSATION FAILED"); 71513639Ssam } 71633559Srick alarm(IsTcpIp?MAXMSGTIME*4:MAXMSGTIME); 71713639Ssam DEBUG(4, "send OO %d,", ret); 71813639Ssam if (!setjmp(Sjbuf)) { 71913639Ssam for (;;) { 72013639Ssam omsg('O', "OOOOO", Ofn); 72113639Ssam ret = imsg(msg, Ifn); 72213639Ssam if (ret != 0) 72313639Ssam break; 72413639Ssam if (msg[0] == 'O') 72513639Ssam break; 72613639Ssam } 72713639Ssam } 72813639Ssam alarm(0); 72917767Sralph clsacu(); 73017767Sralph rmlock(CNULL); 73133559Srick 73213639Ssam } 73313639Ssam next: 73413639Ssam if (!onesys) { 73513639Ssam goto loop; 73613639Ssam } 73713639Ssam cleanup(0); 73813639Ssam } 73913639Ssam 74017767Sralph #ifndef USG 74113639Ssam struct sgttyb Hupvec; 74213639Ssam #endif 74313639Ssam 74425703Sbloom /* 74525703Sbloom * cleanup and exit with "code" status 74613639Ssam */ 74713639Ssam cleanup(code) 74813639Ssam register int code; 74913639Ssam { 75013639Ssam signal(SIGINT, SIG_IGN); 75113639Ssam signal(SIGHUP, SIG_IGN); 75213639Ssam rmlock(CNULL); 75325703Sbloom sleep(5); /* Wait for any pending output */ 75413639Ssam clsacu(); 75513639Ssam logcls(); 75613639Ssam if (Role == SLAVE) { 75717767Sralph if (!IsTcpIp) { 75817767Sralph #ifdef USG 75913639Ssam Savettyb.c_cflag |= HUPCL; 76023590Sbloom (void) ioctl(0, TCSETA, &Savettyb); 76117767Sralph #else !USG 76223590Sbloom (void) ioctl(0, TIOCHPCL, STBNULL); 76318616Sralph #ifdef TIOCSDTR 76423590Sbloom (void) ioctl(0, TIOCCDTR, STBNULL); 76518616Sralph sleep(2); 76623590Sbloom (void) ioctl(0, TIOCSDTR, STBNULL); 76718616Sralph #else !TIOCSDTR 76823590Sbloom (void) ioctl(0, TIOCGETP, &Hupvec); 76913639Ssam Hupvec.sg_ispeed = B0; 77013639Ssam Hupvec.sg_ospeed = B0; 77123590Sbloom (void) ioctl(0, TIOCSETP, &Hupvec); 77225703Sbloom #endif !TIOCSDTR 77313639Ssam sleep(2); 77423590Sbloom (void) ioctl(0, TIOCSETP, &Savettyb); 77517767Sralph /* make *sure* exclusive access is off */ 77623590Sbloom (void) ioctl(0, TIOCNXCL, STBNULL); 77717767Sralph #endif !USG 77813639Ssam } 77913639Ssam if (ttyn != NULL) 78013639Ssam chmod(ttyn, 0600); 78113639Ssam } 78213639Ssam if (Ofn != -1) { 78313639Ssam if (Role == MASTER) 78413639Ssam write(Ofn, EOTMSG, strlen(EOTMSG)); 78513639Ssam close(Ifn); 78613639Ssam close(Ofn); 78713639Ssam } 78818616Sralph #ifdef DIALINOUT 78918616Sralph /* reenable logins on dialout */ 79018616Sralph reenable(); 79118616Sralph #endif DIALINOUT 79213639Ssam if (code == 0) 79313639Ssam xuuxqt(); 79417767Sralph else 79517767Sralph DEBUG(1, "exit code %d\n", code); 79625703Sbloom setdebug (DBG_CLEAN); 79733559Srick do_connect_accounting(); 79813639Ssam exit(code); 79913639Ssam } 80013639Ssam 80133559Srick do_connect_accounting() 80233559Srick { 80333945Srick #ifdef DO_CONNECT_ACCOUNTING 80433559Srick register FILE *fp; 80533559Srick struct tm *localtime(); 80633559Srick register struct tm *tm; 80733559Srick int flags; 80833559Srick 80933559Srick if (StartTime == 0) 81033559Srick return; 81133559Srick 81233945Srick fp = fopen(DO_CONNECT_ACCOUNTING, "a"); 81333945Srick if (fp == NULL) { 81433945Srick syslog(LOG_ALERT, "fopen(%s) failed: %m",DO_CONNECT_ACCOUNTING); 81533945Srick cleanup(FAIL); 81633945Srick } 81733559Srick 81833559Srick tm = localtime(&StartTime); 81933559Srick #ifdef F_SETFL 82033559Srick flags = fcntl(fileno(fp), F_GETFL, 0); 82133559Srick fcntl(fileno(fp), F_SETFL, flags|O_APPEND); 82233559Srick #endif 82333559Srick #ifdef USG 82433559Srick fprintf(fp,"%s %d %d%.2d%.2d %.2d%.2d %d %ld %s %ld %ld\n", 82533559Srick #else /* V7 */ 82633559Srick fprintf(fp,"%s %d %d%02d%02d %02d%02d %d %ld %s %ld %ld\n", 82733559Srick #endif /* V7 */ 82833559Srick Rmtname, InitialRole, tm->tm_year, tm->tm_mon + 1, 82933559Srick tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_wday, 83033559Srick (Now.time - StartTime + 59) / 60, 83133559Srick ttyn == NULL ? "ttyp0" : &ttyn[5], 83233559Srick Bytes_Sent, Bytes_Received); 83333559Srick fclose(fp); 83433559Srick #endif /* DO_CONNECT_ACCOUNTING */ 83533559Srick } 83633559Srick 83725703Sbloom /* 83825703Sbloom * on interrupt - remove locks and exit 83913639Ssam */ 84013639Ssam 84146879Sbostic static void 84213639Ssam onintr(inter) 84313639Ssam register int inter; 84413639Ssam { 84533559Srick char str[BUFSIZ]; 84613639Ssam signal(inter, SIG_IGN); 84734164Srick sprintf(str, "(SIGNAL %d)", inter); 84813639Ssam logent(str, "CAUGHT"); 84917767Sralph US_SST(us_s_intr); 85023590Sbloom if (*Rmtname && strncmp(Rmtname, Myname, MAXBASENAME)) 85117767Sralph systat(Rmtname, SS_FAIL, str); 85234164Srick sprintf(str, "(conversation complete %ld sent %ld received)", 85333559Srick Bytes_Sent, Bytes_Received); 85433559Srick logent(str, _FAILED); 85517767Sralph if (inter == SIGPIPE && !onesys) 85617767Sralph longjmp(Pipebuf, 1); 85713639Ssam cleanup(inter); 85813639Ssam } 85913639Ssam 86013639Ssam /* 86113639Ssam * Catch a special signal 86236449Smarc * (SIGUSR1), and toggle debugging between 0 and 30. 86313639Ssam * Handy for looking in on long running uucicos. 86413639Ssam */ 86546879Sbostic static void 86625703Sbloom dbg_signal() 86713639Ssam { 86825703Sbloom Debug = (Debug == 0) ? 30 : 0; 86925703Sbloom setdebug(DBG_PERM); 87025703Sbloom if (Debug > 0) 87125703Sbloom logent("Signal Enabled", "DEBUG"); 87225703Sbloom } 87317767Sralph 87425703Sbloom 87525703Sbloom /* 87625703Sbloom * Check debugging requests, and open RMTDEBUG audit file if necessary. If an 87725703Sbloom * audit file is needed, the parm argument indicates how to create the file: 87825703Sbloom * 87925703Sbloom * DBG_TEMP - Open a temporary file, with filename = RMTDEBUG/pid. 88025703Sbloom * DBG_PERM - Open a permanent audit file, filename = RMTDEBUG/Rmtname. 88125703Sbloom * If a temp file already exists, it is mv'ed to be permanent. 88225703Sbloom * DBG_CLEAN - Cleanup; unlink temp files. 88325703Sbloom * 88425703Sbloom * Restrictions - this code can only cope with one open debug file at a time. 88525703Sbloom * Each call creates a new file; if an old one of the same name exists it will 88625703Sbloom * be overwritten. 88725703Sbloom */ 88825703Sbloom setdebug(parm) 88925703Sbloom int parm; 89025703Sbloom { 89125703Sbloom char buf[BUFSIZ]; /* Buffer for building filenames */ 89225703Sbloom static char *temp = NULL; /* Ptr to temporary file name */ 89325703Sbloom static int auditopen = 0; /* Set to 1 when we open a file */ 89425703Sbloom struct stat stbuf; /* File status buffer */ 89525703Sbloom 89625703Sbloom /* 89725703Sbloom * If movement or cleanup of a temp file is indicated, we do it no 89825703Sbloom * matter what. 89925703Sbloom */ 90025703Sbloom if (temp != CNULL && parm == DBG_PERM) { 90125703Sbloom sprintf(buf, "%s/%s", RMTDEBUG, Rmtname); 90225703Sbloom unlink(buf); 90325703Sbloom if (link(temp, buf) != 0) { 90423590Sbloom Debug = 0; 90533945Srick syslog(LOG_ERR, "RMTDEBUG link(%s,%s) failed: %m", 90633945Srick temp, buf); 90733945Srick cleanup(FAIL); 90825703Sbloom } 90925703Sbloom parm = DBG_CLEAN; 91023590Sbloom } 91125703Sbloom if (parm == DBG_CLEAN) { 91225703Sbloom if (temp != CNULL) { 91325703Sbloom unlink(temp); 91425703Sbloom free(temp); 91525703Sbloom temp = CNULL; 91625703Sbloom } 91725703Sbloom return; 91825703Sbloom } 91925703Sbloom 92025703Sbloom if (Debug == 0) 92125703Sbloom return; /* Gotta be in debug to come here. */ 92225703Sbloom 92325703Sbloom /* 92425703Sbloom * If we haven't opened a file already, we can just return if it's 92525703Sbloom * alright to use the stderr we came in with. We can if: 92625703Sbloom * 92725703Sbloom * Role == MASTER, and Stderr is a regular file, a TTY or a pipe. 92825703Sbloom * 92925703Sbloom * Caution: Detecting when stderr is a pipe is tricky, because the 4.2 93025703Sbloom * man page for fstat(2) disagrees with reality, and System V leaves it 93125703Sbloom * undefined, which means different implementations act differently. 93225703Sbloom */ 93325703Sbloom if (!auditopen && Role == MASTER) { 93425703Sbloom if (isatty(fileno(stderr))) 93525703Sbloom return; 93625703Sbloom else if (fstat(fileno(stderr), &stbuf) == 0) { 93725703Sbloom #ifdef USG 93825703Sbloom /* Is Regular File or Fifo */ 93925703Sbloom if ((stbuf.st_mode & 0060000) == 0) 94025703Sbloom return; 94125703Sbloom #else !USG 94217767Sralph #ifdef BSD4_2 94325703Sbloom /* Is Regular File */ 94425703Sbloom if ((stbuf.st_mode & S_IFMT) == S_IFREG || 94525703Sbloom stbuf.st_mode == 0) /* Is a pipe */ 94625703Sbloom return; 94725703Sbloom #else !BSD4_2 94825703Sbloom /* Is Regular File or Pipe */ 94925703Sbloom if ((stbuf.st_mode & S_IFMT) == S_IFREG) 95025703Sbloom return; 95125703Sbloom #endif BSD4_2 95225703Sbloom #endif USG 95325703Sbloom } 95417767Sralph } 95513639Ssam 95625703Sbloom /* 95725703Sbloom * We need RMTDEBUG directory to do auditing. If the file doesn't exist, 95825703Sbloom * then we forget about debugging; if it exists but has improper owner- 95925703Sbloom * ship or modes, we gripe about it in ERRLOG. 96025703Sbloom */ 96125703Sbloom if (stat(RMTDEBUG, &stbuf) != SUCCESS) { 96225703Sbloom Debug = 0; 96325703Sbloom return; 96425703Sbloom } 96525703Sbloom if ((geteuid() != stbuf.st_uid) || /* We must own it */ 96625703Sbloom ((stbuf.st_mode & 0170700) != 040700)) { /* Directory, rwx */ 96725703Sbloom Debug = 0; 96833945Srick syslog(LOG_ERR, "%s: invalid directory mode: %o", RMTDEBUG, 96933945Srick stbuf.st_mode); 97025703Sbloom return; 97125703Sbloom } 97213639Ssam 97325703Sbloom if (parm == DBG_TEMP) { 97425703Sbloom sprintf(buf, "%s/%d", RMTDEBUG, getpid()); 97525703Sbloom temp = malloc(strlen (buf) + 1); 97625703Sbloom if (temp == CNULL) { 97725703Sbloom Debug = 0; 97833945Srick syslog(LOG_ERR, "RMTDEBUG malloc failed: %m"); 97933945Srick cleanup(FAIL); 98025703Sbloom } 98125703Sbloom strcpy(temp, buf); 98225703Sbloom } else 98325703Sbloom sprintf(buf, "%s/%s", RMTDEBUG, Rmtname); 98413639Ssam 98525703Sbloom unlink(buf); 98625703Sbloom if (freopen(buf, "w", stderr) != stderr) { 98725703Sbloom Debug = 0; 98833945Srick syslog(LOG_ERR, "RMTDEBUG freopen(%s) failed: %m", buf); 98933945Srick cleanup(FAIL); 99025703Sbloom } 99125703Sbloom setbuf(stderr, CNULL); 99225703Sbloom auditopen = 1; 99313639Ssam } 99413639Ssam 99523590Sbloom /* 99625703Sbloom * catch SIGALRM routine 99713639Ssam */ 99846879Sbostic static void 99913639Ssam timeout() 100013639Ssam { 100123590Sbloom extern int HaveSentHup; 100223590Sbloom if (!HaveSentHup) { 100323590Sbloom logent(Rmtname, "TIMEOUT"); 100423590Sbloom if (*Rmtname && strncmp(Rmtname, Myname, MAXBASENAME)) { 100523590Sbloom US_SST(us_s_tmot); 100623590Sbloom systat(Rmtname, SS_FAIL, "TIMEOUT"); 100723590Sbloom } 100817767Sralph } 100913639Ssam longjmp(Sjbuf, 1); 101013639Ssam } 101113639Ssam 101213639Ssam static char * 101313639Ssam pskip(p) 101413639Ssam register char *p; 101513639Ssam { 101617767Sralph while(*p && *p != ' ') 101713639Ssam ++p; 101823590Sbloom while(*p && *p == ' ') 101917767Sralph *p++ = 0; 102017767Sralph return p; 102113639Ssam } 102234164Srick 102334164Srick /* 102434164Srick * clobber argv so ps will show what we're doing. 102534164Srick * stolen from sendmail 102634164Srick */ 102734164Srick /*VARARGS1*/ 102834164Srick setproctitle(fmt, a, b, c) 102934164Srick char *fmt; 103034164Srick { 103134164Srick #ifdef SETPROCTITLE 103234164Srick register char *p; 103334164Srick register int i; 103434164Srick extern char **Argv; 103534164Srick extern char *LastArgv; 103634164Srick char buf[BUFSIZ]; 103734164Srick 103834164Srick (void) sprintf(buf, fmt, a, b, c); 103934164Srick 104034164Srick /* make ps print "(sendmail)" */ 104134164Srick p = Argv[0]; 104234164Srick *p++ = '-'; 104334164Srick 104434164Srick i = strlen(buf); 104534164Srick if (i > LastArgv - p - 2) { 104634164Srick i = LastArgv - p - 2; 104734164Srick buf[i] = '\0'; 104834164Srick } 104934164Srick (void) strcpy(p, buf); 105034164Srick p += i; 105134164Srick while (p < LastArgv) 105234164Srick *p++ = ' '; 105334164Srick #endif SETPROCTITLE 105434164Srick } 1055