Index: head/libexec/atrun/atrun.c =================================================================== --- head/libexec/atrun/atrun.c (revision 131989) +++ head/libexec/atrun/atrun.c (revision 131990) @@ -1,502 +1,502 @@ /* * atrun.c - run jobs queued by at; run with root privileges. * Copyright (C) 1993, 1994 Thomas Koenig * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author(s) may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ /* System Headers */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ #include #else #include #endif #if (MAXLOGNAME-1) > UT_NAMESIZE #define LOGNAMESIZE UT_NAMESIZE #else #define LOGNAMESIZE (MAXLOGNAME-1) #endif /* Local headers */ #include "gloadavg.h" #define MAIN #include "privs.h" /* Macros */ #ifndef ATJOB_DIR #define ATJOB_DIR "/usr/spool/atjobs/" #endif #ifndef ATSPOOL_DIR #define ATSPOOL_DIR "/usr/spool/atspool/" #endif #ifndef LOADAVG_MX #define LOADAVG_MX 1.5 #endif /* File scope variables */ -static debug = 0; +static int debug = 0; void perr(const char *a); static void usage(void); /* Local functions */ static int write_string(int fd, const char* a) { return write(fd, a, strlen(a)); } #undef DEBUG_FORK #ifdef DEBUG_FORK static pid_t myfork(void) { pid_t res; res = fork(); if (res == 0) kill(getpid(),SIGSTOP); return res; } #define fork myfork #endif static void run_file(const char *filename, uid_t uid, gid_t gid) { /* Run a file by spawning off a process which redirects I/O, * spawns a subshell, then waits for it to complete and sends * mail to the user. */ pid_t pid; int fd_out, fd_in; int queue; char mailbuf[LOGNAMESIZE + 1], fmt[49]; char *mailname = NULL; FILE *stream; int send_mail = 0; struct stat buf, lbuf; off_t size; struct passwd *pentry; int fflags; long nuid; long ngid; PRIV_START if (chmod(filename, S_IRUSR) != 0) { perr("cannot change file permissions"); } PRIV_END pid = fork(); if (pid == -1) perr("cannot fork"); else if (pid != 0) return; /* Let's see who we mail to. Hopefully, we can read it from * the command file; if not, send it to the owner, or, failing that, * to root. */ pentry = getpwuid(uid); if (pentry == NULL) { syslog(LOG_ERR,"Userid %lu not found - aborting job %s", (unsigned long) uid, filename); exit(EXIT_FAILURE); } PRIV_START stream=fopen(filename, "r"); PRIV_END #ifdef __FreeBSD__ if (pentry->pw_expire && time(NULL) >= pentry->pw_expire) { syslog(LOG_ERR, "Userid %lu is expired - aborting job %s", (unsigned long) uid, filename); exit(EXIT_FAILURE); } #endif if (stream == NULL) perr("cannot open input file"); if ((fd_in = dup(fileno(stream))) <0) perr("error duplicating input file descriptor"); if (fstat(fd_in, &buf) == -1) perr("error in fstat of input file descriptor"); if (lstat(filename, &lbuf) == -1) perr("error in fstat of input file"); if (S_ISLNK(lbuf.st_mode)) { syslog(LOG_ERR,"Symbolic link encountered in job %s - aborting", filename); exit(EXIT_FAILURE); } if ((lbuf.st_dev != buf.st_dev) || (lbuf.st_ino != buf.st_ino) || (lbuf.st_uid != buf.st_uid) || (lbuf.st_gid != buf.st_gid) || (lbuf.st_size!=buf.st_size)) { syslog(LOG_ERR,"Somebody changed files from under us for job %s - " "aborting",filename); exit(EXIT_FAILURE); } if (buf.st_nlink > 1) { syslog(LOG_ERR,"Someboy is trying to run a linked script for job %s", filename); exit(EXIT_FAILURE); } if ((fflags = fcntl(fd_in, F_GETFD)) <0) perr("error in fcntl"); fcntl(fd_in, F_SETFD, fflags & ~FD_CLOEXEC); snprintf(fmt, sizeof(fmt), "#!/bin/sh\n# atrun uid=%%ld gid=%%ld\n# mail %%%ds %%d", LOGNAMESIZE); if (fscanf(stream, fmt, &nuid, &ngid, mailbuf, &send_mail) != 4) { syslog(LOG_ERR,"File %s is in wrong format - aborting", filename); exit(EXIT_FAILURE); } if (mailbuf[0] == '-') { syslog(LOG_ERR,"illegal mail name %s in %s",mailbuf,filename); exit(EXIT_FAILURE); } mailname = mailbuf; if (nuid != uid) { syslog(LOG_ERR,"Job %s - userid %ld does not match file uid %lu", filename, nuid, (unsigned long)uid); exit(EXIT_FAILURE); } if (ngid != gid) { syslog(LOG_ERR,"Job %s - groupid %ld does not match file gid %lu", filename, ngid, (unsigned long)gid); exit(EXIT_FAILURE); } fclose(stream); if (chdir(ATSPOOL_DIR) < 0) perr("cannot chdir to " ATSPOOL_DIR); /* Create a file to hold the output of the job we are about to run. * Write the mail header. */ if((fd_out=open(filename, O_WRONLY | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR)) < 0) perr("cannot create output file"); write_string(fd_out, "Subject: Output from your job "); write_string(fd_out, filename); write_string(fd_out, "\n\n"); fstat(fd_out, &buf); size = buf.st_size; close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); pid = fork(); if (pid < 0) perr("error in fork"); else if (pid == 0) { char *nul = NULL; char **nenvp = &nul; /* Set up things for the child; we want standard input from the input file, * and standard output and error sent to our output file. */ if (lseek(fd_in, (off_t) 0, SEEK_SET) < 0) perr("error in lseek"); if (dup(fd_in) != STDIN_FILENO) perr("error in I/O redirection"); if (dup(fd_out) != STDOUT_FILENO) perr("error in I/O redirection"); if (dup(fd_out) != STDERR_FILENO) perr("error in I/O redirection"); close(fd_in); close(fd_out); if (chdir(ATJOB_DIR) < 0) perr("cannot chdir to " ATJOB_DIR); queue = *filename; PRIV_START nice(tolower(queue) - 'a'); if (initgroups(pentry->pw_name,pentry->pw_gid)) perr("cannot delete saved userids"); if (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0) perr("cannot change group"); if (setlogin(pentry->pw_name)) perr("cannot set login name"); if (setuid(uid) < 0 || seteuid(uid) < 0) perr("cannot set user id"); if (chdir(pentry->pw_dir)) chdir("/"); if(execle("/bin/sh","sh",(char *) NULL, nenvp) != 0) perr("exec failed for /bin/sh"); PRIV_END } /* We're the parent. Let's wait. */ close(fd_in); close(fd_out); waitpid(pid, (int *) NULL, 0); /* Send mail. Unlink the output file first, so it is deleted after * the run. */ stat(filename, &buf); if (open(filename, O_RDONLY) != STDIN_FILENO) perr("open of jobfile failed"); unlink(filename); if ((buf.st_size != size) || send_mail) { PRIV_START if (initgroups(pentry->pw_name,pentry->pw_gid)) perr("cannot delete saved userids"); if (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0) perr("cannot change group"); if (setlogin(pentry->pw_name)) perr("cannot set login name"); if (setuid(uid) < 0 || seteuid(uid) < 0) perr("cannot set user id"); if (chdir(pentry->pw_dir)) chdir("/"); #ifdef __FreeBSD__ execl(_PATH_SENDMAIL, "sendmail", "-F", "Atrun Service", "-odi", "-oem", mailname, (char *) NULL); #else execl(MAIL_CMD, MAIL_CMD, mailname, (char *) NULL); #endif perr("exec failed for mail command"); PRIV_END } exit(EXIT_SUCCESS); } /* Global functions */ /* Needed in gloadavg.c */ void perr(const char *a) { if (debug) { warn("%s", a); } else syslog(LOG_ERR, "%s: %m", a); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { /* Browse through ATJOB_DIR, checking all the jobfiles wether they should * be executed and or deleted. The queue is coded into the first byte of * the job filename, the date (in minutes since Eon) as a hex number in the * following eight bytes, followed by a dot and a serial number. A file * which has not been executed yet is denoted by its execute - bit set. * For those files which are to be executed, run_file() is called, which forks * off a child which takes care of I/O redirection, forks off another child * for execution and yet another one, optionally, for sending mail. * Files which already have run are removed during the next invocation. */ DIR *spool; struct dirent *dirent; struct stat buf; unsigned long ctm; unsigned long jobno; char queue; time_t now, run_time; char batch_name[] = "Z2345678901234"; uid_t batch_uid; gid_t batch_gid; int c; int run_batch; double load_avg = LOADAVG_MX; /* We don't need root privileges all the time; running under uid and gid daemon * is fine. */ RELINQUISH_PRIVS_ROOT(DAEMON_UID, DAEMON_GID) openlog("atrun", LOG_PID, LOG_CRON); opterr = 0; while((c=getopt(argc, argv, "dl:"))!= -1) { switch (c) { case 'l': if (sscanf(optarg, "%lf", &load_avg) != 1) perr("garbled option -l"); if (load_avg <= 0.) load_avg = LOADAVG_MX; break; case 'd': debug ++; break; case '?': default: usage(); } } if (chdir(ATJOB_DIR) != 0) perr("cannot change to " ATJOB_DIR); /* Main loop. Open spool directory for reading and look over all the * files in there. If the filename indicates that the job should be run * and the x bit is set, fork off a child which sets its user and group * id to that of the files and exec a /bin/sh which executes the shell * script. Unlink older files if they should no longer be run. For * deletion, their r bit has to be turned on. * * Also, pick the oldest batch job to run, at most one per invocation of * atrun. */ if ((spool = opendir(".")) == NULL) perr("cannot read " ATJOB_DIR); now = time(NULL); run_batch = 0; batch_uid = (uid_t) -1; batch_gid = (gid_t) -1; while ((dirent = readdir(spool)) != NULL) { if (stat(dirent->d_name,&buf) != 0) perr("cannot stat in " ATJOB_DIR); /* We don't want directories */ if (!S_ISREG(buf.st_mode)) continue; if (sscanf(dirent->d_name,"%c%5lx%8lx",&queue,&jobno,&ctm) != 3) continue; run_time = (time_t) ctm*60; if ((S_IXUSR & buf.st_mode) && (run_time <=now)) { if (isupper(queue) && (strcmp(batch_name,dirent->d_name) > 0)) { run_batch = 1; strncpy(batch_name, dirent->d_name, sizeof(batch_name)); batch_uid = buf.st_uid; batch_gid = buf.st_gid; } /* The file is executable and old enough */ if (islower(queue)) run_file(dirent->d_name, buf.st_uid, buf.st_gid); } /* Delete older files */ if ((run_time < now) && !(S_IXUSR & buf.st_mode) && (S_IRUSR & buf.st_mode)) unlink(dirent->d_name); } /* run the single batch file, if any */ if (run_batch && (gloadavg() < load_avg)) run_file(batch_name, batch_uid, batch_gid); closelog(); exit(EXIT_SUCCESS); } static void usage(void) { if (debug) fprintf(stderr, "usage: atrun [-l load_avg] [-d]\n"); else syslog(LOG_ERR, "usage: atrun [-l load_avg] [-d]"); exit(EXIT_FAILURE); } Index: head/usr.bin/gprof/gprof.c =================================================================== --- head/usr.bin/gprof/gprof.c (revision 131989) +++ head/usr.bin/gprof/gprof.c (revision 131990) @@ -1,610 +1,610 @@ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #if 0 #ifndef lint static char sccsid[] = "@(#)gprof.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include "gprof.h" static int valcmp(const void *, const void *); static struct gmonhdr gmonhdr; static int lflag; static int Lflag; int main(argc, argv) int argc; char **argv; { char **sp; nltype **timesortnlp; char **defaultEs; --argc; argv++; debug = 0; bflag = TRUE; while ( *argv != 0 && **argv == '-' ) { (*argv)++; switch ( **argv ) { case 'a': aflag = TRUE; break; case 'b': bflag = FALSE; break; case 'C': Cflag = TRUE; cyclethreshold = atoi( *++argv ); break; case 'd': dflag = TRUE; setlinebuf(stdout); debug |= atoi( *++argv ); debug |= ANYDEBUG; # ifdef DEBUG printf("[main] debug = %d\n", debug); # else /* not DEBUG */ printf("gprof: -d ignored\n"); # endif /* DEBUG */ break; case 'E': ++argv; addlist( Elist , *argv ); Eflag = TRUE; addlist( elist , *argv ); eflag = TRUE; break; case 'e': addlist( elist , *++argv ); eflag = TRUE; break; case 'F': ++argv; addlist( Flist , *argv ); Fflag = TRUE; addlist( flist , *argv ); fflag = TRUE; break; case 'f': addlist( flist , *++argv ); fflag = TRUE; break; case 'k': addlist( kfromlist , *++argv ); addlist( ktolist , *++argv ); kflag = TRUE; break; case 'K': Kflag = TRUE; break; case 'l': lflag = 1; Lflag = 0; break; case 'L': Lflag = 1; lflag = 0; break; case 's': sflag = TRUE; break; case 'u': uflag = TRUE; break; case 'z': zflag = TRUE; break; } argv++; } if ( *argv != 0 ) { a_outname = *argv; argv++; } else { a_outname = A_OUTNAME; } if ( *argv != 0 ) { gmonname = *argv; argv++; } else { gmonname = (char *) malloc(strlen(a_outname)+6); strcpy(gmonname, a_outname); strcat(gmonname, ".gmon"); } /* * get information from the executable file. */ if ((Kflag && kernel_getnfile(a_outname, &defaultEs) == -1) || (elf_getnfile(a_outname, &defaultEs) == -1 && aout_getnfile(a_outname, &defaultEs) == -1)) errx(1, "%s: bad format", a_outname); /* * sort symbol table. */ qsort(nl, nname, sizeof(nltype), valcmp); /* * turn off default functions */ for ( sp = defaultEs ; *sp ; sp++ ) { Eflag = TRUE; addlist( Elist , *sp ); eflag = TRUE; addlist( elist , *sp ); } /* * get information about mon.out file(s). */ do { getpfile( gmonname ); if ( *argv != 0 ) { gmonname = *argv; } } while ( *argv++ != 0 ); /* * how many ticks per second? * if we can't tell, report time in ticks. */ if (hz == 0) { hz = 1; fprintf(stderr, "time is in ticks, not seconds\n"); } /* * dump out a gmon.sum file if requested */ if ( sflag ) { dumpsum( GMONSUM ); } /* * assign samples to procedures */ asgnsamples(); /* * assemble the dynamic profile */ timesortnlp = doarcs(); /* * print the dynamic profile */ if(!lflag) { printgprof( timesortnlp ); } /* * print the flat profile */ if(!Lflag) { printprof(); } /* * print the index */ printindex(); exit(0); } /* * information from a gmon.out file is in two parts: * an array of sampling hits within pc ranges, * and the arcs. */ void getpfile(filename) char *filename; { FILE *pfile; FILE *openpfile(); struct rawarc arc; pfile = openpfile(filename); readsamples(pfile); /* * the rest of the file consists of * a bunch of tuples. */ while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) { # ifdef DEBUG if ( debug & SAMPLEDEBUG ) { printf( "[getpfile] frompc 0x%lx selfpc 0x%lx count %ld\n" , arc.raw_frompc , arc.raw_selfpc , arc.raw_count ); } # endif /* DEBUG */ /* * add this arc */ tally( &arc ); } fclose(pfile); } FILE * openpfile(filename) char *filename; { struct gmonhdr tmp; FILE *pfile; int size; int rate; if((pfile = fopen(filename, "r")) == NULL) err(1, "%s", filename); fread(&tmp, sizeof(struct gmonhdr), 1, pfile); if ( s_highpc != 0 && ( tmp.lpc != gmonhdr.lpc || tmp.hpc != gmonhdr.hpc || tmp.ncnt != gmonhdr.ncnt ) ) errx(1, "%s: incompatible with first gmon file", filename); gmonhdr = tmp; if ( gmonhdr.version == GMONVERSION ) { rate = gmonhdr.profrate; size = sizeof(struct gmonhdr); } else { fseek(pfile, sizeof(struct ophdr), SEEK_SET); size = sizeof(struct ophdr); gmonhdr.profrate = rate = hertz(); gmonhdr.version = GMONVERSION; } if (hz == 0) { hz = rate; } else if (hz != rate) errx(0, "%s: profile clock rate (%d) %s (%ld) in first gmon file", filename, rate, "incompatible with clock rate", hz); if ( gmonhdr.histcounter_type == 0 ) { /* Historical case. The type was u_short (2 bytes in practice). */ histcounter_type = 16; histcounter_size = 2; } else { histcounter_type = gmonhdr.histcounter_type; histcounter_size = abs(histcounter_type) / CHAR_BIT; } s_lowpc = (unsigned long) gmonhdr.lpc; s_highpc = (unsigned long) gmonhdr.hpc; lowpc = (unsigned long)gmonhdr.lpc / HISTORICAL_SCALE_2; highpc = (unsigned long)gmonhdr.hpc / HISTORICAL_SCALE_2; sampbytes = gmonhdr.ncnt - size; nsamples = sampbytes / histcounter_size; # ifdef DEBUG if ( debug & SAMPLEDEBUG ) { printf( "[openpfile] hdr.lpc 0x%lx hdr.hpc 0x%lx hdr.ncnt %d\n", gmonhdr.lpc , gmonhdr.hpc , gmonhdr.ncnt ); printf( "[openpfile] s_lowpc 0x%lx s_highpc 0x%lx\n" , s_lowpc , s_highpc ); printf( "[openpfile] lowpc 0x%lx highpc 0x%lx\n" , lowpc , highpc ); printf( "[openpfile] sampbytes %d nsamples %d\n" , sampbytes , nsamples ); printf( "[openpfile] sample rate %ld\n" , hz ); } # endif /* DEBUG */ return(pfile); } void tally( rawp ) struct rawarc *rawp; { nltype *parentp; nltype *childp; parentp = nllookup( rawp -> raw_frompc ); childp = nllookup( rawp -> raw_selfpc ); if ( parentp == 0 || childp == 0 ) return; if ( kflag && onlist( kfromlist , parentp -> name ) && onlist( ktolist , childp -> name ) ) { return; } childp -> ncall += rawp -> raw_count; # ifdef DEBUG if ( debug & TALLYDEBUG ) { printf( "[tally] arc from %s to %s traversed %ld times\n" , parentp -> name , childp -> name , rawp -> raw_count ); } # endif /* DEBUG */ addarc( parentp , childp , rawp -> raw_count ); } /* * dump out the gmon.sum file */ void dumpsum( sumfile ) char *sumfile; { register nltype *nlp; register arctype *arcp; struct rawarc arc; FILE *sfile; if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) err( 1 , "%s" , sumfile ); /* * dump the header; use the last header read in */ if ( fwrite( &gmonhdr , sizeof gmonhdr , 1 , sfile ) != 1 ) err( 1 , "%s" , sumfile ); /* * dump the samples */ if (fwrite(samples, histcounter_size, nsamples, sfile) != nsamples) err( 1 , "%s" , sumfile ); /* * dump the normalized raw arc information */ for ( nlp = nl ; nlp < npe ; nlp++ ) { for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) { arc.raw_frompc = arcp -> arc_parentp -> value; arc.raw_selfpc = arcp -> arc_childp -> value; arc.raw_count = arcp -> arc_count; if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 ) err( 1 , "%s" , sumfile ); # ifdef DEBUG if ( debug & SAMPLEDEBUG ) { printf( "[dumpsum] frompc 0x%lx selfpc 0x%lx count %ld\n" , arc.raw_frompc , arc.raw_selfpc , arc.raw_count ); } # endif /* DEBUG */ } } fclose( sfile ); } static int valcmp(v1, v2) const void *v1; const void *v2; { const nltype *p1 = (const nltype *)v1; const nltype *p2 = (const nltype *)v2; if ( p1 -> value < p2 -> value ) { return LESSTHAN; } if ( p1 -> value > p2 -> value ) { return GREATERTHAN; } return EQUALTO; } void readsamples(pfile) FILE *pfile; { - register i; + int i; intmax_t sample; if (samples == 0) { samples = (double *) calloc(nsamples, sizeof(double)); if (samples == 0) errx(0, "no room for %d sample pc's", nsamples); } for (i = 0; i < nsamples; i++) { fread(&sample, histcounter_size, 1, pfile); if (feof(pfile)) break; switch ( histcounter_type ) { case -8: samples[i] += *(int8_t *)&sample; break; case 8: samples[i] += *(u_int8_t *)&sample; break; case -16: samples[i] += *(int16_t *)&sample; break; case 16: samples[i] += *(u_int16_t *)&sample; break; case -32: samples[i] += *(int32_t *)&sample; break; case 32: samples[i] += *(u_int32_t *)&sample; break; case -64: samples[i] += *(int64_t *)&sample; break; case 64: samples[i] += *(u_int64_t *)&sample; break; default: err(1, "unsupported histogram counter type %d", histcounter_type); } } if (i != nsamples) errx(1, "unexpected EOF after reading %d/%d samples", --i , nsamples ); } /* * Assign samples to the procedures to which they belong. * * There are three cases as to where pcl and pch can be * with respect to the routine entry addresses svalue0 and svalue1 * as shown in the following diagram. overlap computes the * distance between the arrows, the fraction of the sample * that is to be credited to the routine which starts at svalue0. * * svalue0 svalue1 * | | * v v * * +-----------------------------------------------+ * | | * | ->| |<- ->| |<- ->| |<- | * | | | | | | * +---------+ +---------+ +---------+ * * ^ ^ ^ ^ ^ ^ * | | | | | | * pcl pch pcl pch pcl pch * * For the vax we assert that samples will never fall in the first * two bytes of any routine, since that is the entry mask, * thus we give call alignentries() to adjust the entry points if * the entry mask falls in one bucket but the code for the routine * doesn't start until the next bucket. In conjunction with the * alignment of routine addresses, this should allow us to have * only one sample for every four bytes of text space and never * have any overlap (the two end cases, above). */ void asgnsamples() { register int j; double ccnt; double time; unsigned long pcl, pch; register int i; unsigned long overlap; unsigned long svalue0, svalue1; /* read samples and assign to namelist symbols */ scale = highpc - lowpc; scale /= nsamples; alignentries(); for (i = 0, j = 1; i < nsamples; i++) { ccnt = samples[i]; if (ccnt == 0) continue; pcl = lowpc + (unsigned long)(scale * i); pch = lowpc + (unsigned long)(scale * (i + 1)); time = ccnt; # ifdef DEBUG if ( debug & SAMPLEDEBUG ) { printf( "[asgnsamples] pcl 0x%lx pch 0x%lx ccnt %.0f\n" , pcl , pch , ccnt ); } # endif /* DEBUG */ totime += time; for (j = j - 1; j < nname; j++) { svalue0 = nl[j].svalue; svalue1 = nl[j+1].svalue; /* * if high end of tick is below entry address, * go for next tick. */ if (pch < svalue0) break; /* * if low end of tick into next routine, * go for next routine. */ if (pcl >= svalue1) continue; overlap = min(pch, svalue1) - max(pcl, svalue0); if (overlap > 0) { # ifdef DEBUG if (debug & SAMPLEDEBUG) { printf("[asgnsamples] (0x%lx->0x%lx-0x%lx) %s gets %f ticks %lu overlap\n", nl[j].value / HISTORICAL_SCALE_2, svalue0, svalue1, nl[j].name, overlap * time / scale, overlap); } # endif /* DEBUG */ nl[j].time += overlap * time / scale; } } } # ifdef DEBUG if (debug & SAMPLEDEBUG) { printf("[asgnsamples] totime %f\n", totime); } # endif /* DEBUG */ } unsigned long min(a, b) unsigned long a,b; { if (ab) return(a); return(b); } /* * calculate scaled entry point addresses (to save time in asgnsamples), * and possibly push the scaled entry points over the entry mask, * if it turns out that the entry point is in one bucket and the code * for a routine is in the next bucket. */ void alignentries() { register struct nl *nlp; unsigned long bucket_of_entry; unsigned long bucket_of_code; for (nlp = nl; nlp < npe; nlp++) { nlp -> svalue = nlp -> value / HISTORICAL_SCALE_2; bucket_of_entry = (nlp->svalue - lowpc) / scale; bucket_of_code = (nlp->svalue + OFFSET_OF_CODE / HISTORICAL_SCALE_2 - lowpc) / scale; if (bucket_of_entry < bucket_of_code) { # ifdef DEBUG if (debug & SAMPLEDEBUG) { printf("[alignentries] pushing svalue 0x%lx to 0x%lx\n", nlp->svalue, nlp->svalue + OFFSET_OF_CODE / HISTORICAL_SCALE_2); } # endif /* DEBUG */ nlp->svalue += OFFSET_OF_CODE / HISTORICAL_SCALE_2; } } } Index: head/usr.bin/nfsstat/nfsstat.c =================================================================== --- head/usr.bin/nfsstat/nfsstat.c (revision 131989) +++ head/usr.bin/nfsstat/nfsstat.c (revision 131990) @@ -1,496 +1,497 @@ /* * Copyright (c) 1983, 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Rick Macklem at The University of Guelph. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char copyright[] = "@(#) Copyright (c) 1983, 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)nfsstat.c 8.2 (Berkeley) 3/31/95"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct nlist nl[] = { #define N_NFSSTAT 0 { "nfsstats" }, #define N_NFSRVSTAT 1 { "nfsrvstats" }, "", }; kvm_t *kd; static int deadkernel = 0; static int widemode = 0; void intpr(int, int); void printhdr(int, int); void sidewaysintpr(u_int, int, int); void usage(void); char *sperc1(int, int); char *sperc2(int, int); #define DELTA(field) (nfsstats.field - lastst.field) +int main(argc, argv) int argc; char **argv; { u_int interval; int clientOnly = -1; int serverOnly = -1; int ch; char *memf, *nlistf; char errbuf[_POSIX2_LINE_MAX]; interval = 0; memf = nlistf = NULL; while ((ch = getopt(argc, argv, "csWM:N:w:")) != -1) switch(ch) { case 'M': memf = optarg; break; case 'N': nlistf = optarg; break; case 'W': widemode = 1; break; case 'w': interval = atoi(optarg); break; case 'c': clientOnly = 1; if (serverOnly < 0) serverOnly = 0; break; case 's': serverOnly = 1; if (clientOnly < 0) clientOnly = 0; break; case '?': default: usage(); } argc -= optind; argv += optind; #define BACKWARD_COMPATIBILITY #ifdef BACKWARD_COMPATIBILITY if (*argv) { interval = atoi(*argv); if (*++argv) { nlistf = *argv; if (*++argv) memf = *argv; } } #endif if (nlistf != NULL || memf != NULL) { deadkernel = 1; if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == 0) { errx(1, "kvm_openfiles: %s", errbuf); } if (kvm_nlist(kd, nl) != 0) { errx(1, "kvm_nlist: can't get names"); } } if (interval) sidewaysintpr(interval, clientOnly, serverOnly); else intpr(clientOnly, serverOnly); exit(0); } /* * Read the nfs stats using sysctl(3) for live kernels, or kvm_read * for dead ones. */ void readstats(stp, srvstp) struct nfsstats **stp; struct nfsrvstats **srvstp; { size_t buflen; if (deadkernel) { if (kvm_read(kd, (u_long)nl[N_NFSSTAT].n_value, *stp, sizeof(struct nfsstats)) < 0) { *stp = NULL; } if (kvm_read(kd, (u_long)nl[N_NFSRVSTAT].n_value, *srvstp, sizeof(struct nfsrvstats)) < 0) { *srvstp = NULL; } } else { buflen = sizeof(struct nfsstats); if (sysctlbyname("vfs.nfs.nfsstats", *stp, &buflen, (void *)0, (size_t)0) < 0) { *stp = NULL; } buflen = sizeof(struct nfsrvstats); if (sysctlbyname("vfs.nfsrv.nfsrvstats", *srvstp, &buflen, (void *)0, (size_t)0) < 0) { *srvstp = NULL; } } } /* * Print a description of the nfs stats. */ void intpr(int clientOnly, int serverOnly) { struct nfsstats nfsstats, *nfsstatsp; struct nfsrvstats nfsrvstats, *nfsrvstatsp; nfsstatsp = &nfsstats; nfsrvstatsp = &nfsrvstats; readstats(&nfsstatsp, &nfsrvstatsp); if (clientOnly && !nfsstatsp) { printf("Client not present!\n"); clientOnly = 0; } if (clientOnly) { printf("Client Info:\n"); printf("Rpc Counts:\n"); printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n", "Getattr", "Setattr", "Lookup", "Readlink", "Read", "Write", "Create", "Remove"); printf("%9d %9d %9d %9d %9d %9d %9d %9d\n", nfsstats.rpccnt[NFSPROC_GETATTR], nfsstats.rpccnt[NFSPROC_SETATTR], nfsstats.rpccnt[NFSPROC_LOOKUP], nfsstats.rpccnt[NFSPROC_READLINK], nfsstats.rpccnt[NFSPROC_READ], nfsstats.rpccnt[NFSPROC_WRITE], nfsstats.rpccnt[NFSPROC_CREATE], nfsstats.rpccnt[NFSPROC_REMOVE]); printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n", "Rename", "Link", "Symlink", "Mkdir", "Rmdir", "Readdir", "RdirPlus", "Access"); printf("%9d %9d %9d %9d %9d %9d %9d %9d\n", nfsstats.rpccnt[NFSPROC_RENAME], nfsstats.rpccnt[NFSPROC_LINK], nfsstats.rpccnt[NFSPROC_SYMLINK], nfsstats.rpccnt[NFSPROC_MKDIR], nfsstats.rpccnt[NFSPROC_RMDIR], nfsstats.rpccnt[NFSPROC_READDIR], nfsstats.rpccnt[NFSPROC_READDIRPLUS], nfsstats.rpccnt[NFSPROC_ACCESS]); printf("%9.9s %9.9s %9.9s %9.9s %9.9s\n", "Mknod", "Fsstat", "Fsinfo", "PathConf", "Commit"); printf("%9d %9d %9d %9d %9d\n", nfsstats.rpccnt[NFSPROC_MKNOD], nfsstats.rpccnt[NFSPROC_FSSTAT], nfsstats.rpccnt[NFSPROC_FSINFO], nfsstats.rpccnt[NFSPROC_PATHCONF], nfsstats.rpccnt[NFSPROC_COMMIT]); printf("Rpc Info:\n"); printf("%9.9s %9.9s %9.9s %9.9s %9.9s\n", "TimedOut", "Invalid", "X Replies", "Retries", "Requests"); printf("%9d %9d %9d %9d %9d\n", nfsstats.rpctimeouts, nfsstats.rpcinvalid, nfsstats.rpcunexpected, nfsstats.rpcretries, nfsstats.rpcrequests); printf("Cache Info:\n"); printf("%9.9s %9.9s %9.9s %9.9s", "Attr Hits", "Misses", "Lkup Hits", "Misses"); printf(" %9.9s %9.9s %9.9s %9.9s\n", "BioR Hits", "Misses", "BioW Hits", "Misses"); printf("%9d %9d %9d %9d", nfsstats.attrcache_hits, nfsstats.attrcache_misses, nfsstats.lookupcache_hits, nfsstats.lookupcache_misses); printf(" %9d %9d %9d %9d\n", nfsstats.biocache_reads-nfsstats.read_bios, nfsstats.read_bios, nfsstats.biocache_writes-nfsstats.write_bios, nfsstats.write_bios); printf("%9.9s %9.9s %9.9s %9.9s", "BioRLHits", "Misses", "BioD Hits", "Misses"); printf(" %9.9s %9.9s\n", "DirE Hits", "Misses"); printf("%9d %9d %9d %9d", nfsstats.biocache_readlinks-nfsstats.readlink_bios, nfsstats.readlink_bios, nfsstats.biocache_readdirs-nfsstats.readdir_bios, nfsstats.readdir_bios); printf(" %9d %9d\n", nfsstats.direofcache_hits, nfsstats.direofcache_misses); } if (serverOnly && !nfsrvstatsp) { printf("Server not present!\n"); serverOnly = 0; } if (serverOnly) { printf("\nServer Info:\n"); printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n", "Getattr", "Setattr", "Lookup", "Readlink", "Read", "Write", "Create", "Remove"); printf("%9d %9d %9d %9d %9d %9d %9d %9d\n", nfsrvstats.srvrpccnt[NFSPROC_GETATTR], nfsrvstats.srvrpccnt[NFSPROC_SETATTR], nfsrvstats.srvrpccnt[NFSPROC_LOOKUP], nfsrvstats.srvrpccnt[NFSPROC_READLINK], nfsrvstats.srvrpccnt[NFSPROC_READ], nfsrvstats.srvrpccnt[NFSPROC_WRITE], nfsrvstats.srvrpccnt[NFSPROC_CREATE], nfsrvstats.srvrpccnt[NFSPROC_REMOVE]); printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n", "Rename", "Link", "Symlink", "Mkdir", "Rmdir", "Readdir", "RdirPlus", "Access"); printf("%9d %9d %9d %9d %9d %9d %9d %9d\n", nfsrvstats.srvrpccnt[NFSPROC_RENAME], nfsrvstats.srvrpccnt[NFSPROC_LINK], nfsrvstats.srvrpccnt[NFSPROC_SYMLINK], nfsrvstats.srvrpccnt[NFSPROC_MKDIR], nfsrvstats.srvrpccnt[NFSPROC_RMDIR], nfsrvstats.srvrpccnt[NFSPROC_READDIR], nfsrvstats.srvrpccnt[NFSPROC_READDIRPLUS], nfsrvstats.srvrpccnt[NFSPROC_ACCESS]); printf("%9.9s %9.9s %9.9s %9.9s %9.9s\n", "Mknod", "Fsstat", "Fsinfo", "PathConf", "Commit"); printf("%9d %9d %9d %9d %9d\n", nfsrvstats.srvrpccnt[NFSPROC_MKNOD], nfsrvstats.srvrpccnt[NFSPROC_FSSTAT], nfsrvstats.srvrpccnt[NFSPROC_FSINFO], nfsrvstats.srvrpccnt[NFSPROC_PATHCONF], nfsrvstats.srvrpccnt[NFSPROC_COMMIT]); printf("Server Ret-Failed\n"); printf("%17d\n", nfsrvstats.srvrpc_errs); printf("Server Faults\n"); printf("%13d\n", nfsrvstats.srv_errs); printf("Server Cache Stats:\n"); printf("%9.9s %9.9s %9.9s %9.9s\n", "Inprog", "Idem", "Non-idem", "Misses"); printf("%9d %9d %9d %9d\n", nfsrvstats.srvcache_inproghits, nfsrvstats.srvcache_idemdonehits, nfsrvstats.srvcache_nonidemdonehits, nfsrvstats.srvcache_misses); printf("Server Write Gathering:\n"); printf("%9.9s %9.9s %9.9s\n", "WriteOps", "WriteRPC", "Opsaved"); printf("%9d %9d %9d\n", nfsrvstats.srvvop_writes, nfsrvstats.srvrpccnt[NFSPROC_WRITE], nfsrvstats.srvrpccnt[NFSPROC_WRITE] - nfsrvstats.srvvop_writes); } } u_char signalled; /* set if alarm goes off "early" */ /* * Print a running summary of nfs statistics. * Repeat display every interval seconds, showing statistics * collected over that interval. Assumes that interval is non-zero. * First line printed at top of screen is always cumulative. */ void sidewaysintpr(u_int interval, int clientOnly, int serverOnly) { struct nfsstats nfsstats, lastst, *nfsstatsp; struct nfsrvstats nfsrvstats, lastsrvst, *nfsrvstatsp; int hdrcnt = 1; nfsstatsp = &lastst; nfsrvstatsp = &lastsrvst; readstats(&nfsstatsp, &nfsrvstatsp); if (clientOnly && !nfsstatsp) { printf("Client not present!\n"); clientOnly = 0; } if (serverOnly && !nfsrvstatsp) { printf("Server not present!\n"); serverOnly = 0; } sleep(interval); for (;;) { nfsstatsp = &nfsstats; nfsrvstatsp = &nfsrvstats; readstats(&nfsstatsp, &nfsrvstatsp); if (--hdrcnt == 0) { printhdr(clientOnly, serverOnly); if (clientOnly && serverOnly) hdrcnt = 10; else hdrcnt = 20; } if (clientOnly) { printf("%s %6d %6d %6d %6d %6d %6d %6d %6d", ((clientOnly && serverOnly) ? "Client:" : ""), DELTA(attrcache_hits) + DELTA(attrcache_misses), DELTA(lookupcache_hits) + DELTA(lookupcache_misses), DELTA(biocache_readlinks), DELTA(biocache_reads), DELTA(biocache_writes), nfsstats.rpccnt[NFSPROC_RENAME]-lastst.rpccnt[NFSPROC_RENAME], DELTA(accesscache_hits) + DELTA(accesscache_misses), DELTA(biocache_readdirs) ); if (widemode) { printf(" %s %s %s %s %s %s", sperc1(DELTA(attrcache_hits), DELTA(attrcache_misses)), sperc1(DELTA(lookupcache_hits), DELTA(lookupcache_misses)), sperc2(DELTA(biocache_reads), DELTA(read_bios)), sperc2(DELTA(biocache_writes), DELTA(write_bios)), sperc1(DELTA(accesscache_hits), DELTA(accesscache_misses)), sperc2(DELTA(biocache_readdirs), DELTA(readdir_bios)) ); } printf("\n"); lastst = nfsstats; } if (serverOnly) { printf("%s %6d %6d %6d %6d %6d %6d %6d %6d", ((clientOnly && serverOnly) ? "Server:" : ""), nfsrvstats.srvrpccnt[NFSPROC_GETATTR]-lastsrvst.srvrpccnt[NFSPROC_GETATTR], nfsrvstats.srvrpccnt[NFSPROC_LOOKUP]-lastsrvst.srvrpccnt[NFSPROC_LOOKUP], nfsrvstats.srvrpccnt[NFSPROC_READLINK]-lastsrvst.srvrpccnt[NFSPROC_READLINK], nfsrvstats.srvrpccnt[NFSPROC_READ]-lastsrvst.srvrpccnt[NFSPROC_READ], nfsrvstats.srvrpccnt[NFSPROC_WRITE]-lastsrvst.srvrpccnt[NFSPROC_WRITE], nfsrvstats.srvrpccnt[NFSPROC_RENAME]-lastsrvst.srvrpccnt[NFSPROC_RENAME], nfsrvstats.srvrpccnt[NFSPROC_ACCESS]-lastsrvst.srvrpccnt[NFSPROC_ACCESS], (nfsrvstats.srvrpccnt[NFSPROC_READDIR]-lastsrvst.srvrpccnt[NFSPROC_READDIR]) +(nfsrvstats.srvrpccnt[NFSPROC_READDIRPLUS]-lastsrvst.srvrpccnt[NFSPROC_READDIRPLUS])); printf("\n"); lastsrvst = nfsrvstats; } fflush(stdout); sleep(interval); } /*NOTREACHED*/ } void printhdr(int clientOnly, int serverOnly) { printf("%s%6.6s %6.6s %6.6s %6.6s %6.6s %6.6s %6.6s %6.6s", ((serverOnly && clientOnly) ? " " : " "), "GtAttr", "Lookup", "Rdlink", "Read", "Write", "Rename", "Access", "Rddir"); if (widemode && clientOnly) { printf(" Attr Lkup BioR BioW Accs BioD"); } printf("\n"); fflush(stdout); } void usage() { (void)fprintf(stderr, "usage: nfsstat [-csW] [-M core] [-N system] [-w interval]\n"); exit(1); } static char SPBuf[64][8]; static int SPIndex; char * sperc1(int hits, int misses) { char *p = SPBuf[SPIndex]; if (hits + misses) { sprintf(p, "%3d%%", (int)(char)((quad_t)hits * 100 / (hits + misses))); } else { sprintf(p, " -"); } SPIndex = (SPIndex + 1) & 63; return(p); } char * sperc2(int ttl, int misses) { char *p = SPBuf[SPIndex]; if (ttl) { sprintf(p, "%3d%%", (int)(char)((quad_t)(ttl - misses) * 100 / ttl)); } else { sprintf(p, " -"); } SPIndex = (SPIndex + 1) & 63; return(p); } Index: head/usr.sbin/mrouted/main.c =================================================================== --- head/usr.sbin/mrouted/main.c (revision 131989) +++ head/usr.sbin/mrouted/main.c (revision 131990) @@ -1,1064 +1,1064 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * * main.c,v 3.8.4.29 1998/03/01 01:49:00 fenner Exp */ /* * Written by Steve Deering, Stanford University, February 1989. * * (An earlier version of DVMRP was implemented by David Waitzman of * BBN STC by extending Berkeley's routed program. Some of Waitzman's * extensions have been incorporated into mrouted, but none of the * original routed code has been adopted.) */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif #include #include "defs.h" #ifdef __STDC__ #include #else #include #endif #include #include #ifdef SNMP #include "snmp.h" #endif extern char *configfilename; char versionstring[100]; static char pidfilename[] = _PATH_MROUTED_PID; static char dumpfilename[] = _PATH_MROUTED_DUMP; static char cachefilename[] = _PATH_MROUTED_CACHE; static char genidfilename[] = _PATH_MROUTED_GENID; static int haveterminal = 1; int did_final_init = 0; static int sighandled = 0; #define GOT_SIGINT 0x01 #define GOT_SIGHUP 0x02 #define GOT_SIGUSR1 0x04 #define GOT_SIGUSR2 0x08 int cache_lifetime = DEFAULT_CACHE_LIFETIME; int prune_lifetime = AVERAGE_PRUNE_LIFETIME; int debug = 0; char *progname; time_t mrouted_init_time; #ifdef SNMP #define NHANDLERS 34 #else #define NHANDLERS 2 #endif static struct ihandler { int fd; /* File descriptor */ ihfunc_t func; /* Function to call with &fd_set */ } ihandlers[NHANDLERS]; static int nhandlers = 0; static struct debugname { char *name; int level; int nchars; } debugnames[] = { { "packet", DEBUG_PKT, 2 }, { "pkt", DEBUG_PKT, 3 }, { "pruning", DEBUG_PRUNE, 1 }, { "prunes", DEBUG_PRUNE, 1 }, { "routing", DEBUG_ROUTE, 1 }, { "routes", DEBUG_ROUTE, 1 }, { "route_detail", DEBUG_RTDETAIL, 6 }, { "rtdetail", DEBUG_RTDETAIL, 2 }, { "peers", DEBUG_PEER, 2 }, { "neighbors", DEBUG_PEER, 1 }, { "cache", DEBUG_CACHE, 1 }, { "timeout", DEBUG_TIMEOUT, 1 }, { "callout", DEBUG_TIMEOUT, 2 }, { "interface", DEBUG_IF, 2 }, { "vif", DEBUG_IF, 1 }, { "membership", DEBUG_MEMBER, 1 }, { "groups", DEBUG_MEMBER, 1 }, { "traceroute", DEBUG_TRACE, 2 }, { "mtrace", DEBUG_TRACE, 2 }, { "igmp", DEBUG_IGMP, 1 }, { "icmp", DEBUG_ICMP, 2 }, { "rsrr", DEBUG_RSRR, 2 }, { "3", 0xffffffff, 1 } /* compat. */ }; /* * Forward declarations. */ static void final_init __P((void *)); static void fasttimer __P((void *)); static void timer __P((void *)); static void dump __P((void)); static void dump_version __P((FILE *)); static void fdump __P((void)); static void cdump __P((void)); static void restart __P((void)); static void handler __P((int)); static void cleanup __P((void)); static void resetlogging __P((void *)); static void usage __P((void)); /* To shut up gcc -Wstrict-prototypes */ int main __P((int argc, char **argv)); int register_input_handler(fd, func) int fd; ihfunc_t func; { if (nhandlers >= NHANDLERS) return -1; ihandlers[nhandlers].fd = fd; ihandlers[nhandlers++].func = func; return 0; } int main(argc, argv) int argc; char *argv[]; { register int recvlen; int dummy; FILE *fp; struct timeval tv, difftime, curtime, lasttime, *timeout; u_int32 prev_genid; int vers; fd_set rfds, readers; int nfds, n, i, secs; extern char todaysversion[]; struct sigaction sa; #ifdef SNMP struct timeval timeout, *tvp = &timeout; struct timeval sched, *svp = &sched, now, *nvp = &now; int index, block; #endif setlinebuf(stderr); if (geteuid() != 0) errx(1, "must be root"); progname = strrchr(argv[0], '/'); if (progname) progname++; else progname = argv[0]; argv++, argc--; while (argc > 0 && *argv[0] == '-') { if (strcmp(*argv, "-d") == 0) { if (argc > 1 && *(argv + 1)[0] != '-') { char *p,*q; int i, len; struct debugname *d; argv++, argc--; debug = 0; p = *argv; q = NULL; while (p) { q = strchr(p, ','); if (q) *q++ = '\0'; len = strlen(p); for (i = 0, d = debugnames; i < sizeof(debugnames) / sizeof(debugnames[0]); i++, d++) if (len >= d->nchars && strncmp(d->name, p, len) == 0) break; if (i == sizeof(debugnames) / sizeof(debugnames[0])) { int j = 0xffffffff; int k = 0; fprintf(stderr, "Valid debug levels: "); for (i = 0, d = debugnames; i < sizeof(debugnames) / sizeof(debugnames[0]); i++, d++) { if ((j & d->level) == d->level) { if (k++) putc(',', stderr); fputs(d->name, stderr); j &= ~d->level; } } putc('\n', stderr); usage(); } debug |= d->level; p = q; } } else debug = DEFAULT_DEBUG; } else if (strcmp(*argv, "-c") == 0) { if (argc > 1) { argv++, argc--; configfilename = *argv; } else usage(); } else if (strcmp(*argv, "-p") == 0) { log(LOG_WARNING, 0, "disabling pruning is no longer supported"); #ifdef SNMP } else if (strcmp(*argv, "-P") == 0) { if (argc > 1 && isdigit(*(argv + 1)[0])) { argv++, argc--; dest_port = atoi(*argv); } else dest_port = DEFAULT_PORT; #endif } else usage(); argv++, argc--; } if (argc > 0) usage(); if (debug != 0) { struct debugname *d; char c; int tmpd = debug; fprintf(stderr, "debug level 0x%x ", debug); c = '('; for (d = debugnames; d < debugnames + sizeof(debugnames) / sizeof(debugnames[0]); d++) { if ((tmpd & d->level) == d->level) { tmpd &= ~d->level; fprintf(stderr, "%c%s", c, d->name); c = ','; } } fprintf(stderr, ")\n"); } #ifdef LOG_DAEMON (void)openlog("mrouted", LOG_PID, LOG_DAEMON); (void)setlogmask(LOG_UPTO(LOG_NOTICE)); #else (void)openlog("mrouted", LOG_PID); #endif sprintf(versionstring, "mrouted version %s", todaysversion); log(LOG_DEBUG, 0, "%s starting", versionstring); #ifdef SYSV srand48(time(NULL)); #endif /* * Get generation id */ gettimeofday(&tv, 0); dvmrp_genid = tv.tv_sec; fp = fopen(genidfilename, "r"); if (fp != NULL) { fscanf(fp, "%d", &prev_genid); if (prev_genid == dvmrp_genid) dvmrp_genid++; (void) fclose(fp); } fp = fopen(genidfilename, "w"); if (fp != NULL) { fprintf(fp, "%d", dvmrp_genid); (void) fclose(fp); } /* Start up the log rate-limiter */ resetlogging(NULL); callout_init(); init_igmp(); init_icmp(); init_ipip(); init_routes(); init_ktable(); #ifndef OLD_KERNEL /* * Unfortunately, you can't k_get_version() unless you've * k_init_dvmrp()'d. Now that we want to move the * k_init_dvmrp() to later in the initialization sequence, * we have to do the disgusting hack of initializing, * getting the version, then stopping the kernel multicast * forwarding. */ k_init_dvmrp(); vers = k_get_version(); k_stop_dvmrp(); /*XXX * This function must change whenever the kernel version changes */ if ((((vers >> 8) & 0xff) != 3) || ((vers & 0xff) != 5)) log(LOG_ERR, 0, "kernel (v%d.%d)/mrouted (v%d.%d) version mismatch", (vers >> 8) & 0xff, vers & 0xff, PROTOCOL_VERSION, MROUTED_VERSION); #endif #ifdef SNMP if (i = snmp_init()) return i; gettimeofday(nvp, 0); if (nvp->tv_usec < 500000L){ svp->tv_usec = nvp->tv_usec + 500000L; svp->tv_sec = nvp->tv_sec; } else { svp->tv_usec = nvp->tv_usec - 500000L; svp->tv_sec = nvp->tv_sec + 1; } #endif /* SNMP */ init_vifs(); #ifdef RSRR rsrr_init(); #endif /* RSRR */ sa.sa_handler = handler; sa.sa_flags = 0; /* Interrupt system calls */ sigemptyset(&sa.sa_mask); sigaction(SIGHUP, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGUSR1, &sa, NULL); sigaction(SIGUSR2, &sa, NULL); if (igmp_socket >= FD_SETSIZE) log(LOG_ERR, 0, "descriptor too big"); FD_ZERO(&readers); FD_SET(igmp_socket, &readers); nfds = igmp_socket + 1; for (i = 0; i < nhandlers; i++) { if (ihandlers[i].fd >= FD_SETSIZE) log(LOG_ERR, 0, "descriptor too big"); FD_SET(ihandlers[i].fd, &readers); if (ihandlers[i].fd >= nfds) nfds = ihandlers[i].fd + 1; } IF_DEBUG(DEBUG_IF) dump_vifs(stderr); IF_DEBUG(DEBUG_ROUTE) dump_routes(stderr); /* schedule first timer interrupt */ timer_setTimer(1, fasttimer, NULL); timer_setTimer(TIMER_INTERVAL, timer, NULL); if (debug == 0) { /* * Detach from the terminal */ int t; haveterminal = 0; if (fork()) exit(0); (void)close(0); (void)close(1); (void)close(2); (void)open("/", 0); (void)dup2(0, 1); (void)dup2(0, 2); #if defined(SYSV) || defined(linux) (void)setpgrp(); #else #ifdef TIOCNOTTY t = open(_PATH_TTY, 2); if (t >= 0) { (void)ioctl(t, TIOCNOTTY, (char *)0); (void)close(t); } #else if (setsid() < 0) perror("setsid"); #endif #endif } fp = fopen(pidfilename, "w"); if (fp != NULL) { fprintf(fp, "%d\n", (int)getpid()); (void) fclose(fp); } /* XXX HACK * This will cause black holes for the first few seconds after startup, * since we are exchanging routes but not actually forwarding. * However, it eliminates much of the startup transient. * * It's possible that we can set a flag which says not to report any * routes (just accept reports) until this timer fires, and then * do a report_to_all_neighbors(ALL_ROUTES) immediately before * turning on DVMRP. */ timer_setTimer(10, final_init, NULL); /* * Main receive loop. */ dummy = 0; difftime.tv_usec = 0; gettimeofday(&curtime, NULL); lasttime = curtime; for(;;) { bcopy((char *)&readers, (char *)&rfds, sizeof(rfds)); secs = timer_nextTimer(); if (secs == -1) timeout = NULL; else { timeout = &tv; timeout->tv_sec = secs; timeout->tv_usec = 0; } #ifdef SNMP THIS IS BROKEN if (nvp->tv_sec > svp->tv_sec || (nvp->tv_sec == svp->tv_sec && nvp->tv_usec > svp->tv_usec)){ alarmTimer(nvp); eventTimer(nvp); if (nvp->tv_usec < 500000L){ svp->tv_usec = nvp->tv_usec + 500000L; svp->tv_sec = nvp->tv_sec; } else { svp->tv_usec = nvp->tv_usec - 500000L; svp->tv_sec = nvp->tv_sec + 1; } } tvp = &timeout; tvp->tv_sec = 0; tvp->tv_usec = 500000L; block = 0; snmp_select_info(&nfds, &rfds, tvp, &block); if (block == 1) tvp = NULL; /* block without timeout */ if ((n = select(nfds, &rfds, NULL, NULL, tvp)) < 0) #endif if (sighandled) { if (sighandled & GOT_SIGINT) { sighandled &= ~GOT_SIGINT; break; } if (sighandled & GOT_SIGHUP) { sighandled &= ~GOT_SIGHUP; restart(); } if (sighandled & GOT_SIGUSR1) { sighandled &= ~GOT_SIGUSR1; fdump(); } if (sighandled & GOT_SIGUSR2) { sighandled &= ~GOT_SIGUSR2; cdump(); } } if ((n = select(nfds, &rfds, NULL, NULL, timeout)) < 0) { if (errno != EINTR) log(LOG_WARNING, errno, "select failed"); continue; } if (n > 0) { if (FD_ISSET(igmp_socket, &rfds)) { recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE, 0, NULL, &dummy); if (recvlen < 0) { if (errno != EINTR) log(LOG_ERR, errno, "recvfrom"); continue; } accept_igmp(recvlen); } for (i = 0; i < nhandlers; i++) { if (FD_ISSET(ihandlers[i].fd, &rfds)) { (*ihandlers[i].func)(ihandlers[i].fd, &rfds); } } } #ifdef SNMP THIS IS BROKEN snmp_read(&rfds); snmp_timeout(); /* poll */ #endif /* * Handle timeout queue. * * If select + packet processing took more than 1 second, * or if there is a timeout pending, age the timeout queue. * * If not, collect usec in difftime to make sure that the * time doesn't drift too badly. * * If the timeout handlers took more than 1 second, * age the timeout queue again. XXX This introduces the * potential for infinite loops! */ do { /* * If the select timed out, then there's no other * activity to account for and we don't need to * call gettimeofday. */ if (n == 0) { curtime.tv_sec = lasttime.tv_sec + secs; curtime.tv_usec = lasttime.tv_usec; n = -1; /* don't do this next time through the loop */ } else gettimeofday(&curtime, NULL); difftime.tv_sec = curtime.tv_sec - lasttime.tv_sec; difftime.tv_usec += curtime.tv_usec - lasttime.tv_usec; while (difftime.tv_usec >= 1000000) { difftime.tv_sec++; difftime.tv_usec -= 1000000; } if (difftime.tv_usec < 0) { difftime.tv_sec--; difftime.tv_usec += 1000000; } lasttime = curtime; if (secs == 0 || difftime.tv_sec > 0) age_callout_queue(difftime.tv_sec); secs = -1; } while (difftime.tv_sec > 0); } log(LOG_NOTICE, 0, "%s exiting", versionstring); cleanup(); exit(0); } static void usage() { fprintf(stderr, "usage: mrouted [-p] [-c configfile] [-d [debug_level]]\n"); exit(1); } static void final_init(i) void *i; { char *s = (char *)i; log(LOG_NOTICE, 0, "%s%s", versionstring, s ? s : ""); if (s) free(s); k_init_dvmrp(); /* enable DVMRP routing in kernel */ /* * Install the vifs in the kernel as late as possible in the * initialization sequence. */ init_installvifs(); time(&mrouted_init_time); did_final_init = 1; } /* * routine invoked every second. Its main goal is to cycle through * the routing table and send partial updates to all neighbors at a * rate that will cause the entire table to be sent in ROUTE_REPORT_INTERVAL * seconds. Also, every TIMER_INTERVAL seconds it calls timer() to * do all the other time-based processing. */ static void fasttimer(i) void *i; { static unsigned int tlast; static unsigned int nsent; register unsigned int t = tlast + 1; register int n; /* * if we're in the last second, send everything that's left. * otherwise send at least the fraction we should have sent by now. */ if (t >= ROUTE_REPORT_INTERVAL) { register int nleft = nroutes - nsent; while (nleft > 0) { if ((n = report_next_chunk()) <= 0) break; nleft -= n; } tlast = 0; nsent = 0; } else { register unsigned int ncum = nroutes * t / ROUTE_REPORT_INTERVAL; while (nsent < ncum) { if ((n = report_next_chunk()) <= 0) break; nsent += n; } tlast = t; } timer_setTimer(1, fasttimer, NULL); } /* * The 'virtual_time' variable is initialized to a value that will cause the * first invocation of timer() to send a probe or route report to all vifs * and send group membership queries to all subnets for which this router is * querier. This first invocation occurs approximately TIMER_INTERVAL seconds * after the router starts up. Note that probes for neighbors and queries * for group memberships are also sent at start-up time, as part of initial- * ization. This repetition after a short interval is desirable for quickly * building up topology and membership information in the presence of possible * packet loss. * * 'virtual_time' advances at a rate that is only a crude approximation of * real time, because it does not take into account any time spent processing, * and because the timer intervals are sometimes shrunk by a random amount to * avoid unwanted synchronization with other routers. */ u_long virtual_time = 0; /* * Timer routine. Performs periodic neighbor probing, route reporting, and * group querying duties, and drives various timers in routing entries and * virtual interface data structures. */ static void timer(i) void *i; { age_routes(); /* Advance the timers in the route entries */ age_vifs(); /* Advance the timers for neighbors */ age_table_entry(); /* Advance the timers for the cache entries */ if (virtual_time % IGMP_QUERY_INTERVAL == 0) { /* * Time to query the local group memberships on all subnets * for which this router is the elected querier. */ query_groups(); } if (virtual_time % NEIGHBOR_PROBE_INTERVAL == 0) { /* * Time to send a probe on all vifs from which no neighbors have * been heard. Also, check if any inoperative interfaces have now * come up. (If they have, they will also be probed as part of * their initialization.) */ probe_for_neighbors(); if (vifs_down) check_vif_state(); } delay_change_reports = FALSE; if (routes_changed) { /* * Some routes have changed since the last timer interrupt, but * have not been reported yet. Report the changed routes to all * neighbors. */ report_to_all_neighbors(CHANGED_ROUTES); } #ifdef SNMP sync_timer(); #endif /* * Advance virtual time */ virtual_time += TIMER_INTERVAL; timer_setTimer(TIMER_INTERVAL, timer, NULL); } static void cleanup() { - static in_cleanup = 0; + static int in_cleanup = 0; if (!in_cleanup) { in_cleanup++; #ifdef RSRR rsrr_clean(); #endif /* RSRR */ expire_all_routes(); report_to_all_neighbors(ALL_ROUTES); if (did_final_init) k_stop_dvmrp(); } } /* * Signal handler. Take note of the fact that the signal arrived * so that the main loop can take care of it. */ static void handler(sig) int sig; { switch (sig) { case SIGINT: case SIGTERM: sighandled |= GOT_SIGINT; break; case SIGHUP: sighandled |= GOT_SIGHUP; break; case SIGUSR1: sighandled |= GOT_SIGUSR1; break; case SIGUSR2: sighandled |= GOT_SIGUSR2; break; } } /* * Dump internal data structures to stderr. */ static void dump() { dump_vifs(stderr); dump_routes(stderr); } static void dump_version(fp) FILE *fp; { time_t t; time(&t); fprintf(fp, "%s ", versionstring); if (did_final_init) fprintf(fp, "up %s", scaletime(t - mrouted_init_time)); else fprintf(fp, "(not yet initialized)"); fprintf(fp, " %s\n", ctime(&t)); } /* * Dump internal data structures to a file. */ static void fdump() { FILE *fp; fp = fopen(dumpfilename, "w"); if (fp != NULL) { dump_version(fp); dump_vifs(fp); dump_routes(fp); (void) fclose(fp); } } /* * Dump local cache contents to a file. */ static void cdump() { FILE *fp; fp = fopen(cachefilename, "w"); if (fp != NULL) { dump_version(fp); dump_cache(fp); (void) fclose(fp); } } /* * Restart mrouted */ static void restart() { char *s; s = (char *)malloc(sizeof(" restart")); if (s == NULL) log(LOG_ERR, 0, "out of memory"); strcpy(s, " restart"); /* * reset all the entries */ free_all_prunes(); free_all_routes(); free_all_callouts(); stop_all_vifs(); k_stop_dvmrp(); close(igmp_socket); close(udp_socket); did_final_init = 0; /* * start processing again */ dvmrp_genid++; init_igmp(); init_routes(); init_ktable(); init_vifs(); /*XXX Schedule final_init() as main does? */ final_init(s); /* schedule timer interrupts */ timer_setTimer(1, fasttimer, NULL); timer_setTimer(TIMER_INTERVAL, timer, NULL); } #define LOG_MAX_MSGS 20 /* if > 20/minute then shut up for a while */ #define LOG_SHUT_UP 600 /* shut up for 10 minutes */ static int log_nmsgs = 0; static void resetlogging(arg) void *arg; { int nxttime = 60; void *narg = NULL; if (arg == NULL && log_nmsgs > LOG_MAX_MSGS) { nxttime = LOG_SHUT_UP; narg = (void *)&log_nmsgs; /* just need some valid void * */ syslog(LOG_WARNING, "logging too fast, shutting up for %d minutes", LOG_SHUT_UP / 60); } else { log_nmsgs = 0; } timer_setTimer(nxttime, resetlogging, narg); } char * scaletime(t) u_long t; { #define SCALETIMEBUFLEN 20 static char buf1[20]; static char buf2[20]; static char *buf = buf1; char *p; p = buf; if (buf == buf1) buf = buf2; else buf = buf1; /* XXX snprintf */ sprintf(p, "%2ld:%02ld:%02ld", t / 3600, (t % 3600) / 60, t % 60); p[SCALETIMEBUFLEN - 1] = '\0'; return p; } #ifdef RINGBUFFER #define NLOGMSGS 10000 #define LOGMSGSIZE 200 char *logmsg[NLOGMSGS]; static int logmsgno = 0; void printringbuf() { FILE *f; int i; f = fopen("/var/tmp/mrouted.log", "a"); if (f == NULL) { log(LOG_ERR, errno, "can't open /var/tmp/mrouted.log"); /*NOTREACHED*/ } fprintf(f, "--------------------------------------------\n"); i = (logmsgno + 1) % NLOGMSGS; while (i != logmsgno) { if (*logmsg[i]) { fprintf(f, "%s\n", logmsg[i]); *logmsg[i] = '\0'; } i = (i + 1) % NLOGMSGS; } fclose(f); } #endif /* * Log errors and other messages to the system log daemon and to stderr, * according to the severity of the message and the current debug level. * For errors of severity LOG_ERR or worse, terminate the program. */ #ifdef __STDC__ void log(int severity, int syserr, char *format, ...) { va_list ap; static char fmt[211] = "warning - "; char *msg; struct timeval now; time_t now_sec; struct tm *thyme; #ifdef RINGBUFFER static int ringbufinit = 0; #endif va_start(ap, format); #else /*VARARGS3*/ void log(severity, syserr, format, va_alist) int severity, syserr; char *format; va_dcl { va_list ap; static char fmt[311] = "warning - "; char *msg; char tbuf[20]; struct timeval now; time_t now_sec; struct tm *thyme; #ifdef RINGBUFFER static int ringbufinit = 0; #endif va_start(ap); #endif vsnprintf(&fmt[10], sizeof(fmt) - 10, format, ap); va_end(ap); msg = (severity == LOG_WARNING) ? fmt : &fmt[10]; #ifdef RINGBUFFER if (!ringbufinit) { int i; for (i = 0; i < NLOGMSGS; i++) { logmsg[i] = malloc(LOGMSGSIZE); if (logmsg[i] == 0) { syslog(LOG_ERR, "out of memory"); exit(-1); } *logmsg[i] = 0; } ringbufinit = 1; } gettimeofday(&now,NULL); now_sec = now.tv_sec; thyme = localtime(&now_sec); snprintf(logmsg[logmsgno++], LOGMSGSIZE, "%02d:%02d:%02d.%03ld %s err %d", thyme->tm_hour, thyme->tm_min, thyme->tm_sec, now.tv_usec / 1000, msg, syserr); logmsgno %= NLOGMSGS; if (severity <= LOG_NOTICE) #endif /* * Log to stderr if we haven't forked yet and it's a warning or worse, * or if we're debugging. */ if (haveterminal && (debug || severity <= LOG_WARNING)) { gettimeofday(&now,NULL); now_sec = now.tv_sec; thyme = localtime(&now_sec); if (!debug) fprintf(stderr, "%s: ", progname); fprintf(stderr, "%02d:%02d:%02d.%03ld %s", thyme->tm_hour, thyme->tm_min, thyme->tm_sec, now.tv_usec / 1000, msg); if (syserr == 0) fprintf(stderr, "\n"); else if (syserr < sys_nerr) fprintf(stderr, ": %s\n", sys_errlist[syserr]); else fprintf(stderr, ": errno %d\n", syserr); } /* * Always log things that are worse than warnings, no matter what * the log_nmsgs rate limiter says. * Only count things worse than debugging in the rate limiter * (since if you put daemon.debug in syslog.conf you probably * actually want to log the debugging messages so they shouldn't * be rate-limited) */ if ((severity < LOG_WARNING) || (log_nmsgs < LOG_MAX_MSGS)) { if (severity < LOG_DEBUG) log_nmsgs++; if (syserr != 0) { errno = syserr; syslog(severity, "%s: %m", msg); } else syslog(severity, "%s", msg); } if (severity <= LOG_ERR) exit(-1); } #ifdef DEBUG_MFC void md_log(what, origin, mcastgrp) int what; u_int32 origin, mcastgrp; { static FILE *f = NULL; struct timeval tv; u_int32 buf[4]; if (!f) { if ((f = fopen("/tmp/mrouted.clog", "w")) == NULL) { log(LOG_ERR, errno, "open /tmp/mrouted.clog"); } } gettimeofday(&tv, NULL); buf[0] = tv.tv_sec; buf[1] = what; buf[2] = origin; buf[3] = mcastgrp; fwrite(buf, sizeof(u_int32), 4, f); } #endif Index: head/usr.sbin/sliplogin/sliplogin.c =================================================================== --- head/usr.sbin/sliplogin/sliplogin.c (revision 131989) +++ head/usr.sbin/sliplogin/sliplogin.c (revision 131990) @@ -1,549 +1,550 @@ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char copyright[] = "@(#) Copyright (c) 1990, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)sliplogin.c 8.2 (Berkeley) 2/1/94"; static char rscid[] = "@(#)$FreeBSD$"; #endif /* not lint */ /* * sliplogin.c * [MUST BE RUN SUID, SLOPEN DOES A SUSER()!] * * This program initializes its own tty port to be an async TCP/IP interface. * It sets the line discipline to slip, invokes a shell script to initialize * the network interface, then pauses forever waiting for hangup. * * It is a remote descendant of several similar programs with incestuous ties: * - Kirk Smith's slipconf, modified by Richard Johnsson @ DEC WRL. * - slattach, probably by Rick Adams but touched by countless hordes. * - the original sliplogin for 4.2bsd, Doug Kingston the mover behind it. * * There are two forms of usage: * * "sliplogin" * Invoked simply as "sliplogin", the program looks up the username * in the file /etc/slip.hosts. * If an entry is found, the line on fd0 is configured for SLIP operation * as specified in the file. * * "sliplogin IPhostlogin #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" extern char **environ; static char *restricted_environ[] = { "PATH=" _PATH_STDPATH, NULL }; int unit; int slip_mode; speed_t speed; int uid; int keepal; int outfill; int slunit; char loginargs[BUFSIZ]; char loginfile[MAXPATHLEN]; char loginname[BUFSIZ]; static char raddr[32]; /* remote address */ char ifname[IFNAMSIZ]; /* interface name */ static char pidfilename[MAXPATHLEN]; /* name of pid file */ static char iffilename[MAXPATHLEN]; /* name of if file */ static pid_t pid; /* our pid */ char * make_ipaddr(void) { static char address[20] =""; struct hostent *he; unsigned long ipaddr; address[0] = '\0'; if ((he = gethostbyname(raddr)) != NULL) { ipaddr = ntohl(*(long *)he->h_addr_list[0]); sprintf(address, "%lu.%lu.%lu.%lu", ipaddr >> 24, (ipaddr & 0x00ff0000) >> 16, (ipaddr & 0x0000ff00) >> 8, (ipaddr & 0x000000ff)); } return address; } struct slip_modes { char *sm_name; int sm_or_flag; int sm_and_flag; } modes[] = { "normal", 0 , 0 , "compress", IFF_LINK0, IFF_LINK2, "noicmp", IFF_LINK1, 0 , "autocomp", IFF_LINK2, IFF_LINK0, }; void findid(name) char *name; { FILE *fp; static char slopt[5][16]; static char laddr[16]; static char mask[16]; char slparmsfile[MAXPATHLEN]; char user[16]; char buf[128]; int i, j, n; environ = restricted_environ; /* minimal protection for system() */ (void)strncpy(loginname, name, sizeof(loginname)-1); loginname[sizeof(loginname)-1] = '\0'; if ((fp = fopen(_PATH_ACCESS, "r")) == NULL) { accfile_err: syslog(LOG_ERR, "%s: %m\n", _PATH_ACCESS); exit(1); } while (fgets(loginargs, sizeof(loginargs) - 1, fp)) { if (ferror(fp)) goto accfile_err; if (loginargs[0] == '#' || isspace(loginargs[0])) continue; n = sscanf(loginargs, "%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s\n", user, laddr, raddr, mask, slopt[0], slopt[1], slopt[2], slopt[3], slopt[4]); if (n < 4) { syslog(LOG_ERR, "%s: wrong format\n", _PATH_ACCESS); exit(1); } if (strcmp(user, name) != 0) continue; (void) fclose(fp); slip_mode = 0; for (i = 0; i < n - 4; i++) { for (j = 0; j < sizeof(modes)/sizeof(struct slip_modes); j++) { if (strcmp(modes[j].sm_name, slopt[i]) == 0) { slip_mode |= (modes[j].sm_or_flag); slip_mode &= ~(modes[j].sm_and_flag); break; } } } /* * we've found the guy we're looking for -- see if * there's a login file we can use. First check for * one specific to this host. If none found, try for * a generic one. */ (void)snprintf(loginfile, sizeof(loginfile), "%s.%s", _PATH_SLIP_LOGIN, name); if (access(loginfile, R_OK|X_OK) != 0) { (void)strncpy(loginfile, _PATH_SLIP_LOGIN, sizeof(loginfile) - 1); loginfile[sizeof(loginfile) - 1] = '\0'; if (access(loginfile, R_OK|X_OK)) { syslog(LOG_ERR, "access denied for %s - no %s\n", name, _PATH_SLIP_LOGIN); exit(5); } } (void)snprintf(slparmsfile, sizeof(slparmsfile), "%s.%s", _PATH_SLPARMS, name); if (access(slparmsfile, R_OK|X_OK) != 0) { (void)strncpy(slparmsfile, _PATH_SLPARMS, sizeof(slparmsfile)-1); slparmsfile[sizeof(slparmsfile)-1] = '\0'; if (access(slparmsfile, R_OK|X_OK)) *slparmsfile = '\0'; } keepal = outfill = 0; slunit = -1; if (*slparmsfile) { if ((fp = fopen(slparmsfile, "r")) == NULL) { slfile_err: syslog(LOG_ERR, "%s: %m\n", slparmsfile); exit(1); } n = 0; while (fgets(buf, sizeof(buf) - 1, fp) != NULL) { if (ferror(fp)) goto slfile_err; if (buf[0] == '#' || isspace(buf[0])) continue; n = sscanf(buf, "%d %d %d", &keepal, &outfill, &slunit); if (n < 1) { slwrong_fmt: syslog(LOG_ERR, "%s: wrong format\n", slparmsfile); exit(1); } (void) fclose(fp); break; } if (n == 0) goto slwrong_fmt; } return; } syslog(LOG_ERR, "SLIP access denied for %s\n", name); exit(4); /* NOTREACHED */ } char * sigstr(s) int s; { static char buf[32]; switch (s) { case SIGHUP: return("HUP"); case SIGINT: return("INT"); case SIGQUIT: return("QUIT"); case SIGILL: return("ILL"); case SIGTRAP: return("TRAP"); case SIGIOT: return("IOT"); case SIGEMT: return("EMT"); case SIGFPE: return("FPE"); case SIGKILL: return("KILL"); case SIGBUS: return("BUS"); case SIGSEGV: return("SEGV"); case SIGSYS: return("SYS"); case SIGPIPE: return("PIPE"); case SIGALRM: return("ALRM"); case SIGTERM: return("TERM"); case SIGURG: return("URG"); case SIGSTOP: return("STOP"); case SIGTSTP: return("TSTP"); case SIGCONT: return("CONT"); case SIGCHLD: return("CHLD"); case SIGTTIN: return("TTIN"); case SIGTTOU: return("TTOU"); case SIGIO: return("IO"); case SIGXCPU: return("XCPU"); case SIGXFSZ: return("XFSZ"); case SIGVTALRM: return("VTALRM"); case SIGPROF: return("PROF"); case SIGWINCH: return("WINCH"); #ifdef SIGLOST case SIGLOST: return("LOST"); #endif case SIGUSR1: return("USR1"); case SIGUSR2: return("USR2"); } (void)snprintf(buf, sizeof(buf), "sig %d", s); return(buf); } void hup_handler(s) int s; { char logoutfile[MAXPATHLEN]; (void) close(0); seteuid(0); (void)snprintf(logoutfile, sizeof(logoutfile), "%s.%s", _PATH_SLIP_LOGOUT, loginname); if (access(logoutfile, R_OK|X_OK) != 0) { (void)strncpy(logoutfile, _PATH_SLIP_LOGOUT, sizeof(logoutfile) - 1); logoutfile[sizeof(logoutfile) - 1] = '\0'; } if (access(logoutfile, R_OK|X_OK) == 0) { char logincmd[2*MAXPATHLEN+32]; (void) snprintf(logincmd, sizeof(logincmd), "%s %d %ld %s", logoutfile, unit, speed, loginargs); (void) system(logincmd); } syslog(LOG_INFO, "closed %s slip unit %d (%s)\n", loginname, unit, sigstr(s)); if (unlink(pidfilename) < 0 && errno != ENOENT) syslog(LOG_WARNING, "unable to delete pid file: %m"); if (unlink(iffilename) < 0 && errno != ENOENT) syslog(LOG_WARNING, "unable to delete if file: %m"); exit(1); /* NOTREACHED */ } /* Modify the slip line mode and add any compression or no-icmp flags. */ void line_flags(unit) int unit; { struct ifreq ifr; int s; /* open a socket as the handle to the interface */ s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { syslog(LOG_ERR, "socket: %m"); exit(1); } sprintf(ifr.ifr_name, "sl%d", unit); /* get the flags for the interface */ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m"); exit(1); } /* Assert any compression or no-icmp flags. */ #define SLMASK (~(IFF_LINK0 | IFF_LINK1 | IFF_LINK2)) ifr.ifr_flags &= SLMASK; ifr.ifr_flags |= slip_mode; if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) { syslog(LOG_ERR, "ioctl (SIOCSIFFLAGS): %m"); exit(1); } close(s); } +int main(argc, argv) int argc; char *argv[]; { int fd, s, ldisc; char *name; struct termios tios, otios; char logincmd[2*BUFSIZ+32]; extern uid_t getuid(); FILE *pidfile; /* pid file */ FILE *iffile; /* interfaces file */ char *p; int n; char devnam[MAXPATHLEN] = _PATH_TTY; /* Device name */ if ((name = strrchr(argv[0], '/')) == NULL) name = argv[0]; s = getdtablesize(); for (fd = 3 ; fd < s ; fd++) (void) close(fd); openlog(name, LOG_PID|LOG_PERROR, LOG_DAEMON); uid = getuid(); if (argc > 1) { findid(argv[1]); /* * Disassociate from current controlling terminal, if any, * and ensure that the slip line is our controlling terminal. */ if (daemon(1, 1)) { syslog(LOG_ERR, "daemon(1, 1): %m"); exit(1); } if (argc > 2) { if ((fd = open(argv[2], O_RDWR)) == -1) { syslog(LOG_ERR, "open %s: %m", argv[2]); exit(2); } (void) dup2(fd, 0); if (fd > 2) close(fd); } if (ioctl(0, TIOCSCTTY, 0) == -1) { syslog(LOG_ERR, "ioctl (TIOCSCTTY): %m"); exit(1); } if (tcsetpgrp(0, getpid()) < 0) { syslog(LOG_ERR, "tcsetpgrp failed: %m"); exit(1); } } else { if ((name = getlogin()) == NULL) { syslog(LOG_ERR, "access denied - login name not found\n"); exit(1); } findid(name); } (void) fchmod(0, 0600); (void) fprintf(stderr, "starting slip login for %s\n", loginname); (void) fprintf(stderr, "your address is %s\n\n", make_ipaddr()); (void) fflush(stderr); sleep(1); /* set up the line parameters */ if (tcgetattr(0, &tios) < 0) { syslog(LOG_ERR, "tcgetattr: %m"); exit(1); } otios = tios; cfmakeraw(&tios); if (tcsetattr(0, TCSAFLUSH, &tios) < 0) { syslog(LOG_ERR, "tcsetattr: %m"); exit(1); } speed = cfgetispeed(&tios); ldisc = SLIPDISC; if (ioctl(0, TIOCSETD, &ldisc) < 0) { syslog(LOG_ERR, "ioctl(TIOCSETD): %m"); exit(1); } if (slunit >= 0 && ioctl(0, SLIOCSUNIT, &slunit) < 0) { syslog(LOG_ERR, "ioctl (SLIOCSUNIT): %m"); exit(1); } /* find out what unit number we were assigned */ if (ioctl(0, SLIOCGUNIT, &unit) < 0) { syslog(LOG_ERR, "ioctl (SLIOCGUNIT): %m"); exit(1); } (void) signal(SIGHUP, hup_handler); (void) signal(SIGTERM, hup_handler); if (keepal > 0) { (void) signal(SIGURG, hup_handler); if (ioctl(0, SLIOCSKEEPAL, &keepal) < 0) { syslog(LOG_ERR, "ioctl(SLIOCSKEEPAL): %m"); exit(1); } } if (outfill > 0 && ioctl(0, SLIOCSOUTFILL, &outfill) < 0) { syslog(LOG_ERR, "ioctl(SLIOCSOUTFILL): %m"); exit(1); } /* write pid to file */ pid = getpid(); (void) sprintf(ifname, "sl%d", unit); (void) sprintf(pidfilename, "%s%s.pid", _PATH_VARRUN, ifname); if ((pidfile = fopen(pidfilename, "w")) != NULL) { fprintf(pidfile, "%d\n", pid); (void) fclose(pidfile); } else { syslog(LOG_ERR, "Failed to create pid file %s: %m", pidfilename); pidfilename[0] = 0; } /* write interface unit number to file */ p = ttyname(0); if (p) strcpy(devnam, p); for (n = strlen(devnam); n > 0; n--) if (devnam[n] == '/') { n++; break; } (void) sprintf(iffilename, "%s%s.if", _PATH_VARRUN, &devnam[n]); if ((iffile = fopen(iffilename, "w")) != NULL) { fprintf(iffile, "sl%d\n", unit); (void) fclose(iffile); } else { syslog(LOG_ERR, "Failed to create if file %s: %m", iffilename); iffilename[0] = 0; } syslog(LOG_INFO, "attaching slip unit %d for %s\n", unit, loginname); (void)snprintf(logincmd, sizeof(logincmd), "%s %d %ld %s", loginfile, unit, speed, loginargs); /* * aim stdout and errout at /dev/null so logincmd output won't * babble into the slip tty line. */ (void) close(1); if ((fd = open(_PATH_DEVNULL, O_WRONLY)) != 1) { if (fd < 0) { syslog(LOG_ERR, "open %s: %m", _PATH_DEVNULL); exit(1); } (void) dup2(fd, 1); (void) close(fd); } (void) dup2(1, 2); /* * Run login and logout scripts as root (real and effective); * current route(8) is setuid root, and checks the real uid * to see whether changes are allowed (or just "route get"). */ (void) setuid(0); if (s = system(logincmd)) { syslog(LOG_ERR, "%s login failed: exit status %d from %s", loginname, s, loginfile); exit(6); } /* Handle any compression or no-icmp flags. */ line_flags(unit); /* reset uid to users' to allow the user to give a signal. */ seteuid(uid); /* twiddle thumbs until we get a signal */ while (1) sigpause(0); /* NOTREACHED */ }