Index: stable/12/contrib/sendmail/mail.local/mail.local.c =================================================================== --- stable/12/contrib/sendmail/mail.local/mail.local.c (revision 353576) +++ stable/12/contrib/sendmail/mail.local/mail.local.c (revision 353577) @@ -1,1959 +1,1959 @@ /* * Copyright (c) 1998-2004 Proofpoint, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. * * $FreeBSD$ * */ #include SM_IDSTR(copyright, "@(#) Copyright (c) 1998-2004 Proofpoint, Inc. and its suppliers.\n\ All rights reserved.\n\ Copyright (c) 1990, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n") SM_IDSTR(id, "@(#)$Id: mail.local.c,v 8.257 2013-11-22 20:51:51 ca Exp $") #include #include #include #include # include # ifdef EX_OK # undef EX_OK /* unistd.h may have another use for this */ # endif /* EX_OK */ # define LOCKFILE_PMODE 0 #include #include #ifndef HASHSPOOL # define HASHSPOOL 0 #endif /* ! HASHSPOOL */ #ifndef HASHSPOOLMD5 # define HASHSPOOLMD5 0 #endif /* ! HASHSPOOLMD5 */ /* ** This is not intended to work on System V derived systems ** such as Solaris or HP-UX, since they use a totally different ** approach to mailboxes (essentially, they have a set-group-ID program ** rather than set-user-ID, and they rely on the ability to "give away" ** files to do their work). IT IS NOT A BUG that this doesn't ** work on such architectures. */ #include #include #include #include #include #include #include # include # include # include # include # include # include #include #include #include #include #include #if HASHSPOOL # define HASH_NONE 0 # define HASH_USER 1 # if HASHSPOOLMD5 # define HASH_MD5 2 # include # endif /* HASHSPOOLMD5 */ #endif /* HASHSPOOL */ #if _FFR_SPOOL_PATH /* ** Override path to mail store at run time (using -p). ** From: Eugene Grosbein of Svyaz Service JSC ** See: http://www.freebsd.org/cgi/query-pr.cgi?pr=bin/114195 ** NOTE: Update man page before adding this to a release. */ #endif /* _FFR_SPOOL_PATH */ #ifndef LOCKTO_RM # define LOCKTO_RM 300 /* timeout for stale lockfile removal */ #endif /* ! LOCKTO_RM */ #ifndef LOCKTO_GLOB # define LOCKTO_GLOB 400 /* global timeout for lockfile creation */ #endif /* ! LOCKTO_GLOB */ /* define a realloc() which works for NULL pointers */ #define REALLOC(ptr, size) (((ptr) == NULL) ? malloc(size) : realloc(ptr, size)) /* ** If you don't have flock, you could try using lockf instead. */ #ifdef LDA_USE_LOCKF # define flock(a, b) lockf(a, b, 0) # ifdef LOCK_EX # undef LOCK_EX # endif /* LOCK_EX */ # define LOCK_EX F_LOCK #endif /* LDA_USE_LOCKF */ #ifndef LOCK_EX # include #endif /* ! LOCK_EX */ /* ** If you don't have setreuid, and you have saved uids, and you have ** a seteuid() call that doesn't try to emulate using setuid(), then ** you can try defining LDA_USE_SETEUID. */ #ifdef LDA_USE_SETEUID # define setreuid(r, e) seteuid(e) #endif /* LDA_USE_SETEUID */ #ifdef LDA_CONTENTLENGTH # define CONTENTLENGTH 1 #endif /* LDA_CONTENTLENGTH */ #ifndef INADDRSZ # define INADDRSZ 4 /* size of an IPv4 address in bytes */ #endif /* ! INADDRSZ */ #ifdef MAILLOCK # include #endif /* MAILLOCK */ #ifndef MAILER_DAEMON # define MAILER_DAEMON "MAILER-DAEMON" #endif /* ! MAILER_DAEMON */ #ifdef CONTENTLENGTH char ContentHdr[40] = "Content-Length: "; off_t HeaderLength; off_t BodyLength; #endif /* CONTENTLENGTH */ bool EightBitMime = true; /* advertise 8BITMIME in LMTP */ char ErrBuf[10240]; /* error buffer */ int ExitVal = EX_OK; /* sysexits.h error value. */ bool nobiff = false; bool nofsync = false; bool HoldErrs = false; /* Hold errors in ErrBuf */ bool LMTPMode = false; bool BounceQuota = false; /* permanent error when over quota */ bool CloseMBDB = false; char *HomeMailFile = NULL; /* store mail in homedir */ #if HASHSPOOL int HashType = HASH_NONE; int HashDepth = 0; bool StripRcptDomain = true; #else /* HASHSPOOL */ # define StripRcptDomain true #endif /* HASHSPOOL */ char SpoolPath[MAXPATHLEN]; char *parseaddr __P((char *, bool)); char *process_recipient __P((char *)); void dolmtp __P((void)); void deliver __P((int, char *)); int e_to_sys __P((int)); void notifybiff __P((char *)); int store __P((char *, bool *)); void usage __P((void)); int lockmbox __P((char *)); void unlockmbox __P((void)); void mailerr __P((const char *, const char *, ...)); void flush_error __P((void)); #if HASHSPOOL const char *hashname __P((char *)); #endif /* HASHSPOOL */ static void sm_exit __P((int)); static void sm_exit(status) int status; { if (CloseMBDB) { sm_mbdb_terminate(); CloseMBDB = false; /* not really necessary, but ... */ } exit(status); } int main(argc, argv) int argc; char *argv[]; { struct passwd *pw; int ch, fd; uid_t uid; char *from; char *mbdbname = "pw"; int err; extern char *optarg; extern int optind; /* make sure we have some open file descriptors */ for (fd = 10; fd < 30; fd++) (void) close(fd); /* use a reasonable umask */ (void) umask(0077); # ifdef LOG_MAIL openlog("mail.local", 0, LOG_MAIL); # else /* LOG_MAIL */ openlog("mail.local", 0); # endif /* LOG_MAIL */ from = NULL; /* XXX can this be converted to a compile time check? */ if (sm_strlcpy(SpoolPath, _PATH_MAILDIR, sizeof(SpoolPath)) >= sizeof(SpoolPath)) { mailerr("421", "Configuration error: _PATH_MAILDIR too large"); sm_exit(EX_CONFIG); } #if HASHSPOOL while ((ch = getopt(argc, argv, "7BbdD:f:h:r:lH:p:ns")) != -1) #else /* HASHSPOOL */ # if _FFR_SPOOL_PATH while ((ch = getopt(argc, argv, "7BbdD:f:h:r:lp:s")) != -1) # else /* _FFR_SPOOL_PATH */ while ((ch = getopt(argc, argv, "7BbdD:f:h:r:ls")) != -1) # endif /* _FFR_SPOOL_PATH */ #endif /* HASHSPOOL */ { switch(ch) { case '7': /* Do not advertise 8BITMIME */ EightBitMime = false; break; case 'B': nobiff = true; break; case 'b': /* bounce mail when over quota. */ BounceQuota = true; break; case 'd': /* Backward compatible. */ break; case 'D': /* mailbox database type */ mbdbname = optarg; break; case 'f': case 'r': /* Backward compatible. */ if (from != NULL) { mailerr(NULL, "Multiple -f options"); usage(); } from = optarg; break; case 'h': if (optarg != NULL || *optarg != '\0') HomeMailFile = optarg; else { mailerr(NULL, "-h: missing filename"); usage(); } break; case 'l': LMTPMode = true; break; case 's': nofsync++; break; #if HASHSPOOL case 'H': if (optarg == NULL || *optarg == '\0') { mailerr(NULL, "-H: missing hashinfo"); usage(); } switch(optarg[0]) { case 'u': HashType = HASH_USER; break; # if HASHSPOOLMD5 case 'm': HashType = HASH_MD5; break; # endif /* HASHSPOOLMD5 */ default: mailerr(NULL, "-H: unknown hash type"); usage(); } if (optarg[1] == '\0') { mailerr(NULL, "-H: invalid hash depth"); usage(); } HashDepth = atoi(&optarg[1]); if ((HashDepth <= 0) || ((HashDepth * 2) >= MAXPATHLEN)) { mailerr(NULL, "-H: invalid hash depth"); usage(); } break; case 'n': StripRcptDomain = false; break; #endif /* HASHSPOOL */ #if HASHSPOOL || _FFR_SPOOL_PATH case 'p': if (optarg == NULL || *optarg == '\0') { mailerr(NULL, "-p: missing spool path"); usage(); } if (sm_strlcpy(SpoolPath, optarg, sizeof(SpoolPath)) >= sizeof(SpoolPath)) { mailerr(NULL, "-p: invalid spool path"); usage(); } break; #endif /* HASHSPOOL || _FFR_SPOOL_PATH */ case '?': default: usage(); } } argc -= optind; argv += optind; /* initialize biff structures */ if (!nobiff) notifybiff(NULL); err = sm_mbdb_initialize(mbdbname); if (err != EX_OK) { char *errcode = "521"; if (err == EX_TEMPFAIL) errcode = "421"; mailerr(errcode, "Can not open mailbox database %s: %s", mbdbname, sm_strexit(err)); sm_exit(err); } CloseMBDB = true; if (LMTPMode) { if (argc > 0) { mailerr("421", "Users should not be specified in command line if LMTP required"); sm_exit(EX_TEMPFAIL); } dolmtp(); /* NOTREACHED */ sm_exit(EX_OK); } /* Non-LMTP from here on out */ - if (*argv == '\0') + if (*argv == NULL) usage(); /* ** If from not specified, use the name from getlogin() if the ** uid matches, otherwise, use the name from the password file ** corresponding to the uid. */ uid = getuid(); if (from == NULL && ((from = getlogin()) == NULL || (pw = getpwnam(from)) == NULL || pw->pw_uid != uid)) from = (pw = getpwuid(uid)) != NULL ? pw->pw_name : "???"; /* ** There is no way to distinguish the error status of one delivery ** from the rest of the deliveries. So, if we failed hard on one ** or more deliveries, but had no failures on any of the others, we ** return a hard failure. If we failed temporarily on one or more ** deliveries, we return a temporary failure regardless of the other ** failures. This results in the delivery being reattempted later ** at the expense of repeated failures and multiple deliveries. */ HoldErrs = true; fd = store(from, NULL); HoldErrs = false; if (fd < 0) { flush_error(); sm_exit(ExitVal); } for (; *argv != NULL; ++argv) deliver(fd, *argv); sm_exit(ExitVal); /* NOTREACHED */ return ExitVal; } char * parseaddr(s, rcpt) char *s; bool rcpt; { char *p; int l; if (*s++ != '<') return NULL; p = s; /* at-domain-list */ while (*p == '@') { p++; while (*p != ',' && *p != ':' && *p != '\0') p++; if (*p == '\0') return NULL; /* Skip over , or : */ p++; } s = p; /* local-part */ while (*p != '\0' && *p != '@' && *p != '>') { if (*p == '\\') { if (*++p == '\0') return NULL; } else if (*p == '\"') { p++; while (*p != '\0' && *p != '\"') { if (*p == '\\') { if (*++p == '\0') return NULL; } p++; } if (*p == '\0' || *(p + 1) == '\0') return NULL; } /* +detail ? */ if (*p == '+' && rcpt) *p = '\0'; p++; } /* @domain */ if (*p == '@') { if (rcpt) *p++ = '\0'; while (*p != '\0' && *p != '>') p++; } if (*p != '>') return NULL; else *p = '\0'; p++; if (*p != '\0' && *p != ' ') return NULL; if (*s == '\0') s = MAILER_DAEMON; l = strlen(s) + 1; if (l < 0) return NULL; p = malloc(l); if (p == NULL) { mailerr("421 4.3.0", "Memory exhausted"); sm_exit(EX_TEMPFAIL); } (void) sm_strlcpy(p, s, l); return p; } char * process_recipient(addr) char *addr; { SM_MBDB_T user; switch (sm_mbdb_lookup(addr, &user)) { case EX_OK: return NULL; case EX_NOUSER: return "550 5.1.1 User unknown"; case EX_TEMPFAIL: return "451 4.3.0 User database failure; retry later"; default: return "550 5.3.0 User database failure"; } } #define RCPT_GROW 30 void dolmtp() { char *return_path = NULL; char **rcpt_addr = NULL; int rcpt_num = 0; int rcpt_alloc = 0; bool gotlhlo = false; char *err; int msgfd; char *p; int i; char myhostname[1024]; char buf[4096]; memset(myhostname, '\0', sizeof myhostname); (void) gethostname(myhostname, sizeof myhostname - 1); if (myhostname[0] == '\0') sm_strlcpy(myhostname, "localhost", sizeof myhostname); printf("220 %s LMTP ready\r\n", myhostname); for (;;) { (void) fflush(stdout); if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) sm_exit(EX_OK); p = buf + strlen(buf) - 1; if (p >= buf && *p == '\n') *p-- = '\0'; if (p >= buf && *p == '\r') *p-- = '\0'; switch (buf[0]) { case 'd': case 'D': if (sm_strcasecmp(buf, "data") == 0) { bool inbody = false; if (rcpt_num == 0) { mailerr("503 5.5.1", "No recipients"); continue; } HoldErrs = true; msgfd = store(return_path, &inbody); HoldErrs = false; if (msgfd < 0 && !inbody) { flush_error(); continue; } for (i = 0; i < rcpt_num; i++) { if (msgfd < 0) { /* print error for rcpt */ flush_error(); continue; } p = strchr(rcpt_addr[i], '+'); if (p != NULL) *p = '\0'; deliver(msgfd, rcpt_addr[i]); } if (msgfd >= 0) (void) close(msgfd); goto rset; } goto syntaxerr; /* NOTREACHED */ break; case 'l': case 'L': if (sm_strncasecmp(buf, "lhlo ", 5) == 0) { /* check for duplicate per RFC 1651 4.2 */ if (gotlhlo) { mailerr("503", "%s Duplicate LHLO", myhostname); continue; } gotlhlo = true; printf("250-%s\r\n", myhostname); if (EightBitMime) printf("250-8BITMIME\r\n"); printf("250-ENHANCEDSTATUSCODES\r\n"); printf("250 PIPELINING\r\n"); continue; } goto syntaxerr; /* NOTREACHED */ break; case 'm': case 'M': if (sm_strncasecmp(buf, "mail ", 5) == 0) { if (return_path != NULL) { mailerr("503 5.5.1", "Nested MAIL command"); continue; } if (sm_strncasecmp(buf + 5, "from:", 5) != 0 || ((return_path = parseaddr(buf + 10, false)) == NULL)) { mailerr("501 5.5.4", "Syntax error in parameters"); continue; } printf("250 2.5.0 Ok\r\n"); continue; } goto syntaxerr; /* NOTREACHED */ break; case 'n': case 'N': if (sm_strcasecmp(buf, "noop") == 0) { printf("250 2.0.0 Ok\r\n"); continue; } goto syntaxerr; /* NOTREACHED */ break; case 'q': case 'Q': if (sm_strcasecmp(buf, "quit") == 0) { printf("221 2.0.0 Bye\r\n"); sm_exit(EX_OK); } goto syntaxerr; /* NOTREACHED */ break; case 'r': case 'R': if (sm_strncasecmp(buf, "rcpt ", 5) == 0) { if (return_path == NULL) { mailerr("503 5.5.1", "Need MAIL command"); continue; } if (rcpt_num >= rcpt_alloc) { rcpt_alloc += RCPT_GROW; rcpt_addr = (char **) REALLOC((char *) rcpt_addr, rcpt_alloc * sizeof(char **)); if (rcpt_addr == NULL) { mailerr("421 4.3.0", "Memory exhausted"); sm_exit(EX_TEMPFAIL); } } if (sm_strncasecmp(buf + 5, "to:", 3) != 0 || ((rcpt_addr[rcpt_num] = parseaddr(buf + 8, StripRcptDomain)) == NULL)) { mailerr("501 5.5.4", "Syntax error in parameters"); continue; } err = process_recipient(rcpt_addr[rcpt_num]); if (err != NULL) { mailerr(NULL, "%s", err); continue; } rcpt_num++; printf("250 2.1.5 Ok\r\n"); continue; } else if (sm_strcasecmp(buf, "rset") == 0) { printf("250 2.0.0 Ok\r\n"); rset: while (rcpt_num > 0) free(rcpt_addr[--rcpt_num]); if (return_path != NULL) free(return_path); return_path = NULL; continue; } goto syntaxerr; /* NOTREACHED */ break; case 'v': case 'V': if (sm_strncasecmp(buf, "vrfy ", 5) == 0) { printf("252 2.3.3 Try RCPT to attempt delivery\r\n"); continue; } goto syntaxerr; /* NOTREACHED */ break; default: syntaxerr: mailerr("500 5.5.2", "Syntax error"); continue; /* NOTREACHED */ break; } } } int store(from, inbody) char *from; bool *inbody; { FILE *fp = NULL; time_t tval; bool eline; /* previous line was empty */ bool fullline = true; /* current line is terminated */ bool prevfl; /* previous line was terminated */ char line[2048]; int fd; char tmpbuf[sizeof _PATH_LOCTMP + 1]; if (inbody != NULL) *inbody = false; (void) umask(0077); (void) sm_strlcpy(tmpbuf, _PATH_LOCTMP, sizeof tmpbuf); if ((fd = mkstemp(tmpbuf)) < 0 || (fp = fdopen(fd, "w+")) == NULL) { if (fd >= 0) (void) close(fd); mailerr("451 4.3.0", "Unable to open temporary file"); return -1; } (void) unlink(tmpbuf); if (LMTPMode) { printf("354 Go ahead\r\n"); (void) fflush(stdout); } if (inbody != NULL) *inbody = true; (void) time(&tval); (void) fprintf(fp, "From %s %s", from, ctime(&tval)); #ifdef CONTENTLENGTH HeaderLength = 0; BodyLength = -1; #endif /* CONTENTLENGTH */ line[0] = '\0'; eline = true; while (fgets(line, sizeof(line), stdin) != (char *) NULL) { size_t line_len = 0; int peek; prevfl = fullline; /* preserve state of previous line */ while (line[line_len] != '\n' && line_len < sizeof(line) - 2) line_len++; line_len++; /* Check for dot-stuffing */ if (prevfl && LMTPMode && line[0] == '.') { if (line[1] == '\n' || (line[1] == '\r' && line[2] == '\n')) goto lmtpdot; memcpy(line, line + 1, line_len); line_len--; } /* Check to see if we have the full line from fgets() */ fullline = false; if (line_len > 0) { if (line[line_len - 1] == '\n') { if (line_len >= 2 && line[line_len - 2] == '\r') { line[line_len - 2] = '\n'; line[line_len - 1] = '\0'; line_len--; } fullline = true; } else if (line[line_len - 1] == '\r') { /* Did we just miss the CRLF? */ peek = fgetc(stdin); if (peek == '\n') { line[line_len - 1] = '\n'; fullline = true; } else (void) ungetc(peek, stdin); } } else fullline = true; #ifdef CONTENTLENGTH if (prevfl && line[0] == '\n' && HeaderLength == 0) { eline = false; if (fp != NULL) HeaderLength = ftell(fp); if (HeaderLength <= 0) { /* ** shouldn't happen, unless ftell() is ** badly broken */ HeaderLength = -1; } } #else /* CONTENTLENGTH */ if (prevfl && line[0] == '\n') eline = true; #endif /* CONTENTLENGTH */ else { if (eline && line[0] == 'F' && fp != NULL && !memcmp(line, "From ", 5)) (void) putc('>', fp); eline = false; #ifdef CONTENTLENGTH /* discard existing "Content-Length:" headers */ if (prevfl && HeaderLength == 0 && (line[0] == 'C' || line[0] == 'c') && sm_strncasecmp(line, ContentHdr, 15) == 0) { /* ** be paranoid: clear the line ** so no "wrong matches" may occur later */ line[0] = '\0'; continue; } #endif /* CONTENTLENGTH */ } if (fp != NULL) { (void) fwrite(line, sizeof(char), line_len, fp); if (ferror(fp)) { mailerr("451 4.3.0", "Temporary file write error"); (void) fclose(fp); fp = NULL; continue; } } } /* check if an error occurred */ if (fp == NULL) return -1; if (LMTPMode) { /* Got a premature EOF -- toss message and exit */ sm_exit(EX_OK); } /* If message not newline terminated, need an extra. */ if (fp != NULL && strchr(line, '\n') == NULL) (void) putc('\n', fp); lmtpdot: #ifdef CONTENTLENGTH if (fp != NULL) BodyLength = ftell(fp); if (HeaderLength == 0 && BodyLength > 0) /* empty body */ { HeaderLength = BodyLength; BodyLength = 0; } else BodyLength = BodyLength - HeaderLength - 1 ; if (HeaderLength > 0 && BodyLength >= 0) { (void) sm_snprintf(line, sizeof line, "%lld\n", (LONGLONG_T) BodyLength); (void) sm_strlcpy(&ContentHdr[16], line, sizeof(ContentHdr) - 16); } else BodyLength = -1; /* Something is wrong here */ #endif /* CONTENTLENGTH */ /* Output a newline; note, empty messages are allowed. */ if (fp != NULL) (void) putc('\n', fp); if (fp == NULL || fflush(fp) == EOF || ferror(fp) != 0) { mailerr("451 4.3.0", "Temporary file write error"); if (fp != NULL) (void) fclose(fp); return -1; } return fd; } void deliver(fd, name) int fd; char *name; { struct stat fsb; struct stat sb; char path[MAXPATHLEN]; int mbfd = -1, nr = 0, nw, off; int exitval; char *p; char *errcode; off_t curoff, cursize; #ifdef CONTENTLENGTH off_t headerbytes; int readamount; #endif /* CONTENTLENGTH */ char biffmsg[100], buf[8 * 1024]; SM_MBDB_T user; /* ** Disallow delivery to unknown names -- special mailboxes can be ** handled in the sendmail aliases file. */ exitval = sm_mbdb_lookup(name, &user); switch (exitval) { case EX_OK: break; case EX_NOUSER: exitval = EX_UNAVAILABLE; mailerr("550 5.1.1", "%s: User unknown", name); break; case EX_TEMPFAIL: mailerr("451 4.3.0", "%s: User database failure; retry later", name); break; default: exitval = EX_UNAVAILABLE; mailerr("550 5.3.0", "%s: User database failure", name); break; } if (exitval != EX_OK) { if (ExitVal != EX_TEMPFAIL) ExitVal = exitval; return; } endpwent(); /* ** Keep name reasonably short to avoid buffer overruns. ** This isn't necessary on BSD because of the proper ** definition of snprintf(), but it can cause problems ** on other systems. ** Also, clear out any bogus characters. */ #if !HASHSPOOL if (strlen(name) > 40) name[40] = '\0'; for (p = name; *p != '\0'; p++) { if (!isascii(*p)) *p &= 0x7f; else if (!isprint(*p)) *p = '.'; } #endif /* !HASHSPOOL */ if (HomeMailFile == NULL) { if (sm_strlcpyn(path, sizeof(path), #if HASHSPOOL 4, #else /* HASHSPOOL */ 3, #endif /* HASHSPOOL */ SpoolPath, "/", #if HASHSPOOL hashname(name), #endif /* HASHSPOOL */ name) >= sizeof(path)) { exitval = EX_UNAVAILABLE; mailerr("550 5.1.1", "%s: Invalid mailbox path", name); return; } } else if (*user.mbdb_homedir == '\0') { exitval = EX_UNAVAILABLE; mailerr("550 5.1.1", "%s: User missing home directory", name); return; } else if (sm_snprintf(path, sizeof(path), "%s/%s", user.mbdb_homedir, HomeMailFile) >= sizeof(path)) { exitval = EX_UNAVAILABLE; mailerr("550 5.1.1", "%s: Invalid mailbox path", name); return; } /* ** If the mailbox is linked or a symlink, fail. There's an obvious ** race here, that the file was replaced with a symbolic link after ** the lstat returned, but before the open. We attempt to detect ** this by comparing the original stat information and information ** returned by an fstat of the file descriptor returned by the open. ** ** NB: this is a symptom of a larger problem, that the mail spooling ** directory is writeable by the wrong users. If that directory is ** writeable, system security is compromised for other reasons, and ** it cannot be fixed here. ** ** If we created the mailbox, set the owner/group. If that fails, ** just return. Another process may have already opened it, so we ** can't unlink it. Historically, binmail set the owner/group at ** each mail delivery. We no longer do this, assuming that if the ** ownership or permissions were changed there was a reason. ** ** XXX ** open(2) should support flock'ing the file. */ tryagain: #ifdef MAILLOCK p = name; #else /* MAILLOCK */ p = path; #endif /* MAILLOCK */ if ((off = lockmbox(p)) != 0) { if (off == EX_TEMPFAIL || e_to_sys(off) == EX_TEMPFAIL) { ExitVal = EX_TEMPFAIL; errcode = "451 4.3.0"; } else errcode = "551 5.3.0"; mailerr(errcode, "lockmailbox %s failed; error code %d %s", p, off, errno > 0 ? sm_errstring(errno) : ""); return; } if (lstat(path, &sb) < 0) { int save_errno; int mode = S_IRUSR|S_IWUSR; gid_t gid = user.mbdb_gid; #ifdef MAILGID (void) umask(0007); gid = MAILGID; mode |= S_IRGRP|S_IWGRP; #endif /* MAILGID */ mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY, mode); save_errno = errno; if (lstat(path, &sb) < 0) { ExitVal = EX_CANTCREAT; mailerr("550 5.2.0", "%s: lstat: file changed after open", path); goto err1; } if (mbfd < 0) { if (save_errno == EEXIST) goto tryagain; /* open failed, don't try again */ mailerr("450 4.2.0", "%s: %s", path, sm_errstring(save_errno)); goto err0; } else if (fchown(mbfd, user.mbdb_uid, gid) < 0) { mailerr("451 4.3.0", "chown %u.%u: %s", user.mbdb_uid, gid, name); goto err1; } else { /* ** open() was successful, now close it so can ** be opened as the right owner again. ** Paranoia: reset mbdf since the file descriptor ** is no longer valid; better safe than sorry. */ sb.st_uid = user.mbdb_uid; (void) close(mbfd); mbfd = -1; } } else if (sb.st_nlink != 1) { mailerr("550 5.2.0", "%s: too many links", path); goto err0; } else if (!S_ISREG(sb.st_mode)) { mailerr("550 5.2.0", "%s: irregular file", path); goto err0; } else if (sb.st_uid != user.mbdb_uid) { ExitVal = EX_CANTCREAT; mailerr("550 5.2.0", "%s: wrong ownership (%d)", path, (int) sb.st_uid); goto err0; } /* change UID for quota checks */ if (setreuid(0, user.mbdb_uid) < 0) { mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)", (int) user.mbdb_uid, sm_errstring(errno), (int) getuid(), (int) geteuid()); goto err1; } #ifdef DEBUG fprintf(stderr, "new euid = %d\n", (int) geteuid()); #endif /* DEBUG */ mbfd = open(path, O_APPEND|O_WRONLY, 0); if (mbfd < 0) { mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); goto err0; } else if (fstat(mbfd, &fsb) < 0 || fsb.st_nlink != 1 || sb.st_nlink != 1 || !S_ISREG(fsb.st_mode) || sb.st_dev != fsb.st_dev || sb.st_ino != fsb.st_ino || # if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */ sb.st_gen != fsb.st_gen || # endif /* HAS_ST_GEN && 0 */ sb.st_uid != fsb.st_uid) { ExitVal = EX_TEMPFAIL; mailerr("550 5.2.0", "%s: fstat: file changed after open", path); goto err1; } #if 0 /* ** This code could be reused if we decide to add a ** per-user quota field to the sm_mbdb interface. */ /* ** Fail if the user has a quota specified, and delivery of this ** message would exceed that quota. We bounce such failures using ** EX_UNAVAILABLE, unless there were internal problems, since ** storing immense messages for later retries can cause queueing ** issues. */ if (ui.quota > 0) { struct stat dsb; if (fstat(fd, &dsb) < 0) { ExitVal = EX_TEMPFAIL; mailerr("451 4.3.0", "%s: fstat: can't stat temporary storage: %s", ui.mailspool, sm_errstring(errno)); goto err1; } if (dsb.st_size + sb.st_size + 1 > ui.quota) { ExitVal = EX_UNAVAILABLE; mailerr("551 5.2.2", "%s: Mailbox full or quota exceeded", ui.mailspool); goto err1; } } #endif /* 0 */ /* Wait until we can get a lock on the file. */ if (flock(mbfd, LOCK_EX) < 0) { mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); goto err1; } /* Get the starting offset of the new message */ curoff = lseek(mbfd, (off_t) 0, SEEK_END); if (!nobiff) { (void) sm_snprintf(biffmsg, sizeof(biffmsg), "%s@%lld\n", name, (LONGLONG_T) curoff); } /* Copy the message into the file. */ if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1) { mailerr("450 4.2.0", "Temporary file: %s", sm_errstring(errno)); goto err1; } #ifdef DEBUG fprintf(stderr, "before writing: euid = %d\n", (int) geteuid()); #endif /* DEBUG */ #ifdef CONTENTLENGTH headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ; for (;;) { if (headerbytes == 0) { (void) sm_snprintf(buf, sizeof buf, "%s", ContentHdr); nr = strlen(buf); headerbytes = -1; readamount = 0; } else if (headerbytes > sizeof(buf) || headerbytes < 0) readamount = sizeof(buf); else readamount = headerbytes; if (readamount != 0) nr = read(fd, buf, readamount); if (nr <= 0) break; if (headerbytes > 0) headerbytes -= nr ; #else /* CONTENTLENGTH */ while ((nr = read(fd, buf, sizeof(buf))) > 0) { #endif /* CONTENTLENGTH */ for (off = 0; off < nr; off += nw) { if ((nw = write(mbfd, buf + off, nr - off)) < 0) { errcode = "450 4.2.0"; #ifdef EDQUOT if (errno == EDQUOT && BounceQuota) errcode = "552 5.2.2"; #endif /* EDQUOT */ mailerr(errcode, "%s: %s", path, sm_errstring(errno)); goto err3; } } } if (nr < 0) { mailerr("450 4.2.0", "Temporary file: %s", sm_errstring(errno)); goto err3; } /* Flush to disk, don't wait for update. */ if (!nofsync && fsync(mbfd) < 0) { mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); err3: #ifdef DEBUG fprintf(stderr, "reset euid = %d\n", (int) geteuid()); #endif /* DEBUG */ if (mbfd >= 0) (void) ftruncate(mbfd, curoff); err1: if (mbfd >= 0) (void) close(mbfd); err0: (void) setreuid(0, 0); unlockmbox(); return; } /* ** Save the current size so if the close() fails below ** we can make sure no other process has changed the mailbox ** between the failed close and the re-open()/re-lock(). ** If something else has changed the size, we shouldn't ** try to truncate it as we may do more harm then good ** (e.g., truncate a later message delivery). */ if (fstat(mbfd, &sb) < 0) cursize = 0; else cursize = sb.st_size; /* Close and check -- NFS doesn't write until the close. */ if (close(mbfd)) { errcode = "450 4.2.0"; #ifdef EDQUOT if (errno == EDQUOT && BounceQuota) errcode = "552 5.2.2"; #endif /* EDQUOT */ mailerr(errcode, "%s: %s", path, sm_errstring(errno)); mbfd = open(path, O_WRONLY, 0); if (mbfd < 0 || cursize == 0 || flock(mbfd, LOCK_EX) < 0 || fstat(mbfd, &sb) < 0 || sb.st_size != cursize || sb.st_nlink != 1 || !S_ISREG(sb.st_mode) || sb.st_dev != fsb.st_dev || sb.st_ino != fsb.st_ino || # if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */ sb.st_gen != fsb.st_gen || # endif /* HAS_ST_GEN && 0 */ sb.st_uid != fsb.st_uid ) { /* Don't use a bogus file */ if (mbfd >= 0) { (void) close(mbfd); mbfd = -1; } } /* Attempt to truncate back to pre-write size */ goto err3; } else if (!nobiff) notifybiff(biffmsg); if (setreuid(0, 0) < 0) { mailerr("450 4.2.0", "setreuid(0, 0): %s", sm_errstring(errno)); goto err0; } #ifdef DEBUG fprintf(stderr, "reset euid = %d\n", (int) geteuid()); #endif /* DEBUG */ unlockmbox(); if (LMTPMode) printf("250 2.1.5 %s Ok\r\n", name); } /* ** user.lock files are necessary for compatibility with other ** systems, e.g., when the mail spool file is NFS exported. ** Alas, mailbox locking is more than just a local matter. ** EPA 11/94. */ bool Locked = false; #ifdef MAILLOCK int lockmbox(name) char *name; { int r = 0; if (Locked) return 0; if ((r = maillock(name, 15)) == L_SUCCESS) { Locked = true; return 0; } switch (r) { case L_TMPLOCK: /* Can't create tmp file */ case L_TMPWRITE: /* Can't write pid into lockfile */ case L_MAXTRYS: /* Failed after retrycnt attempts */ errno = 0; r = EX_TEMPFAIL; break; case L_ERROR: /* Check errno for reason */ r = errno; break; default: /* other permanent errors */ errno = 0; r = EX_UNAVAILABLE; break; } return r; } void unlockmbox() { if (Locked) mailunlock(); Locked = false; } #else /* MAILLOCK */ char LockName[MAXPATHLEN]; int lockmbox(path) char *path; { int statfailed = 0; time_t start; if (Locked) return 0; if (strlen(path) + 6 > sizeof LockName) return EX_SOFTWARE; (void) sm_snprintf(LockName, sizeof LockName, "%s.lock", path); (void) time(&start); for (; ; sleep(5)) { int fd; struct stat st; time_t now; /* global timeout */ (void) time(&now); if (now > start + LOCKTO_GLOB) { errno = 0; return EX_TEMPFAIL; } fd = open(LockName, O_WRONLY|O_EXCL|O_CREAT, LOCKFILE_PMODE); if (fd >= 0) { /* defeat lock checking programs which test pid */ (void) write(fd, "0", 2); Locked = true; (void) close(fd); return 0; } if (stat(LockName, &st) < 0) { if (statfailed++ > 5) { errno = 0; return EX_TEMPFAIL; } continue; } statfailed = 0; (void) time(&now); if (now < st.st_ctime + LOCKTO_RM) continue; /* try to remove stale lockfile */ if (unlink(LockName) < 0) return errno; } } void unlockmbox() { if (!Locked) return; (void) unlink(LockName); Locked = false; } #endif /* MAILLOCK */ void notifybiff(msg) char *msg; { static bool initialized = false; static int f = -1; struct hostent *hp; struct servent *sp; int len; static struct sockaddr_in addr; if (!initialized) { initialized = true; /* Be silent if biff service not available. */ if ((sp = getservbyname("biff", "udp")) == NULL || (hp = gethostbyname("localhost")) == NULL || hp->h_length != INADDRSZ) return; addr.sin_family = hp->h_addrtype; memcpy(&addr.sin_addr, hp->h_addr, INADDRSZ); addr.sin_port = sp->s_port; } /* No message, just return */ if (msg == NULL) return; /* Couldn't initialize addr struct */ if (addr.sin_family == AF_UNSPEC) return; if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return; len = strlen(msg) + 1; (void) sendto(f, msg, len, 0, (struct sockaddr *) &addr, sizeof(addr)); } void usage() { ExitVal = EX_USAGE; # if _FFR_SPOOL_PATH mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] [-p path] user ..."); # else /* _FFR_SPOOL_PATH */ mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] user ..."); # endif /* _FFR_SPOOL_PATH */ sm_exit(ExitVal); } void /*VARARGS2*/ #ifdef __STDC__ mailerr(const char *hdr, const char *fmt, ...) #else /* __STDC__ */ mailerr(hdr, fmt, va_alist) const char *hdr; const char *fmt; va_dcl #endif /* __STDC__ */ { size_t len = 0; SM_VA_LOCAL_DECL (void) e_to_sys(errno); SM_VA_START(ap, fmt); if (LMTPMode && hdr != NULL) { sm_snprintf(ErrBuf, sizeof ErrBuf, "%s ", hdr); len = strlen(ErrBuf); } (void) sm_vsnprintf(&ErrBuf[len], sizeof ErrBuf - len, fmt, ap); SM_VA_END(ap); if (!HoldErrs) flush_error(); /* Log the message to syslog. */ if (!LMTPMode) syslog(LOG_ERR, "%s", ErrBuf); } void flush_error() { if (LMTPMode) printf("%s\r\n", ErrBuf); else { if (ExitVal != EX_USAGE) (void) fprintf(stderr, "mail.local: "); fprintf(stderr, "%s\n", ErrBuf); } } #if HASHSPOOL const char * hashname(name) char *name; { static char p[MAXPATHLEN]; int i; int len; char *str; # if HASHSPOOLMD5 char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_"; MD5_CTX ctx; unsigned char md5[18]; # if MAXPATHLEN <= 24 ERROR _MAXPATHLEN <= 24 # endif /* MAXPATHLEN <= 24 */ char b64[24]; MD5_LONG bits; int j; # endif /* HASHSPOOLMD5 */ if (HashType == HASH_NONE || HashDepth * 2 >= MAXPATHLEN) { p[0] = '\0'; return p; } switch(HashType) { case HASH_USER: str = name; break; # if HASHSPOOLMD5 case HASH_MD5: MD5_Init(&ctx); MD5_Update(&ctx, name, strlen(name)); MD5_Final(md5, &ctx); md5[16] = 0; md5[17] = 0; for (i = 0; i < 6; i++) { bits = (unsigned) md5[(3 * i)] << 16; bits |= (unsigned) md5[(3 * i) + 1] << 8; bits |= (unsigned) md5[(3 * i) + 2]; for (j = 3; j >= 0; j--) { b64[(4 * i) + j] = Base64[(bits & 0x3f)]; bits >>= 6; } } b64[22] = '\0'; str = b64; break; # endif /* HASHSPOOLMD5 */ } len = strlen(str); for (i = 0; i < HashDepth; i++) { if (i < len) p[i * 2] = str[i]; else p[i * 2] = '_'; p[(i * 2) + 1] = '/'; } p[HashDepth * 2] = '\0'; return p; } #endif /* HASHSPOOL */ /* * e_to_sys -- * Guess which errno's are temporary. Gag me. */ int e_to_sys(num) int num; { /* Temporary failures override hard errors. */ if (ExitVal == EX_TEMPFAIL) return ExitVal; switch (num) /* Hopefully temporary errors. */ { #ifdef EDQUOT case EDQUOT: /* Disc quota exceeded */ if (BounceQuota) { ExitVal = EX_UNAVAILABLE; break; } /* FALLTHROUGH */ #endif /* EDQUOT */ #ifdef EAGAIN case EAGAIN: /* Resource temporarily unavailable */ #endif /* EAGAIN */ #ifdef EBUSY case EBUSY: /* Device busy */ #endif /* EBUSY */ #ifdef EPROCLIM case EPROCLIM: /* Too many processes */ #endif /* EPROCLIM */ #ifdef EUSERS case EUSERS: /* Too many users */ #endif /* EUSERS */ #ifdef ECONNABORTED case ECONNABORTED: /* Software caused connection abort */ #endif /* ECONNABORTED */ #ifdef ECONNREFUSED case ECONNREFUSED: /* Connection refused */ #endif /* ECONNREFUSED */ #ifdef ECONNRESET case ECONNRESET: /* Connection reset by peer */ #endif /* ECONNRESET */ #ifdef EDEADLK case EDEADLK: /* Resource deadlock avoided */ #endif /* EDEADLK */ #ifdef EFBIG case EFBIG: /* File too large */ #endif /* EFBIG */ #ifdef EHOSTDOWN case EHOSTDOWN: /* Host is down */ #endif /* EHOSTDOWN */ #ifdef EHOSTUNREACH case EHOSTUNREACH: /* No route to host */ #endif /* EHOSTUNREACH */ #ifdef EMFILE case EMFILE: /* Too many open files */ #endif /* EMFILE */ #ifdef ENETDOWN case ENETDOWN: /* Network is down */ #endif /* ENETDOWN */ #ifdef ENETRESET case ENETRESET: /* Network dropped connection on reset */ #endif /* ENETRESET */ #ifdef ENETUNREACH case ENETUNREACH: /* Network is unreachable */ #endif /* ENETUNREACH */ #ifdef ENFILE case ENFILE: /* Too many open files in system */ #endif /* ENFILE */ #ifdef ENOBUFS case ENOBUFS: /* No buffer space available */ #endif /* ENOBUFS */ #ifdef ENOMEM case ENOMEM: /* Cannot allocate memory */ #endif /* ENOMEM */ #ifdef ENOSPC case ENOSPC: /* No space left on device */ #endif /* ENOSPC */ #ifdef EROFS case EROFS: /* Read-only file system */ #endif /* EROFS */ #ifdef ESTALE case ESTALE: /* Stale NFS file handle */ #endif /* ESTALE */ #ifdef ETIMEDOUT case ETIMEDOUT: /* Connection timed out */ #endif /* ETIMEDOUT */ #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK case EWOULDBLOCK: /* Operation would block. */ #endif /* defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK */ ExitVal = EX_TEMPFAIL; break; default: ExitVal = EX_UNAVAILABLE; break; } return ExitVal; } #if defined(ultrix) || defined(_CRAY) /* * Copyright (c) 1987, 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. */ # if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; # endif /* defined(LIBC_SCCS) && !defined(lint) */ # include # include # include # include # include # include static int _gettemp(); mkstemp(path) char *path; { int fd; return (_gettemp(path, &fd) ? fd : -1); } static _gettemp(path, doopen) char *path; register int *doopen; { extern int errno; register char *start, *trv; struct stat sbuf; unsigned int pid; pid = getpid(); for (trv = path; *trv; ++trv); /* extra X's get set to 0's */ while (*--trv == 'X') { *trv = (pid % 10) + '0'; pid /= 10; } /* * check the target directory; if you have six X's and it * doesn't exist this runs for a *very* long time. */ for (start = trv + 1;; --trv) { if (trv <= path) break; if (*trv == '/') { *trv = '\0'; if (stat(path, &sbuf) < 0) return(0); if (!S_ISDIR(sbuf.st_mode)) { errno = ENOTDIR; return(0); } *trv = '/'; break; } } for (;;) { if (doopen) { if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0) return(1); if (errno != EEXIST) return(0); } else if (stat(path, &sbuf) < 0) return(errno == ENOENT ? 1 : 0); /* tricky little algorithm for backward compatibility */ for (trv = start;;) { if (!*trv) return(0); if (*trv == 'z') *trv++ = 'a'; else { if (isascii(*trv) && isdigit(*trv)) *trv = 'a'; else ++*trv; break; } } } /* NOTREACHED */ } #endif /* defined(ultrix) || defined(_CRAY) */ Index: stable/12/lib/libc/tests/nss/getgr_test.c =================================================================== --- stable/12/lib/libc/tests/nss/getgr_test.c (revision 353576) +++ stable/12/lib/libc/tests/nss/getgr_test.c (revision 353577) @@ -1,544 +1,544 @@ /*- * Copyright (c) 2006 Michael Bushkov * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "testutil.h" enum test_methods { TEST_GETGRENT = 1, TEST_GETGRNAM = 2, TEST_GETGRGID = 4, TEST_GETGRENT_2PASS = 8, TEST_BUILD_SNAPSHOT = 16, }; DECLARE_TEST_DATA(group) DECLARE_TEST_FILE_SNAPSHOT(group) DECLARE_1PASS_TEST(group) DECLARE_2PASS_TEST(group) static void clone_group(struct group *, struct group const *); static int compare_group(struct group *, struct group *, void *); static void dump_group(struct group *); static void free_group(struct group *); static void sdump_group(struct group *, char *, size_t); static int group_read_snapshot_func(struct group *, char *); static int group_check_ambiguity(struct group_test_data *, struct group *); static int group_fill_test_data(struct group_test_data *); static int group_test_correctness(struct group *, void *); static int group_test_getgrnam(struct group *, void *); static int group_test_getgrgid(struct group *, void *); static int group_test_getgrent(struct group *, void *); IMPLEMENT_TEST_DATA(group) IMPLEMENT_TEST_FILE_SNAPSHOT(group) IMPLEMENT_1PASS_TEST(group) IMPLEMENT_2PASS_TEST(group) static void clone_group(struct group *dest, struct group const *src) { ATF_REQUIRE(dest != NULL); ATF_REQUIRE(src != NULL); char **cp; int members_num; memset(dest, 0, sizeof(struct group)); if (src->gr_name != NULL) { dest->gr_name = strdup(src->gr_name); ATF_REQUIRE(dest->gr_name != NULL); } if (src->gr_passwd != NULL) { dest->gr_passwd = strdup(src->gr_passwd); ATF_REQUIRE(dest->gr_passwd != NULL); } dest->gr_gid = src->gr_gid; if (src->gr_mem != NULL) { members_num = 0; for (cp = src->gr_mem; *cp; ++cp) ++members_num; dest->gr_mem = calloc(members_num + 1, sizeof(char *)); ATF_REQUIRE(dest->gr_mem != NULL); for (cp = src->gr_mem; *cp; ++cp) { dest->gr_mem[cp - src->gr_mem] = strdup(*cp); ATF_REQUIRE(dest->gr_mem[cp - src->gr_mem] != NULL); } } } static void free_group(struct group *grp) { char **cp; ATF_REQUIRE(grp != NULL); free(grp->gr_name); free(grp->gr_passwd); for (cp = grp->gr_mem; *cp; ++cp) free(*cp); free(grp->gr_mem); } static int compare_group(struct group *grp1, struct group *grp2, void *mdata) { char **c1, **c2; if (grp1 == grp2) return (0); if (grp1 == NULL || grp2 == NULL) goto errfin; if (strcmp(grp1->gr_name, grp2->gr_name) != 0 || strcmp(grp1->gr_passwd, grp2->gr_passwd) != 0 || grp1->gr_gid != grp2->gr_gid) goto errfin; c1 = grp1->gr_mem; c2 = grp2->gr_mem; if (grp1->gr_mem == NULL || grp2->gr_mem == NULL) goto errfin; for (; *c1 && *c2; ++c1, ++c2) if (strcmp(*c1, *c2) != 0) goto errfin; - if (*c1 != '\0' || *c2 != '\0') + if (*c1 != NULL || *c2 != NULL) goto errfin; return 0; errfin: if (mdata == NULL) { printf("following structures are not equal:\n"); dump_group(grp1); dump_group(grp2); } return (-1); } static void sdump_group(struct group *grp, char *buffer, size_t buflen) { char **cp; int written; written = snprintf(buffer, buflen, "%s:%s:%d:", grp->gr_name, grp->gr_passwd, grp->gr_gid); buffer += written; if (written > (int)buflen) return; buflen -= written; if (grp->gr_mem != NULL) { - if (*(grp->gr_mem) != '\0') { + if (*(grp->gr_mem) != NULL) { for (cp = grp->gr_mem; *cp; ++cp) { written = snprintf(buffer, buflen, "%s%s", cp == grp->gr_mem ? "" : ",", *cp); buffer += written; if (written > (int)buflen) return; buflen -= written; if (buflen == 0) return; } } else snprintf(buffer, buflen, "nomem"); } else snprintf(buffer, buflen, "(null)"); } static int group_read_snapshot_func(struct group *grp, char *line) { StringList *sl; char *s, *ps, *ts; const char *sep; int i; printf("1 line read from snapshot:\n%s\n", line); i = 0; sl = NULL; ps = line; sep = ":"; memset(grp, 0, sizeof(struct group)); while ((s = strsep(&ps, sep)) != NULL) { switch (i) { case 0: grp->gr_name = strdup(s); ATF_REQUIRE(grp->gr_name != NULL); break; case 1: grp->gr_passwd = strdup(s); ATF_REQUIRE(grp->gr_passwd != NULL); break; case 2: grp->gr_gid = (gid_t)strtol(s, &ts, 10); if (*ts != '\0') { free(grp->gr_name); free(grp->gr_passwd); grp->gr_name = NULL; grp->gr_passwd = NULL; return (-1); } /* Change to parsing groups. */ sep = ","; break; default: if (sl == NULL) { if (strcmp(s, "(null)") == 0) return (0); sl = sl_init(); ATF_REQUIRE(sl != NULL); if (strcmp(s, "nomem") != 0) { ts = strdup(s); ATF_REQUIRE(ts != NULL); sl_add(sl, ts); } } else { ts = strdup(s); ATF_REQUIRE(ts != NULL); sl_add(sl, ts); } break; } ++i; } if (i < 3) { free(grp->gr_name); free(grp->gr_passwd); memset(grp, 0, sizeof(struct group)); return (-1); } sl_add(sl, NULL); grp->gr_mem = sl->sl_str; /* NOTE: is it a dirty hack or not? */ free(sl); return (0); } static void dump_group(struct group *result) { if (result != NULL) { char buffer[1024]; sdump_group(result, buffer, sizeof(buffer)); printf("%s\n", buffer); } else printf("(null)\n"); } static int group_fill_test_data(struct group_test_data *td) { struct group *grp; setgroupent(1); while ((grp = getgrent()) != NULL) { if (group_test_correctness(grp, NULL) == 0) TEST_DATA_APPEND(group, td, grp); else return (-1); } endgrent(); return (0); } static int group_test_correctness(struct group *grp, void *mdata __unused) { printf("testing correctness with the following data:\n"); dump_group(grp); if (grp == NULL) goto errfin; if (grp->gr_name == NULL) goto errfin; if (grp->gr_passwd == NULL) goto errfin; if (grp->gr_mem == NULL) goto errfin; printf("correct\n"); return (0); errfin: printf("incorrect\n"); return (-1); } /* group_check_ambiguity() is needed here because when doing the getgrent() * calls sequence, records from different nsswitch sources can be different, * though having the same pw_name/pw_uid */ static int group_check_ambiguity(struct group_test_data *td, struct group *pwd) { return (TEST_DATA_FIND(group, td, pwd, compare_group, NULL) != NULL ? 0 : -1); } static int group_test_getgrnam(struct group *grp_model, void *mdata) { struct group *grp; printf("testing getgrnam() with the following data:\n"); dump_group(grp_model); grp = getgrnam(grp_model->gr_name); if (group_test_correctness(grp, NULL) != 0) goto errfin; if (compare_group(grp, grp_model, NULL) != 0 && group_check_ambiguity((struct group_test_data *)mdata, grp) != 0) goto errfin; return (0); errfin: return (-1); } static int group_test_getgrgid(struct group *grp_model, void *mdata) { struct group *grp; printf("testing getgrgid() with the following data...\n"); dump_group(grp_model); grp = getgrgid(grp_model->gr_gid); if (group_test_correctness(grp, NULL) != 0 || (compare_group(grp, grp_model, NULL) != 0 && group_check_ambiguity((struct group_test_data *)mdata, grp) != 0)) { return (-1); } else { return (0); } } static int group_test_getgrent(struct group *grp, void *mdata __unused) { /* Only correctness can be checked when doing 1-pass test for * getgrent(). */ return (group_test_correctness(grp, NULL)); } static int run_tests(const char *snapshot_file, enum test_methods method) { struct group_test_data td, td_snap, td_2pass; int rv; TEST_DATA_INIT(group, &td, clone_group, free_group); TEST_DATA_INIT(group, &td_snap, clone_group, free_group); if (snapshot_file != NULL) { if (access(snapshot_file, W_OK | R_OK) != 0) { if (errno == ENOENT) method = TEST_BUILD_SNAPSHOT; else { printf("can't access the file %s\n", snapshot_file); rv = -1; goto fin; } } else { if (method == TEST_BUILD_SNAPSHOT) { rv = 0; goto fin; } TEST_SNAPSHOT_FILE_READ(group, snapshot_file, &td_snap, group_read_snapshot_func); } } rv = group_fill_test_data(&td); if (rv == -1) return (-1); switch (method) { case TEST_GETGRNAM: if (snapshot_file == NULL) rv = DO_1PASS_TEST(group, &td, group_test_getgrnam, (void *)&td); else rv = DO_1PASS_TEST(group, &td_snap, group_test_getgrnam, (void *)&td_snap); break; case TEST_GETGRGID: if (snapshot_file == NULL) rv = DO_1PASS_TEST(group, &td, group_test_getgrgid, (void *)&td); else rv = DO_1PASS_TEST(group, &td_snap, group_test_getgrgid, (void *)&td_snap); break; case TEST_GETGRENT: if (snapshot_file == NULL) rv = DO_1PASS_TEST(group, &td, group_test_getgrent, (void *)&td); else rv = DO_2PASS_TEST(group, &td, &td_snap, compare_group, NULL); break; case TEST_GETGRENT_2PASS: TEST_DATA_INIT(group, &td_2pass, clone_group, free_group); rv = group_fill_test_data(&td_2pass); if (rv != -1) rv = DO_2PASS_TEST(group, &td, &td_2pass, compare_group, NULL); TEST_DATA_DESTROY(group, &td_2pass); break; case TEST_BUILD_SNAPSHOT: if (snapshot_file != NULL) rv = TEST_SNAPSHOT_FILE_WRITE(group, snapshot_file, &td, sdump_group); break; default: rv = 0; break; } fin: TEST_DATA_DESTROY(group, &td_snap); TEST_DATA_DESTROY(group, &td); return (rv); } #define SNAPSHOT_FILE "snapshot_grp" ATF_TC_WITHOUT_HEAD(getgrent); ATF_TC_BODY(getgrent, tc) { ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETGRENT) == 0); } ATF_TC_WITHOUT_HEAD(getgrent_with_snapshot); ATF_TC_BODY(getgrent_with_snapshot, tc) { ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETGRENT) == 0); } ATF_TC_WITHOUT_HEAD(getgrent_with_two_pass); ATF_TC_BODY(getgrent_with_two_pass, tc) { ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETGRENT_2PASS) == 0); } ATF_TC_WITHOUT_HEAD(getgrgid); ATF_TC_BODY(getgrgid, tc) { ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETGRGID) == 0); } ATF_TC_WITHOUT_HEAD(getgrgid_with_snapshot); ATF_TC_BODY(getgrgid_with_snapshot, tc) { ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETGRGID) == 0); } ATF_TC_WITHOUT_HEAD(getgrnam); ATF_TC_BODY(getgrnam, tc) { ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETGRNAM) == 0); } ATF_TC_WITHOUT_HEAD(getgrnam_with_snapshot); ATF_TC_BODY(getgrnam_with_snapshot, tc) { ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETGRNAM) == 0); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, getgrent); ATF_TP_ADD_TC(tp, getgrent_with_snapshot); ATF_TP_ADD_TC(tp, getgrent_with_two_pass); ATF_TP_ADD_TC(tp, getgrgid); ATF_TP_ADD_TC(tp, getgrgid_with_snapshot); ATF_TP_ADD_TC(tp, getgrnam); ATF_TP_ADD_TC(tp, getgrnam_with_snapshot); return (atf_no_error()); } Index: stable/12/lib/libc/tests/nss/getproto_test.c =================================================================== --- stable/12/lib/libc/tests/nss/getproto_test.c (revision 353576) +++ stable/12/lib/libc/tests/nss/getproto_test.c (revision 353577) @@ -1,556 +1,556 @@ /*- * Copyright (c) 2006 Michael Bushkov * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "testutil.h" enum test_methods { TEST_GETPROTOENT, TEST_GETPROTOBYNAME, TEST_GETPROTOBYNUMBER, TEST_GETPROTOENT_2PASS, TEST_BUILD_SNAPSHOT }; DECLARE_TEST_DATA(protoent) DECLARE_TEST_FILE_SNAPSHOT(protoent) DECLARE_1PASS_TEST(protoent) DECLARE_2PASS_TEST(protoent) static void clone_protoent(struct protoent *, struct protoent const *); static int compare_protoent(struct protoent *, struct protoent *, void *); static void dump_protoent(struct protoent *); static void free_protoent(struct protoent *); static void sdump_protoent(struct protoent *, char *, size_t); static int protoent_read_snapshot_func(struct protoent *, char *); static int protoent_check_ambiguity(struct protoent_test_data *, struct protoent *); static int protoent_fill_test_data(struct protoent_test_data *); static int protoent_test_correctness(struct protoent *, void *); static int protoent_test_getprotobyname(struct protoent *, void *); static int protoent_test_getprotobynumber(struct protoent *, void *); static int protoent_test_getprotoent(struct protoent *, void *); IMPLEMENT_TEST_DATA(protoent) IMPLEMENT_TEST_FILE_SNAPSHOT(protoent) IMPLEMENT_1PASS_TEST(protoent) IMPLEMENT_2PASS_TEST(protoent) static void clone_protoent(struct protoent *dest, struct protoent const *src) { assert(dest != NULL); assert(src != NULL); char **cp; int aliases_num; memset(dest, 0, sizeof(struct protoent)); if (src->p_name != NULL) { dest->p_name = strdup(src->p_name); assert(dest->p_name != NULL); } dest->p_proto = src->p_proto; if (src->p_aliases != NULL) { aliases_num = 0; for (cp = src->p_aliases; *cp; ++cp) ++aliases_num; dest->p_aliases = calloc(aliases_num + 1, sizeof(char *)); assert(dest->p_aliases != NULL); for (cp = src->p_aliases; *cp; ++cp) { dest->p_aliases[cp - src->p_aliases] = strdup(*cp); assert(dest->p_aliases[cp - src->p_aliases] != NULL); } } } static void free_protoent(struct protoent *pe) { char **cp; assert(pe != NULL); free(pe->p_name); for (cp = pe->p_aliases; *cp; ++cp) free(*cp); free(pe->p_aliases); } static int compare_protoent(struct protoent *pe1, struct protoent *pe2, void *mdata) { char **c1, **c2; if (pe1 == pe2) return 0; if ((pe1 == NULL) || (pe2 == NULL)) goto errfin; if ((strcmp(pe1->p_name, pe2->p_name) != 0) || (pe1->p_proto != pe2->p_proto)) goto errfin; c1 = pe1->p_aliases; c2 = pe2->p_aliases; if ((pe1->p_aliases == NULL) || (pe2->p_aliases == NULL)) goto errfin; for (;*c1 && *c2; ++c1, ++c2) if (strcmp(*c1, *c2) != 0) goto errfin; - if ((*c1 != '\0') || (*c2 != '\0')) + if ((*c1 != NULL) || (*c2 != NULL)) goto errfin; return 0; errfin: if (mdata == NULL) { printf("following structures are not equal:\n"); dump_protoent(pe1); dump_protoent(pe2); } return (-1); } static void sdump_protoent(struct protoent *pe, char *buffer, size_t buflen) { char **cp; int written; written = snprintf(buffer, buflen, "%s %d", pe->p_name, pe->p_proto); buffer += written; if (written > (int)buflen) return; buflen -= written; if (pe->p_aliases != NULL) { - if (*(pe->p_aliases) != '\0') { + if (*(pe->p_aliases) != NULL) { for (cp = pe->p_aliases; *cp; ++cp) { written = snprintf(buffer, buflen, " %s", *cp); buffer += written; if (written > (int)buflen) return; buflen -= written; if (buflen == 0) return; } } else snprintf(buffer, buflen, " noaliases"); } else snprintf(buffer, buflen, " (null)"); } static int protoent_read_snapshot_func(struct protoent *pe, char *line) { StringList *sl; char *s, *ps, *ts; int i; printf("1 line read from snapshot:\n%s\n", line); i = 0; sl = NULL; ps = line; memset(pe, 0, sizeof(struct protoent)); while ( (s = strsep(&ps, " ")) != NULL) { switch (i) { case 0: pe->p_name = strdup(s); assert(pe->p_name != NULL); break; case 1: pe->p_proto = (int)strtol(s, &ts, 10); if (*ts != '\0') { free(pe->p_name); return (-1); } break; default: if (sl == NULL) { if (strcmp(s, "(null)") == 0) return (0); sl = sl_init(); assert(sl != NULL); if (strcmp(s, "noaliases") != 0) { ts = strdup(s); assert(ts != NULL); sl_add(sl, ts); } } else { ts = strdup(s); assert(ts != NULL); sl_add(sl, ts); } break; } ++i; } if (i < 3) { free(pe->p_name); memset(pe, 0, sizeof(struct protoent)); return (-1); } sl_add(sl, NULL); pe->p_aliases = sl->sl_str; /* NOTE: is it a dirty hack or not? */ free(sl); return (0); } static void dump_protoent(struct protoent *result) { if (result != NULL) { char buffer[1024]; sdump_protoent(result, buffer, sizeof(buffer)); printf("%s\n", buffer); } else printf("(null)\n"); } static int protoent_fill_test_data(struct protoent_test_data *td) { struct protoent *pe; setprotoent(1); while ((pe = getprotoent()) != NULL) { if (protoent_test_correctness(pe, NULL) == 0) TEST_DATA_APPEND(protoent, td, pe); else return (-1); } endprotoent(); return (0); } static int protoent_test_correctness(struct protoent *pe, void *mdata __unused) { printf("testing correctness with the following data:\n"); dump_protoent(pe); if (pe == NULL) goto errfin; if (pe->p_name == NULL) goto errfin; if (pe->p_proto < 0) goto errfin; if (pe->p_aliases == NULL) goto errfin; printf("correct\n"); return (0); errfin: printf("incorrect\n"); return (-1); } /* protoent_check_ambiguity() is needed when one port+proto is associated with * more than one piece (these cases are usually marked as PROBLEM in * /etc/peices. This functions is needed also when one piece+proto is * associated with several ports. We have to check all the protoent structures * to make sure that pe really exists and correct */ static int protoent_check_ambiguity(struct protoent_test_data *td, struct protoent *pe) { return (TEST_DATA_FIND(protoent, td, pe, compare_protoent, NULL) != NULL ? 0 : -1); } static int protoent_test_getprotobyname(struct protoent *pe_model, void *mdata) { char **alias; struct protoent *pe; printf("testing getprotobyname() with the following data:\n"); dump_protoent(pe_model); pe = getprotobyname(pe_model->p_name); if (protoent_test_correctness(pe, NULL) != 0) goto errfin; if ((compare_protoent(pe, pe_model, NULL) != 0) && (protoent_check_ambiguity((struct protoent_test_data *)mdata, pe) !=0)) goto errfin; for (alias = pe_model->p_aliases; *alias; ++alias) { pe = getprotobyname(*alias); if (protoent_test_correctness(pe, NULL) != 0) goto errfin; if ((compare_protoent(pe, pe_model, NULL) != 0) && (protoent_check_ambiguity( (struct protoent_test_data *)mdata, pe) != 0)) goto errfin; } printf("ok\n"); return (0); errfin: printf("not ok\n"); return (-1); } static int protoent_test_getprotobynumber(struct protoent *pe_model, void *mdata) { struct protoent *pe; printf("testing getprotobyport() with the following data...\n"); dump_protoent(pe_model); pe = getprotobynumber(pe_model->p_proto); if ((protoent_test_correctness(pe, NULL) != 0) || ((compare_protoent(pe, pe_model, NULL) != 0) && (protoent_check_ambiguity((struct protoent_test_data *)mdata, pe) != 0))) { printf("not ok\n"); return (-1); } else { printf("ok\n"); return (0); } } static int protoent_test_getprotoent(struct protoent *pe, void *mdata __unused) { /* Only correctness can be checked when doing 1-pass test for * getprotoent(). */ return (protoent_test_correctness(pe, NULL)); } static int run_tests(const char *snapshot_file, enum test_methods method) { struct protoent_test_data td, td_snap, td_2pass; int rv; TEST_DATA_INIT(protoent, &td, clone_protoent, free_protoent); TEST_DATA_INIT(protoent, &td_snap, clone_protoent, free_protoent); if (snapshot_file != NULL) { if (access(snapshot_file, W_OK | R_OK) != 0) { if (errno == ENOENT) method = TEST_BUILD_SNAPSHOT; else { printf("can't access the file %s\n", snapshot_file); rv = -1; goto fin; } } else { if (method == TEST_BUILD_SNAPSHOT) { rv = 0; goto fin; } TEST_SNAPSHOT_FILE_READ(protoent, snapshot_file, &td_snap, protoent_read_snapshot_func); } } rv = protoent_fill_test_data(&td); if (rv == -1) return (-1); switch (method) { case TEST_GETPROTOBYNAME: if (snapshot_file == NULL) rv = DO_1PASS_TEST(protoent, &td, protoent_test_getprotobyname, (void *)&td); else rv = DO_1PASS_TEST(protoent, &td_snap, protoent_test_getprotobyname, (void *)&td_snap); break; case TEST_GETPROTOBYNUMBER: if (snapshot_file == NULL) rv = DO_1PASS_TEST(protoent, &td, protoent_test_getprotobynumber, (void *)&td); else rv = DO_1PASS_TEST(protoent, &td_snap, protoent_test_getprotobynumber, (void *)&td_snap); break; case TEST_GETPROTOENT: if (snapshot_file == NULL) rv = DO_1PASS_TEST(protoent, &td, protoent_test_getprotoent, (void *)&td); else rv = DO_2PASS_TEST(protoent, &td, &td_snap, compare_protoent, NULL); break; case TEST_GETPROTOENT_2PASS: TEST_DATA_INIT(protoent, &td_2pass, clone_protoent, free_protoent); rv = protoent_fill_test_data(&td_2pass); if (rv != -1) rv = DO_2PASS_TEST(protoent, &td, &td_2pass, compare_protoent, NULL); TEST_DATA_DESTROY(protoent, &td_2pass); break; case TEST_BUILD_SNAPSHOT: if (snapshot_file != NULL) rv = TEST_SNAPSHOT_FILE_WRITE(protoent, snapshot_file, &td, sdump_protoent); break; default: rv = 0; break; } fin: TEST_DATA_DESTROY(protoent, &td_snap); TEST_DATA_DESTROY(protoent, &td); return (rv); } #define SNAPSHOT_FILE "snapshot_proto" ATF_TC_WITHOUT_HEAD(build_snapshot); ATF_TC_BODY(build_snapshot, tc) { ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); } ATF_TC_WITHOUT_HEAD(getprotoent); ATF_TC_BODY(getprotoent, tc) { ATF_REQUIRE(run_tests(NULL, TEST_GETPROTOENT) == 0); } ATF_TC_WITHOUT_HEAD(getprotoent_with_snapshot); ATF_TC_BODY(getprotoent_with_snapshot, tc) { ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETPROTOENT) == 0); } ATF_TC_WITHOUT_HEAD(getprotoent_with_two_pass); ATF_TC_BODY(getprotoent_with_two_pass, tc) { ATF_REQUIRE(run_tests(NULL, TEST_GETPROTOENT_2PASS) == 0); } ATF_TC_WITHOUT_HEAD(getprotobyname); ATF_TC_BODY(getprotobyname, tc) { ATF_REQUIRE(run_tests(NULL, TEST_GETPROTOBYNAME) == 0); } ATF_TC_WITHOUT_HEAD(getprotobyname_with_snapshot); ATF_TC_BODY(getprotobyname_with_snapshot, tc) { ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETPROTOBYNAME) == 0); } ATF_TC_WITHOUT_HEAD(getprotobynumber); ATF_TC_BODY(getprotobynumber, tc) { ATF_REQUIRE(run_tests(NULL, TEST_GETPROTOBYNUMBER) == 0); } ATF_TC_WITHOUT_HEAD(getprotobynumber_with_snapshot); ATF_TC_BODY(getprotobynumber_with_snapshot, tc) { ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETPROTOBYNUMBER) == 0); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, build_snapshot); ATF_TP_ADD_TC(tp, getprotoent); ATF_TP_ADD_TC(tp, getprotoent_with_snapshot); ATF_TP_ADD_TC(tp, getprotoent_with_two_pass); ATF_TP_ADD_TC(tp, getprotobyname); ATF_TP_ADD_TC(tp, getprotobyname_with_snapshot); ATF_TP_ADD_TC(tp, getprotobynumber); ATF_TP_ADD_TC(tp, getprotobynumber_with_snapshot); return (atf_no_error()); } Index: stable/12/lib/libc/tests/nss/getrpc_test.c =================================================================== --- stable/12/lib/libc/tests/nss/getrpc_test.c (revision 353576) +++ stable/12/lib/libc/tests/nss/getrpc_test.c (revision 353577) @@ -1,558 +1,558 @@ /*- * Copyright (c) 2006 Michael Bushkov * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "testutil.h" enum test_methods { TEST_GETRPCENT, TEST_GETRPCBYNAME, TEST_GETRPCBYNUMBER, TEST_GETRPCENT_2PASS, TEST_BUILD_SNAPSHOT }; DECLARE_TEST_DATA(rpcent) DECLARE_TEST_FILE_SNAPSHOT(rpcent) DECLARE_1PASS_TEST(rpcent) DECLARE_2PASS_TEST(rpcent) static void clone_rpcent(struct rpcent *, struct rpcent const *); static int compare_rpcent(struct rpcent *, struct rpcent *, void *); static void dump_rpcent(struct rpcent *); static void free_rpcent(struct rpcent *); static void sdump_rpcent(struct rpcent *, char *, size_t); static int rpcent_read_snapshot_func(struct rpcent *, char *); static int rpcent_check_ambiguity(struct rpcent_test_data *, struct rpcent *); static int rpcent_fill_test_data(struct rpcent_test_data *); static int rpcent_test_correctness(struct rpcent *, void *); static int rpcent_test_getrpcbyname(struct rpcent *, void *); static int rpcent_test_getrpcbynumber(struct rpcent *, void *); static int rpcent_test_getrpcent(struct rpcent *, void *); IMPLEMENT_TEST_DATA(rpcent) IMPLEMENT_TEST_FILE_SNAPSHOT(rpcent) IMPLEMENT_1PASS_TEST(rpcent) IMPLEMENT_2PASS_TEST(rpcent) static void clone_rpcent(struct rpcent *dest, struct rpcent const *src) { ATF_REQUIRE(dest != NULL); ATF_REQUIRE(src != NULL); char **cp; int aliases_num; memset(dest, 0, sizeof(struct rpcent)); if (src->r_name != NULL) { dest->r_name = strdup(src->r_name); ATF_REQUIRE(dest->r_name != NULL); } dest->r_number = src->r_number; if (src->r_aliases != NULL) { aliases_num = 0; for (cp = src->r_aliases; *cp; ++cp) ++aliases_num; dest->r_aliases = calloc(aliases_num + 1, sizeof(char *)); ATF_REQUIRE(dest->r_aliases != NULL); for (cp = src->r_aliases; *cp; ++cp) { dest->r_aliases[cp - src->r_aliases] = strdup(*cp); ATF_REQUIRE(dest->r_aliases[cp - src->r_aliases] != NULL); } } } static void free_rpcent(struct rpcent *rpc) { char **cp; ATF_REQUIRE(rpc != NULL); free(rpc->r_name); for (cp = rpc->r_aliases; *cp; ++cp) free(*cp); free(rpc->r_aliases); } static int compare_rpcent(struct rpcent *rpc1, struct rpcent *rpc2, void *mdata) { char **c1, **c2; if (rpc1 == rpc2) return 0; if ((rpc1 == NULL) || (rpc2 == NULL)) goto errfin; if ((strcmp(rpc1->r_name, rpc2->r_name) != 0) || (rpc1->r_number != rpc2->r_number)) goto errfin; c1 = rpc1->r_aliases; c2 = rpc2->r_aliases; if ((rpc1->r_aliases == NULL) || (rpc2->r_aliases == NULL)) goto errfin; for (;*c1 && *c2; ++c1, ++c2) if (strcmp(*c1, *c2) != 0) goto errfin; - if ((*c1 != '\0') || (*c2 != '\0')) + if ((*c1 != NULL) || (*c2 != NULL)) goto errfin; return 0; errfin: if (mdata == NULL) { printf("following structures are not equal:\n"); dump_rpcent(rpc1); dump_rpcent(rpc2); } return (-1); } static void sdump_rpcent(struct rpcent *rpc, char *buffer, size_t buflen) { char **cp; int written; written = snprintf(buffer, buflen, "%s %d", rpc->r_name, rpc->r_number); buffer += written; if (written > (int)buflen) return; buflen -= written; if (rpc->r_aliases != NULL) { - if (*(rpc->r_aliases) != '\0') { + if (*(rpc->r_aliases) != NULL) { for (cp = rpc->r_aliases; *cp; ++cp) { written = snprintf(buffer, buflen, " %s", *cp); buffer += written; if (written > (int)buflen) return; buflen -= written; if (buflen == 0) return; } } else snprintf(buffer, buflen, " noaliases"); } else snprintf(buffer, buflen, " (null)"); } static int rpcent_read_snapshot_func(struct rpcent *rpc, char *line) { StringList *sl; char *s, *ps, *ts; int i; printf("1 line read from snapshot:\n%s\n", line); i = 0; sl = NULL; ps = line; memset(rpc, 0, sizeof(struct rpcent)); while ((s = strsep(&ps, " ")) != NULL) { switch (i) { case 0: rpc->r_name = strdup(s); ATF_REQUIRE(rpc->r_name != NULL); break; case 1: rpc->r_number = (int)strtol(s, &ts, 10); if (*ts != '\0') { free(rpc->r_name); return (-1); } break; default: if (sl == NULL) { if (strcmp(s, "(null)") == 0) return (0); sl = sl_init(); ATF_REQUIRE(sl != NULL); if (strcmp(s, "noaliases") != 0) { ts = strdup(s); ATF_REQUIRE(ts != NULL); sl_add(sl, ts); } } else { ts = strdup(s); ATF_REQUIRE(ts != NULL); sl_add(sl, ts); } break; } i++; } if (i < 3) { free(rpc->r_name); memset(rpc, 0, sizeof(struct rpcent)); return (-1); } sl_add(sl, NULL); rpc->r_aliases = sl->sl_str; /* NOTE: is it a dirty hack or not? */ free(sl); return (0); } static void dump_rpcent(struct rpcent *result) { if (result != NULL) { char buffer[1024]; sdump_rpcent(result, buffer, sizeof(buffer)); printf("%s\n", buffer); } else printf("(null)\n"); } static int rpcent_fill_test_data(struct rpcent_test_data *td) { struct rpcent *rpc; setrpcent(1); while ((rpc = getrpcent()) != NULL) { if (rpcent_test_correctness(rpc, NULL) == 0) TEST_DATA_APPEND(rpcent, td, rpc); else return (-1); } endrpcent(); return (0); } static int rpcent_test_correctness(struct rpcent *rpc, void *mdata __unused) { printf("testing correctness with the following data:\n"); dump_rpcent(rpc); if (rpc == NULL) goto errfin; if (rpc->r_name == NULL) goto errfin; if (rpc->r_number < 0) goto errfin; if (rpc->r_aliases == NULL) goto errfin; printf("correct\n"); return (0); errfin: printf("incorrect\n"); return (-1); } /* rpcent_check_ambiguity() is needed when one port+rpc is associated with * more than one piece (these cases are usually marked as PROBLEM in * /etc/peices. This functions is needed also when one piece+rpc is * associated with several ports. We have to check all the rpcent structures * to make sure that rpc really exists and correct */ static int rpcent_check_ambiguity(struct rpcent_test_data *td, struct rpcent *rpc) { return (TEST_DATA_FIND(rpcent, td, rpc, compare_rpcent, NULL) != NULL ? 0 : -1); } static int rpcent_test_getrpcbyname(struct rpcent *rpc_model, void *mdata) { char **alias; struct rpcent *rpc; printf("testing getrpcbyname() with the following data:\n"); dump_rpcent(rpc_model); rpc = getrpcbyname(rpc_model->r_name); if (rpcent_test_correctness(rpc, NULL) != 0) goto errfin; if ((compare_rpcent(rpc, rpc_model, NULL) != 0) && (rpcent_check_ambiguity((struct rpcent_test_data *)mdata, rpc) !=0)) goto errfin; for (alias = rpc_model->r_aliases; *alias; ++alias) { rpc = getrpcbyname(*alias); if (rpcent_test_correctness(rpc, NULL) != 0) goto errfin; if ((compare_rpcent(rpc, rpc_model, NULL) != 0) && (rpcent_check_ambiguity( (struct rpcent_test_data *)mdata, rpc) != 0)) goto errfin; } printf("ok\n"); return (0); errfin: printf("not ok\n"); return (-1); } static int rpcent_test_getrpcbynumber(struct rpcent *rpc_model, void *mdata) { struct rpcent *rpc; printf("testing getrpcbyport() with the following data...\n"); dump_rpcent(rpc_model); rpc = getrpcbynumber(rpc_model->r_number); if (rpcent_test_correctness(rpc, NULL) != 0 || (compare_rpcent(rpc, rpc_model, NULL) != 0 && rpcent_check_ambiguity((struct rpcent_test_data *)mdata, rpc) != 0)) { printf("not ok\n"); return (-1); } else { printf("ok\n"); return (0); } } static int rpcent_test_getrpcent(struct rpcent *rpc, void *mdata __unused) { /* * Only correctness can be checked when doing 1-pass test for * getrpcent(). */ return (rpcent_test_correctness(rpc, NULL)); } static int run_tests(const char *snapshot_file, enum test_methods method) { struct rpcent_test_data td, td_snap, td_2pass; int rv; TEST_DATA_INIT(rpcent, &td, clone_rpcent, free_rpcent); TEST_DATA_INIT(rpcent, &td_snap, clone_rpcent, free_rpcent); if (snapshot_file != NULL) { if (access(snapshot_file, W_OK | R_OK) != 0) { if (errno == ENOENT) method = TEST_BUILD_SNAPSHOT; else { printf("can't access the file %s\n", snapshot_file); rv = -1; goto fin; } } else { if (method == TEST_BUILD_SNAPSHOT) { rv = 0; goto fin; } TEST_SNAPSHOT_FILE_READ(rpcent, snapshot_file, &td_snap, rpcent_read_snapshot_func); } } rv = rpcent_fill_test_data(&td); if (rv == -1) return (-1); switch (method) { case TEST_GETRPCBYNAME: if (snapshot_file == NULL) rv = DO_1PASS_TEST(rpcent, &td, rpcent_test_getrpcbyname, (void *)&td); else rv = DO_1PASS_TEST(rpcent, &td_snap, rpcent_test_getrpcbyname, (void *)&td_snap); break; case TEST_GETRPCBYNUMBER: if (snapshot_file == NULL) rv = DO_1PASS_TEST(rpcent, &td, rpcent_test_getrpcbynumber, (void *)&td); else rv = DO_1PASS_TEST(rpcent, &td_snap, rpcent_test_getrpcbynumber, (void *)&td_snap); break; case TEST_GETRPCENT: if (snapshot_file == NULL) rv = DO_1PASS_TEST(rpcent, &td, rpcent_test_getrpcent, (void *)&td); else rv = DO_2PASS_TEST(rpcent, &td, &td_snap, compare_rpcent, NULL); break; case TEST_GETRPCENT_2PASS: TEST_DATA_INIT(rpcent, &td_2pass, clone_rpcent, free_rpcent); rv = rpcent_fill_test_data(&td_2pass); if (rv != -1) rv = DO_2PASS_TEST(rpcent, &td, &td_2pass, compare_rpcent, NULL); TEST_DATA_DESTROY(rpcent, &td_2pass); break; case TEST_BUILD_SNAPSHOT: if (snapshot_file != NULL) rv = TEST_SNAPSHOT_FILE_WRITE(rpcent, snapshot_file, &td, sdump_rpcent); break; default: rv = 0; break; } fin: TEST_DATA_DESTROY(rpcent, &td_snap); TEST_DATA_DESTROY(rpcent, &td); return (rv); } #define SNAPSHOT_FILE "snapshot_rpc" ATF_TC_WITHOUT_HEAD(build_snapshot); ATF_TC_BODY(build_snapshot, tc) { ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); } ATF_TC_WITHOUT_HEAD(getrpcbyname); ATF_TC_BODY(getrpcbyname, tc) { ATF_REQUIRE(run_tests(NULL, TEST_GETRPCBYNAME) == 0); } ATF_TC_WITHOUT_HEAD(getrpcbyname_with_snapshot); ATF_TC_BODY(getrpcbyname_with_snapshot, tc) { ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETRPCBYNAME) == 0); } ATF_TC_WITHOUT_HEAD(getrpcbynumber); ATF_TC_BODY(getrpcbynumber, tc) { ATF_REQUIRE(run_tests(NULL, TEST_GETRPCBYNUMBER) == 0); } ATF_TC_WITHOUT_HEAD(getrpcbynumber_with_snapshot); ATF_TC_BODY(getrpcbynumber_with_snapshot, tc) { ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETRPCBYNUMBER) == 0); } ATF_TC_WITHOUT_HEAD(getrpcbyent); ATF_TC_BODY(getrpcbyent, tc) { ATF_REQUIRE(run_tests(NULL, TEST_GETRPCENT) == 0); } ATF_TC_WITHOUT_HEAD(getrpcbyent_with_snapshot); ATF_TC_BODY(getrpcbyent_with_snapshot, tc) { ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETRPCENT) == 0); } ATF_TC_WITHOUT_HEAD(getrpcbyent_with_two_pass); ATF_TC_BODY(getrpcbyent_with_two_pass, tc) { ATF_REQUIRE(run_tests(NULL, TEST_GETRPCENT_2PASS) == 0); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, build_snapshot); ATF_TP_ADD_TC(tp, getrpcbyname); ATF_TP_ADD_TC(tp, getrpcbyname_with_snapshot); ATF_TP_ADD_TC(tp, getrpcbynumber); ATF_TP_ADD_TC(tp, getrpcbynumber_with_snapshot); ATF_TP_ADD_TC(tp, getrpcbyent); ATF_TP_ADD_TC(tp, getrpcbyent_with_snapshot); ATF_TP_ADD_TC(tp, getrpcbyent_with_two_pass); return (atf_no_error()); } Index: stable/12/lib/libc/tests/nss/getserv_test.c =================================================================== --- stable/12/lib/libc/tests/nss/getserv_test.c (revision 353576) +++ stable/12/lib/libc/tests/nss/getserv_test.c (revision 353577) @@ -1,570 +1,570 @@ /*- * Copyright (c) 2006 Michael Bushkov * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "testutil.h" enum test_methods { TEST_GETSERVENT, TEST_GETSERVBYNAME, TEST_GETSERVBYPORT, TEST_GETSERVENT_2PASS, TEST_BUILD_SNAPSHOT }; DECLARE_TEST_DATA(servent) DECLARE_TEST_FILE_SNAPSHOT(servent) DECLARE_1PASS_TEST(servent) DECLARE_2PASS_TEST(servent) static void clone_servent(struct servent *, struct servent const *); static int compare_servent(struct servent *, struct servent *, void *); static void dump_servent(struct servent *); static void free_servent(struct servent *); static void sdump_servent(struct servent *, char *, size_t); static int servent_read_snapshot_func(struct servent *, char *); static int servent_check_ambiguity(struct servent_test_data *, struct servent *); static int servent_fill_test_data(struct servent_test_data *); static int servent_test_correctness(struct servent *, void *); static int servent_test_getservbyname(struct servent *, void *); static int servent_test_getservbyport(struct servent *, void *); static int servent_test_getservent(struct servent *, void *); IMPLEMENT_TEST_DATA(servent) IMPLEMENT_TEST_FILE_SNAPSHOT(servent) IMPLEMENT_1PASS_TEST(servent) IMPLEMENT_2PASS_TEST(servent) static void clone_servent(struct servent *dest, struct servent const *src) { ATF_REQUIRE(dest != NULL); ATF_REQUIRE(src != NULL); char **cp; int aliases_num; memset(dest, 0, sizeof(struct servent)); if (src->s_name != NULL) { dest->s_name = strdup(src->s_name); ATF_REQUIRE(dest->s_name != NULL); } if (src->s_proto != NULL) { dest->s_proto = strdup(src->s_proto); ATF_REQUIRE(dest->s_proto != NULL); } dest->s_port = src->s_port; if (src->s_aliases != NULL) { aliases_num = 0; for (cp = src->s_aliases; *cp; ++cp) ++aliases_num; dest->s_aliases = calloc(aliases_num + 1, sizeof(char *)); ATF_REQUIRE(dest->s_aliases != NULL); for (cp = src->s_aliases; *cp; ++cp) { dest->s_aliases[cp - src->s_aliases] = strdup(*cp); ATF_REQUIRE(dest->s_aliases[cp - src->s_aliases] != NULL); } } } static void free_servent(struct servent *serv) { char **cp; ATF_REQUIRE(serv != NULL); free(serv->s_name); free(serv->s_proto); for (cp = serv->s_aliases; *cp; ++cp) free(*cp); free(serv->s_aliases); } static int compare_servent(struct servent *serv1, struct servent *serv2, void *mdata) { char **c1, **c2; if (serv1 == serv2) return 0; if ((serv1 == NULL) || (serv2 == NULL)) goto errfin; if ((strcmp(serv1->s_name, serv2->s_name) != 0) || (strcmp(serv1->s_proto, serv2->s_proto) != 0) || (serv1->s_port != serv2->s_port)) goto errfin; c1 = serv1->s_aliases; c2 = serv2->s_aliases; if ((serv1->s_aliases == NULL) || (serv2->s_aliases == NULL)) goto errfin; for (;*c1 && *c2; ++c1, ++c2) if (strcmp(*c1, *c2) != 0) goto errfin; - if ((*c1 != '\0') || (*c2 != '\0')) + if ((*c1 != NULL) || (*c2 != NULL)) goto errfin; return 0; errfin: if (mdata == NULL) { printf("following structures are not equal:\n"); dump_servent(serv1); dump_servent(serv2); } return (-1); } static void sdump_servent(struct servent *serv, char *buffer, size_t buflen) { char **cp; int written; written = snprintf(buffer, buflen, "%s %d %s", serv->s_name, ntohs(serv->s_port), serv->s_proto); buffer += written; if (written > (int)buflen) return; buflen -= written; if (serv->s_aliases != NULL) { - if (*(serv->s_aliases) != '\0') { + if (*(serv->s_aliases) != NULL) { for (cp = serv->s_aliases; *cp; ++cp) { written = snprintf(buffer, buflen, " %s", *cp); buffer += written; if (written > (int)buflen) return; buflen -= written; if (buflen == 0) return; } } else snprintf(buffer, buflen, " noaliases"); } else snprintf(buffer, buflen, " (null)"); } static int servent_read_snapshot_func(struct servent *serv, char *line) { StringList *sl; char *s, *ps, *ts; int i; printf("1 line read from snapshot:\n%s\n", line); i = 0; sl = NULL; ps = line; memset(serv, 0, sizeof(struct servent)); while ( (s = strsep(&ps, " ")) != NULL) { switch (i) { case 0: serv->s_name = strdup(s); ATF_REQUIRE(serv->s_name != NULL); break; case 1: serv->s_port = htons( (int)strtol(s, &ts, 10)); if (*ts != '\0') { free(serv->s_name); return (-1); } break; case 2: serv->s_proto = strdup(s); ATF_REQUIRE(serv->s_proto != NULL); break; default: if (sl == NULL) { if (strcmp(s, "(null)") == 0) return (0); sl = sl_init(); ATF_REQUIRE(sl != NULL); if (strcmp(s, "noaliases") != 0) { ts = strdup(s); ATF_REQUIRE(ts != NULL); sl_add(sl, ts); } } else { ts = strdup(s); ATF_REQUIRE(ts != NULL); sl_add(sl, ts); } break; } ++i; } if (i < 3) { free(serv->s_name); free(serv->s_proto); memset(serv, 0, sizeof(struct servent)); return (-1); } sl_add(sl, NULL); serv->s_aliases = sl->sl_str; /* NOTE: is it a dirty hack or not? */ free(sl); return (0); } static void dump_servent(struct servent *result) { if (result != NULL) { char buffer[1024]; sdump_servent(result, buffer, sizeof(buffer)); printf("%s\n", buffer); } else printf("(null)\n"); } static int servent_fill_test_data(struct servent_test_data *td) { struct servent *serv; setservent(1); while ((serv = getservent()) != NULL) { if (servent_test_correctness(serv, NULL) == 0) TEST_DATA_APPEND(servent, td, serv); else return (-1); } endservent(); return (0); } static int servent_test_correctness(struct servent *serv, void *mdata __unused) { printf("testing correctness with the following data:\n"); dump_servent(serv); if (serv == NULL) goto errfin; if (serv->s_name == NULL) goto errfin; if (serv->s_proto == NULL) goto errfin; if (ntohs(serv->s_port < 0)) goto errfin; if (serv->s_aliases == NULL) goto errfin; printf("correct\n"); return (0); errfin: printf("incorrect\n"); return (-1); } /* servent_check_ambiguity() is needed when one port+proto is associated with * more than one service (these cases are usually marked as PROBLEM in * /etc/services. This functions is needed also when one service+proto is * associated with several ports. We have to check all the servent structures * to make sure that serv really exists and correct */ static int servent_check_ambiguity(struct servent_test_data *td, struct servent *serv) { return (TEST_DATA_FIND(servent, td, serv, compare_servent, NULL) != NULL ? 0 : -1); } static int servent_test_getservbyname(struct servent *serv_model, void *mdata) { char **alias; struct servent *serv; printf("testing getservbyname() with the following data:\n"); dump_servent(serv_model); serv = getservbyname(serv_model->s_name, serv_model->s_proto); if (servent_test_correctness(serv, NULL) != 0) goto errfin; if ((compare_servent(serv, serv_model, NULL) != 0) && (servent_check_ambiguity((struct servent_test_data *)mdata, serv) !=0)) goto errfin; for (alias = serv_model->s_aliases; *alias; ++alias) { serv = getservbyname(*alias, serv_model->s_proto); if (servent_test_correctness(serv, NULL) != 0) goto errfin; if ((compare_servent(serv, serv_model, NULL) != 0) && (servent_check_ambiguity( (struct servent_test_data *)mdata, serv) != 0)) goto errfin; } printf("ok\n"); return (0); errfin: printf("not ok\n"); return (-1); } static int servent_test_getservbyport(struct servent *serv_model, void *mdata) { struct servent *serv; printf("testing getservbyport() with the following data...\n"); dump_servent(serv_model); serv = getservbyport(serv_model->s_port, serv_model->s_proto); if ((servent_test_correctness(serv, NULL) != 0) || ((compare_servent(serv, serv_model, NULL) != 0) && (servent_check_ambiguity((struct servent_test_data *)mdata, serv) != 0))) { printf("not ok\n"); return (-1); } else { printf("ok\n"); return (0); } } static int servent_test_getservent(struct servent *serv, void *mdata __unused) { /* Only correctness can be checked when doing 1-pass test for * getservent(). */ return (servent_test_correctness(serv, NULL)); } static int run_tests(const char *snapshot_file, enum test_methods method) { struct servent_test_data td, td_snap, td_2pass; int rv; TEST_DATA_INIT(servent, &td, clone_servent, free_servent); TEST_DATA_INIT(servent, &td_snap, clone_servent, free_servent); if (snapshot_file != NULL) { if (access(snapshot_file, W_OK | R_OK) != 0) { if (errno == ENOENT) method = TEST_BUILD_SNAPSHOT; else { printf("can't access the file %s\n", snapshot_file); rv = -1; goto fin; } } else { if (method == TEST_BUILD_SNAPSHOT) { rv = 0; goto fin; } TEST_SNAPSHOT_FILE_READ(servent, snapshot_file, &td_snap, servent_read_snapshot_func); } } rv = servent_fill_test_data(&td); if (rv == -1) return (-1); switch (method) { case TEST_GETSERVBYNAME: if (snapshot_file == NULL) rv = DO_1PASS_TEST(servent, &td, servent_test_getservbyname, (void *)&td); else rv = DO_1PASS_TEST(servent, &td_snap, servent_test_getservbyname, (void *)&td_snap); break; case TEST_GETSERVBYPORT: if (snapshot_file == NULL) rv = DO_1PASS_TEST(servent, &td, servent_test_getservbyport, (void *)&td); else rv = DO_1PASS_TEST(servent, &td_snap, servent_test_getservbyport, (void *)&td_snap); break; case TEST_GETSERVENT: if (snapshot_file == NULL) rv = DO_1PASS_TEST(servent, &td, servent_test_getservent, (void *)&td); else rv = DO_2PASS_TEST(servent, &td, &td_snap, compare_servent, NULL); break; case TEST_GETSERVENT_2PASS: TEST_DATA_INIT(servent, &td_2pass, clone_servent, free_servent); rv = servent_fill_test_data(&td_2pass); if (rv != -1) rv = DO_2PASS_TEST(servent, &td, &td_2pass, compare_servent, NULL); TEST_DATA_DESTROY(servent, &td_2pass); break; case TEST_BUILD_SNAPSHOT: if (snapshot_file != NULL) rv = TEST_SNAPSHOT_FILE_WRITE(servent, snapshot_file, &td, sdump_servent); break; default: rv = 0; break; } fin: TEST_DATA_DESTROY(servent, &td_snap); TEST_DATA_DESTROY(servent, &td); return (rv); } #define SNAPSHOT_FILE "snapshot_serv" ATF_TC_WITHOUT_HEAD(build_snapshot); ATF_TC_BODY(build_snapshot, tc) { ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); } ATF_TC_WITHOUT_HEAD(getservbyname); ATF_TC_BODY(getservbyname, tc) { ATF_REQUIRE(run_tests(NULL, TEST_GETSERVBYNAME) == 0); } ATF_TC_WITHOUT_HEAD(getservbyname_with_snapshot); ATF_TC_BODY(getservbyname_with_snapshot, tc) { ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETSERVBYNAME) == 0); } ATF_TC_WITHOUT_HEAD(getservbyport); ATF_TC_BODY(getservbyport, tc) { ATF_REQUIRE(run_tests(NULL, TEST_GETSERVBYPORT) == 0); } ATF_TC_WITHOUT_HEAD(getservbyport_with_snapshot); ATF_TC_BODY(getservbyport_with_snapshot, tc) { ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETSERVBYPORT) == 0); } ATF_TC_WITHOUT_HEAD(getservbyent); ATF_TC_BODY(getservbyent, tc) { ATF_REQUIRE(run_tests(NULL, TEST_GETSERVENT) == 0); } ATF_TC_WITHOUT_HEAD(getservbyent_with_snapshot); ATF_TC_BODY(getservbyent_with_snapshot, tc) { ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETSERVENT) == 0); } ATF_TC_WITHOUT_HEAD(getservbyent_with_two_pass); ATF_TC_BODY(getservbyent_with_two_pass, tc) { ATF_REQUIRE(run_tests(NULL, TEST_GETSERVENT_2PASS) == 0); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, build_snapshot); ATF_TP_ADD_TC(tp, getservbyent); ATF_TP_ADD_TC(tp, getservbyent_with_snapshot); ATF_TP_ADD_TC(tp, getservbyent_with_two_pass); ATF_TP_ADD_TC(tp, getservbyname); ATF_TP_ADD_TC(tp, getservbyname_with_snapshot); ATF_TP_ADD_TC(tp, getservbyport); ATF_TP_ADD_TC(tp, getservbyport_with_snapshot); return (atf_no_error()); } Index: stable/12/usr.bin/tip/tip/acu.c =================================================================== --- stable/12/usr.bin/tip/tip/acu.c (revision 353576) +++ stable/12/usr.bin/tip/tip/acu.c (revision 353577) @@ -1,197 +1,197 @@ /* $OpenBSD: acu.c,v 1.12 2006/03/17 14:43:06 moritz Exp $ */ /* $NetBSD: acu.c,v 1.4 1996/12/29 10:34:03 cgd Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #ifndef lint #if 0 static char sccsid[] = "@(#)acu.c 8.1 (Berkeley) 6/6/93"; static const char rcsid[] = "$OpenBSD: acu.c,v 1.12 2006/03/17 14:43:06 moritz Exp $"; #endif #endif /* not lint */ #include "tip.h" static acu_t *acu = NOACU; static int conflag; static void acuabort(int); static acu_t *acutype(char *); static jmp_buf jmpbuf; /* * Establish connection for tip * * If DU is true, we should dial an ACU whose type is AT. * The phone numbers are in PN, and the call unit is in CU. * * If the PN is an '@', then we consult the PHONES file for * the phone numbers. This file is /etc/phones, unless overriden * by an exported shell variable. * * The data base files must be in the format: * host-name[ \t]*phone-number * with the possibility of multiple phone numbers * for a single host acting as a rotary (in the order * found in the file). */ char * con(void) { char *cp = PN; char *phnum, string[256]; FILE *fd; volatile int tried = 0; if (!DU) { /* regular connect message */ if (CM != NOSTR) parwrite(FD, CM, size(CM)); logent(value(HOST), "", DV, "call completed"); return (NOSTR); } /* * @ =>'s use data base in PHONES environment variable * otherwise, use /etc/phones */ signal(SIGINT, acuabort); signal(SIGQUIT, acuabort); if (setjmp(jmpbuf)) { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); printf("\ncall aborted\n"); logent(value(HOST), "", "", "call aborted"); if (acu != NOACU) { setboolean(value(VERBOSE), FALSE); if (conflag) disconnect(NOSTR); else (*acu->acu_abort)(); } return ("interrupt"); } if ((acu = acutype(AT)) == NOACU) return ("unknown ACU type"); if (*cp != '@') { while (*cp) { phnum = cp; cp += strcspn(cp, ","); if (*cp != '\0') *cp++ = '\0'; if (strlen(phnum) == 0) continue; conflag = (*acu->acu_dialer)(phnum, CU); if (conflag) break; logent(value(HOST), phnum, acu->acu_name, "call failed"); tried++; } } else { if ((fd = fopen(PH, "r")) == NOFILE) { printf("%s: ", PH); return ("can't open phone number file"); } while (fgets(string, sizeof(string), fd) != NOSTR) { cp = &string[strcspn(string, " \t\n")]; if (*cp != '\0') *cp++ = '\0'; if (strcmp(string, value(HOST)) != 0) continue; cp += strspn(cp, " \t\n"); phnum = cp; *(cp + strcspn(cp, ",\n")) = '\0'; if (strlen(phnum) == 0) continue; conflag = (*acu->acu_dialer)(phnum, CU); if (conflag) break; logent(value(HOST), phnum, acu->acu_name, "call failed"); tried++; } fclose(fd); } if (conflag) { if (CM != NOSTR) parwrite(FD, CM, size(CM)); logent(value(HOST), phnum, acu->acu_name, "call completed"); return (NOSTR); } else if (!tried) { logent(value(HOST), "", acu->acu_name, "missing phone number"); return ("missing phone number"); } else { (*acu->acu_abort)(); return ("call failed"); } } void disconnect(char *reason) { if (!conflag) { logent(value(HOST), "", DV, "call terminated"); return; } if (reason == NOSTR) { logent(value(HOST), "", acu->acu_name, "call terminated"); if (boolean(value(VERBOSE))) printf("\r\ndisconnecting..."); } else logent(value(HOST), "", acu->acu_name, reason); (*acu->acu_disconnect)(); } static void acuabort(int s) { signal(s, SIG_IGN); longjmp(jmpbuf, 1); } static acu_t * acutype(char *s) { acu_t *p; extern acu_t acutable[]; - for (p = acutable; p->acu_name != '\0'; p++) + for (p = acutable; p->acu_name != NULL; p++) if (!strcmp(s, p->acu_name)) return (p); return (NOACU); } Index: stable/12/usr.sbin/fwcontrol/fwcontrol.c =================================================================== --- stable/12/usr.sbin/fwcontrol/fwcontrol.c (revision 353576) +++ stable/12/usr.sbin/fwcontrol/fwcontrol.c (revision 353577) @@ -1,1091 +1,1091 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (C) 2002 * Hidetoshi Shimokawa. 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 Hidetoshi Shimokawa. * * 4. Neither the name of the author 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. */ #if defined(__FreeBSD__) #include __FBSDID("$FreeBSD$"); #endif #include #include #include #include #include #include #include #if defined(__FreeBSD__) #include #include #include #include #include #elif defined(__NetBSD__) #include "eui64.h" #include #include #include #include #else #warning "You need to add support for your OS" #endif #include #include #include #include #include #include #include #include #include #include #include "fwmethods.h" static void sysctl_set_int(const char *, int); static void usage(void) { fprintf(stderr, "%s [-u bus_num] [-prt] [-c node] [-d node] [-o node] [-s node]\n" "\t [-l file] [-g gap_count] [-f force_root ] [-b pri_req]\n" "\t [-M mode] [-R filename] [-S filename] [-m EUI64 | hostname]\n" "\t-u: specify bus number\n" "\t-p: Display current PHY register settings\n" "\t-r: bus reset\n" "\t-t: read topology map\n" "\t-c: read configuration ROM\n" "\t-d: hex dump of configuration ROM\n" "\t-o: send link-on packet to the node\n" "\t-s: write RESET_START register on the node\n" "\t-l: load and parse hex dump file of configuration ROM\n" "\t-g: set gap count\n" "\t-f: force root node\n" "\t-b: set PRIORITY_BUDGET register on all supported nodes\n" "\t-M: specify dv or mpeg\n" "\t-R: Receive DV or MPEG TS stream\n" "\t-S: Send DV stream\n" "\t-m: set fwmem target\n" , getprogname() ); fprintf(stderr, "\n"); } static void fweui2eui64(const struct fw_eui64 *fweui, struct eui64 *eui) { *(u_int32_t*)&(eui->octet[0]) = htonl(fweui->hi); *(u_int32_t*)&(eui->octet[4]) = htonl(fweui->lo); } static void get_dev(int fd, struct fw_devlstreq *data) { if (data == NULL) err(EX_SOFTWARE, "%s: data malloc", __func__); if( ioctl(fd, FW_GDEVLST, data) < 0) { err(EX_IOERR, "%s: ioctl", __func__); } } static int str2node(int fd, const char *nodestr) { struct eui64 eui, tmpeui; struct fw_devlstreq *data; char *endptr; int i, node; - if (nodestr == '\0') + if (nodestr == NULL || *nodestr == '\0') return (-1); /* * Deal with classic node specifications. */ node = strtol(nodestr, &endptr, 0); if (*endptr == '\0') goto gotnode; /* * Try to get an eui and match it against available nodes. */ if (eui64_hostton(nodestr, &eui) != 0 && eui64_aton(nodestr, &eui) != 0) return (-1); data = (struct fw_devlstreq *)malloc(sizeof(struct fw_devlstreq)); if (data == NULL) err(EX_SOFTWARE, "%s: data malloc", __func__); get_dev(fd,data); for (i = 0; i < data->info_len; i++) { fweui2eui64(&data->dev[i].eui, &tmpeui); if (memcmp(&eui, &tmpeui, sizeof(struct eui64)) == 0) { node = data->dev[i].dst; free(data); goto gotnode; } } if (i >= data->info_len) { if (data != NULL) free(data); return (-1); } gotnode: if (node < 0 || node > 63) return (-1); else return (node); } static void list_dev(int fd) { struct fw_devlstreq *data; struct fw_devinfo *devinfo; struct eui64 eui; char addr[EUI64_SIZ], hostname[40]; int i; data = (struct fw_devlstreq *)malloc(sizeof(struct fw_devlstreq)); if (data == NULL) err(EX_SOFTWARE, "%s:data malloc", __func__); get_dev(fd, data); printf("%d devices (info_len=%d)\n", data->n, data->info_len); printf("node EUI64 status hostname\n"); for (i = 0; i < data->info_len; i++) { devinfo = &data->dev[i]; fweui2eui64(&devinfo->eui, &eui); eui64_ntoa(&eui, addr, sizeof(addr)); if (eui64_ntohost(hostname, sizeof(hostname), &eui)) hostname[0] = 0; printf("%4d %s %6d %s\n", (devinfo->status || i == 0) ? devinfo->dst : -1, addr, devinfo->status, hostname ); } free((void *)data); } static u_int32_t read_write_quad(int fd, struct fw_eui64 eui, u_int32_t addr_lo, int readmode, u_int32_t data) { struct fw_asyreq *asyreq; u_int32_t *qld, res; asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 16); if (asyreq == NULL) err(EX_SOFTWARE, "%s:asyreq malloc", __func__); asyreq->req.len = 16; #if 0 asyreq->req.type = FWASREQNODE; asyreq->pkt.mode.rreqq.dst = FWLOCALBUS | node; #else asyreq->req.type = FWASREQEUI; asyreq->req.dst.eui = eui; #endif asyreq->pkt.mode.rreqq.tlrt = 0; if (readmode) asyreq->pkt.mode.rreqq.tcode = FWTCODE_RREQQ; else asyreq->pkt.mode.rreqq.tcode = FWTCODE_WREQQ; asyreq->pkt.mode.rreqq.dest_hi = 0xffff; asyreq->pkt.mode.rreqq.dest_lo = addr_lo; qld = (u_int32_t *)&asyreq->pkt; if (!readmode) asyreq->pkt.mode.wreqq.data = htonl(data); if (ioctl(fd, FW_ASYREQ, asyreq) < 0) { err(EX_IOERR, "%s: ioctl", __func__); } res = qld[3]; free(asyreq); if (readmode) return ntohl(res); else return 0; } /* * Send a PHY Config Packet * ieee 1394a-2005 4.3.4.3 * * Message ID Root ID R T Gap Count * 00(2 bits) (6 bits) 1 1 (6 bits) * * if "R" is set, then Root ID will be the next * root node upon the next bus reset. * if "T" is set, then Gap Count will be the * value that all nodes use for their Gap Count * if "R" and "T" are not set, then this message * is either ignored or interpreted as an extended * PHY config Packet as per 1394a-2005 4.3.4.4 */ static void send_phy_config(int fd, int root_node, int gap_count) { struct fw_asyreq *asyreq; asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 12); if (asyreq == NULL) err(EX_SOFTWARE, "%s:asyreq malloc", __func__); asyreq->req.len = 12; asyreq->req.type = FWASREQNODE; asyreq->pkt.mode.ld[0] = 0; asyreq->pkt.mode.ld[1] = 0; asyreq->pkt.mode.common.tcode = FWTCODE_PHY; if (root_node >= 0) asyreq->pkt.mode.ld[1] |= ((root_node << 24) | (1 << 23)); if (gap_count >= 0) asyreq->pkt.mode.ld[1] |= ((1 << 22) | (gap_count << 16)); asyreq->pkt.mode.ld[2] = ~asyreq->pkt.mode.ld[1]; printf("send phy_config root_node=%d gap_count=%d\n", root_node, gap_count); if (ioctl(fd, FW_ASYREQ, asyreq) < 0) err(EX_IOERR, "%s: ioctl", __func__); free(asyreq); } static void link_on(int fd, int node) { struct fw_asyreq *asyreq; asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 12); if (asyreq == NULL) err(EX_SOFTWARE, "%s:asyreq malloc", __func__); asyreq->req.len = 12; asyreq->req.type = FWASREQNODE; asyreq->pkt.mode.common.tcode = FWTCODE_PHY; asyreq->pkt.mode.ld[1] |= (1 << 30) | ((node & 0x3f) << 24); asyreq->pkt.mode.ld[2] = ~asyreq->pkt.mode.ld[1]; if (ioctl(fd, FW_ASYREQ, asyreq) < 0) err(EX_IOERR, "%s: ioctl", __func__); free(asyreq); } static void reset_start(int fd, int node) { struct fw_asyreq *asyreq; asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 16); if (asyreq == NULL) err(EX_SOFTWARE, "%s:asyreq malloc", __func__); asyreq->req.len = 16; asyreq->req.type = FWASREQNODE; asyreq->pkt.mode.wreqq.dst = FWLOCALBUS | (node & 0x3f); asyreq->pkt.mode.wreqq.tlrt = 0; asyreq->pkt.mode.wreqq.tcode = FWTCODE_WREQQ; asyreq->pkt.mode.wreqq.dest_hi = 0xffff; asyreq->pkt.mode.wreqq.dest_lo = 0xf0000000 | RESET_START; asyreq->pkt.mode.wreqq.data = htonl(0x1); if (ioctl(fd, FW_ASYREQ, asyreq) < 0) err(EX_IOERR, "%s: ioctl", __func__); free(asyreq); } static void set_pri_req(int fd, u_int32_t pri_req) { struct fw_devlstreq *data; struct fw_devinfo *devinfo; struct eui64 eui; char addr[EUI64_SIZ]; u_int32_t max, reg, old; int i; data = (struct fw_devlstreq *)malloc(sizeof(struct fw_devlstreq)); if (data == NULL) err(EX_SOFTWARE, "%s:data malloc", __func__); get_dev(fd, data); #define BUGET_REG 0xf0000218 for (i = 0; i < data->info_len; i++) { devinfo = &data->dev[i]; if (!devinfo->status) continue; reg = read_write_quad(fd, devinfo->eui, BUGET_REG, 1, 0); fweui2eui64(&devinfo->eui, &eui); eui64_ntoa(&eui, addr, sizeof(addr)); printf("%d %s, %08x", devinfo->dst, addr, reg); if (reg > 0) { old = (reg & 0x3f); max = (reg & 0x3f00) >> 8; if (pri_req > max) pri_req = max; printf(" 0x%x -> 0x%x\n", old, pri_req); read_write_quad(fd, devinfo->eui, BUGET_REG, 0, pri_req); } else { printf("\n"); } } free((void *)data); } static void parse_bus_info_block(u_int32_t *p) { char addr[EUI64_SIZ]; struct bus_info *bi; struct eui64 eui; bi = (struct bus_info *)p; fweui2eui64(&bi->eui64, &eui); eui64_ntoa(&eui, addr, sizeof(addr)); printf("bus_name: 0x%04x\n" "irmc:%d cmc:%d isc:%d bmc:%d pmc:%d\n" "cyc_clk_acc:%d max_rec:%d max_rom:%d\n" "generation:%d link_spd:%d\n" "EUI64: %s\n", bi->bus_name, bi->irmc, bi->cmc, bi->isc, bi->bmc, bi->pmc, bi->cyc_clk_acc, bi->max_rec, bi->max_rom, bi->generation, bi->link_spd, addr); } static int get_crom(int fd, int node, void *crom_buf, int len) { struct fw_crom_buf buf; int i, error; struct fw_devlstreq *data; data = (struct fw_devlstreq *)malloc(sizeof(struct fw_devlstreq)); if (data == NULL) err(EX_SOFTWARE, "%s:data malloc", __func__); get_dev(fd, data); for (i = 0; i < data->info_len; i++) { if (data->dev[i].dst == node && data->dev[i].eui.lo != 0) break; } if (i == data->info_len) errx(1, "no such node %d.", node); else buf.eui = data->dev[i].eui; free((void *)data); buf.len = len; buf.ptr = crom_buf; bzero(crom_buf, len); if ((error = ioctl(fd, FW_GCROM, &buf)) < 0) { err(EX_IOERR, "%s: ioctl", __func__); } return error; } static void show_crom(u_int32_t *crom_buf) { int i; struct crom_context cc; char *desc, info[256]; static const char *key_types = "ICLD"; struct csrreg *reg; struct csrdirectory *dir; struct csrhdr *hdr; u_int16_t crc; printf("first quad: 0x%08x ", *crom_buf); if (crom_buf[0] == 0) { printf("(Invalid Configuration ROM)\n"); return; } hdr = (struct csrhdr *)crom_buf; if (hdr->info_len == 1) { /* minimum ROM */ reg = (struct csrreg *)hdr; printf("verndor ID: 0x%06x\n", reg->val); return; } printf("info_len=%d crc_len=%d crc=0x%04x", hdr->info_len, hdr->crc_len, hdr->crc); crc = crom_crc(crom_buf+1, hdr->crc_len); if (crc == hdr->crc) printf("(OK)\n"); else printf("(NG)\n"); parse_bus_info_block(crom_buf+1); crom_init_context(&cc, crom_buf); dir = cc.stack[0].dir; if (!dir) { printf("no root directory - giving up\n"); return; } printf("root_directory: len=0x%04x(%d) crc=0x%04x", dir->crc_len, dir->crc_len, dir->crc); crc = crom_crc((u_int32_t *)&dir->entry[0], dir->crc_len); if (crc == dir->crc) printf("(OK)\n"); else printf("(NG)\n"); if (dir->crc_len < 1) return; while (cc.depth >= 0) { desc = crom_desc(&cc, info, sizeof(info)); reg = crom_get(&cc); for (i = 0; i < cc.depth; i++) printf("\t"); printf("%02x(%c:%02x) %06x %s: %s\n", reg->key, key_types[(reg->key & CSRTYPE_MASK)>>6], reg->key & CSRKEY_MASK, reg->val, desc, info); crom_next(&cc); } } #define DUMP_FORMAT "%08x %08x %08x %08x %08x %08x %08x %08x\n" static void dump_crom(u_int32_t *p) { int len=1024, i; for (i = 0; i < len/(4*8); i ++) { printf(DUMP_FORMAT, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); p += 8; } } static void load_crom(char *filename, u_int32_t *p) { FILE *file; int len=1024, i; if ((file = fopen(filename, "r")) == NULL) err(1, "load_crom %s", filename); for (i = 0; i < len/(4*8); i ++) { fscanf(file, DUMP_FORMAT, p, p+1, p+2, p+3, p+4, p+5, p+6, p+7); p += 8; } fclose(file); } static void show_topology_map(int fd) { struct fw_topology_map *tmap; union fw_self_id sid; int i; static const char *port_status[] = {" ", "-", "P", "C"}; static const char *pwr_class[] = {" 0W", "15W", "30W", "45W", "-1W", "-2W", "-5W", "-9W"}; static const char *speed[] = {"S100", "S200", "S400", "S800"}; tmap = malloc(sizeof(struct fw_topology_map)); if (tmap == NULL) err(EX_SOFTWARE, "%s:tmap malloc", __func__); if (ioctl(fd, FW_GTPMAP, tmap) < 0) { err(EX_IOERR, "%s: ioctl", __func__); } printf("crc_len: %d generation:%d node_count:%d sid_count:%d\n", tmap->crc_len, tmap->generation, tmap->node_count, tmap->self_id_count); printf("id link gap_cnt speed delay cIRM power port0 port1 port2" " ini more\n"); for (i = 0; i < tmap->crc_len - 2; i++) { sid = tmap->self_id[i]; if (sid.p0.sequel) { printf("%02d sequel packet\n", sid.p0.phy_id); continue; } printf("%02d %2d %2d %4s %d %3s" " %s %s %s %d %d\n", sid.p0.phy_id, sid.p0.link_active, sid.p0.gap_count, speed[sid.p0.phy_speed], sid.p0.contender, pwr_class[sid.p0.power_class], port_status[sid.p0.port0], port_status[sid.p0.port1], port_status[sid.p0.port2], sid.p0.initiated_reset, sid.p0.more_packets ); } free(tmap); } static void read_phy_registers(int fd, u_int8_t *buf, int offset, int len) { struct fw_reg_req_t reg; int i; for (i = 0; i < len; i++) { reg.addr = offset + i; if (ioctl(fd, FWOHCI_RDPHYREG, ®) < 0) err(EX_IOERR, "%s: ioctl", __func__); buf[i] = (u_int8_t) reg.data; printf("0x%02x ", reg.data); } printf("\n"); } static void read_phy_page(int fd, u_int8_t *buf, int page, int port) { struct fw_reg_req_t reg; reg.addr = 0x7; reg.data = ((page & 7) << 5) | (port & 0xf); if (ioctl(fd, FWOHCI_WRPHYREG, ®) < 0) err(EX_IOERR, "%s: ioctl", __func__); read_phy_registers(fd, buf, 8, 8); } static void dump_phy_registers(int fd) { struct phyreg_base b; struct phyreg_page0 p; struct phyreg_page1 v; int i; printf("=== base register ===\n"); read_phy_registers(fd, (u_int8_t *)&b, 0, 8); printf( "Physical_ID:%d R:%d CPS:%d\n" "RHB:%d IBR:%d Gap_Count:%d\n" "Extended:%d Num_Ports:%d\n" "PHY_Speed:%d Delay:%d\n" "LCtrl:%d C:%d Jitter:%d Pwr_Class:%d\n" "WDIE:%d ISBR:%d CTOI:%d CPSI:%d STOI:%d PEI:%d EAA:%d EMC:%d\n" "Max_Legacy_SPD:%d BLINK:%d Bridge:%d\n" "Page_Select:%d Port_Select%d\n", b.phy_id, b.r, b.cps, b.rhb, b.ibr, b.gap_count, b.extended, b.num_ports, b.phy_speed, b.delay, b.lctrl, b.c, b.jitter, b.pwr_class, b.wdie, b.isbr, b.ctoi, b.cpsi, b.stoi, b.pei, b.eaa, b.emc, b.legacy_spd, b.blink, b.bridge, b.page_select, b.port_select ); for (i = 0; i < b.num_ports; i ++) { printf("\n=== page 0 port %d ===\n", i); read_phy_page(fd, (u_int8_t *)&p, 0, i); printf( "Astat:%d BStat:%d Ch:%d Con:%d RXOK:%d Dis:%d\n" "Negotiated_speed:%d PIE:%d Fault:%d Stanby_fault:%d Disscrm:%d B_Only:%d\n" "DC_connected:%d Max_port_speed:%d LPP:%d Cable_speed:%d\n" "Connection_unreliable:%d Beta_mode:%d\n" "Port_error:0x%x\n" "Loop_disable:%d In_standby:%d Hard_disable:%d\n", p.astat, p.bstat, p.ch, p.con, p.rxok, p.dis, p.negotiated_speed, p.pie, p.fault, p.stanby_fault, p.disscrm, p.b_only, p.dc_connected, p.max_port_speed, p.lpp, p.cable_speed, p.connection_unreliable, p.beta_mode, p.port_error, p.loop_disable, p.in_standby, p.hard_disable ); } printf("\n=== page 1 ===\n"); read_phy_page(fd, (u_int8_t *)&v, 1, 0); printf( "Compliance:%d\n" "Vendor_ID:0x%06x\n" "Product_ID:0x%06x\n", v.compliance, (v.vendor_id[0] << 16) | (v.vendor_id[1] << 8) | v.vendor_id[2], (v.product_id[0] << 16) | (v.product_id[1] << 8) | v.product_id[2] ); } static int open_dev(int *fd, char *devname) { if (*fd < 0) { *fd = open(devname, O_RDWR); if (*fd < 0) return(-1); } return(0); } static void sysctl_set_int(const char *name, int val) { if (sysctlbyname(name, NULL, NULL, &val, sizeof(int)) < 0) err(1, "sysctl %s failed.", name); } static fwmethod * detect_recv_fn(int fd, char ich) { char *buf; struct fw_isochreq isoreq; struct fw_isobufreq bufreq; int len; u_int32_t *ptr; struct ciphdr *ciph; fwmethod *retfn; #define RECV_NUM_PACKET 16 #define RECV_PACKET_SZ 1024 bufreq.rx.nchunk = 8; bufreq.rx.npacket = RECV_NUM_PACKET; bufreq.rx.psize = RECV_PACKET_SZ; bufreq.tx.nchunk = 0; bufreq.tx.npacket = 0; bufreq.tx.psize = 0; if (ioctl(fd, FW_SSTBUF, &bufreq) < 0) err(EX_IOERR, "%s: ioctl FW_SSTBUF", __func__); isoreq.ch = ich & 0x3f; isoreq.tag = (ich >> 6) & 3; if (ioctl(fd, FW_SRSTREAM, &isoreq) < 0) err(EX_IOERR, "%s: ioctl FW_SRSTREAM", __func__); buf = (char *)malloc(RECV_NUM_PACKET * RECV_PACKET_SZ); if (buf == NULL) err(EX_SOFTWARE, "%s:buf malloc", __func__); /* * fwdev.c seems to return EIO on error and * the return value of the last uiomove * on success. For now, checking that the * return is not less than zero should be * sufficient. fwdev.c::fw_read() should * return the total length read, not the value * of the last uiomove(). */ len = read(fd, buf, RECV_NUM_PACKET * RECV_PACKET_SZ); if (len < 0) err(EX_IOERR, "%s: error reading from device", __func__); ptr = (u_int32_t *) buf; ciph = (struct ciphdr *)(ptr + 1); switch(ciph->fmt) { case CIP_FMT_DVCR: fprintf(stderr, "Detected DV format on input.\n"); retfn = dvrecv; break; case CIP_FMT_MPEG: fprintf(stderr, "Detected MPEG TS format on input.\n"); retfn = mpegtsrecv; break; default: errx(1, "Unsupported format for receiving: fmt=0x%x", ciph->fmt); } free(buf); return retfn; } int main(int argc, char **argv) { #define MAX_BOARDS 10 u_int32_t crom_buf[1024/4]; u_int32_t crom_buf_hex[1024/4]; char devbase[64]; const char *device_string = "/dev/fw"; int fd = -1, ch, len=1024; int32_t current_board = 0; /* * If !command_set, then -u will display the nodes for the board. * This emulates the previous behavior when -u is passed by itself */ bool command_set = false; bool open_needed = false; long tmp; struct fw_eui64 eui; struct eui64 target; fwmethod *recvfn = NULL; /* * Holders for which functions * to iterate through */ bool display_board_only = false; bool display_crom = false; bool send_bus_reset = false; bool display_crom_hex = false; bool load_crom_from_file = false; bool set_fwmem_target = false; bool dump_topology = false; bool dump_phy_reg = false; int32_t priority_budget = -1; int32_t set_root_node = -1; int32_t set_gap_count = -1; int32_t send_link_on = -1; int32_t send_reset_start = -1; char *crom_string = NULL; char *crom_string_hex = NULL; char *recv_data = NULL; char *send_data = NULL; if (argc < 2) { for (current_board = 0; current_board < MAX_BOARDS; current_board++) { snprintf(devbase, sizeof(devbase), "%s%d.0", device_string, current_board); if (open_dev(&fd, devbase) < 0) { if (current_board == 0) { usage(); err(EX_IOERR, "%s: Error opening firewire controller #%d %s", __func__, current_board, devbase); } return(EIO); } list_dev(fd); close(fd); fd = -1; } } /* * Parse all command line options, then execute requested operations. */ while ((ch = getopt(argc, argv, "M:f:g:m:o:s:b:prtc:d:l:u:R:S:")) != -1) { switch(ch) { case 'b': priority_budget = strtol(optarg, NULL, 0); if (priority_budget < 0 || priority_budget > INT32_MAX) errx(EX_USAGE, "%s: priority_budget out of range: %s", __func__, optarg); command_set = true; open_needed = true; display_board_only = false; break; case 'c': crom_string = malloc(strlen(optarg)+1); if (crom_string == NULL) err(EX_SOFTWARE, "%s:crom_string malloc", __func__); if ( (strtol(crom_string, NULL, 0) < 0) || strtol(crom_string, NULL, 0) > MAX_BOARDS) errx(EX_USAGE, "%s:Invalid value for node", __func__); strcpy(crom_string, optarg); display_crom = 1; open_needed = true; command_set = true; display_board_only = false; break; case 'd': crom_string_hex = malloc(strlen(optarg)+1); if (crom_string_hex == NULL) err(EX_SOFTWARE, "%s:crom_string_hex malloc", __func__); strcpy(crom_string_hex, optarg); display_crom_hex = 1; open_needed = true; command_set = true; display_board_only = false; break; case 'f': #define MAX_PHY_CONFIG 0x3f set_root_node = strtol(optarg, NULL, 0); if ( (set_root_node < 0) || (set_root_node > MAX_PHY_CONFIG) ) errx(EX_USAGE, "%s:set_root_node out of range", __func__); open_needed = true; command_set = true; display_board_only = false; break; case 'g': set_gap_count = strtol(optarg, NULL, 0); if ( (set_gap_count < 0) || (set_gap_count > MAX_PHY_CONFIG) ) errx(EX_USAGE, "%s:set_gap_count out of range", __func__); open_needed = true; command_set = true; display_board_only = false; break; case 'l': load_crom_from_file = 1; load_crom(optarg, crom_buf); command_set = true; display_board_only = false; break; case 'm': set_fwmem_target = 1; open_needed = 0; command_set = true; display_board_only = false; if (eui64_hostton(optarg, &target) != 0 && eui64_aton(optarg, &target) != 0) errx(EX_USAGE, "%s: invalid target: %s", __func__, optarg); break; case 'o': send_link_on = str2node(fd, optarg); if ( (send_link_on < 0) || (send_link_on > MAX_PHY_CONFIG) ) errx(EX_USAGE, "%s: node out of range: %s\n",__func__, optarg); open_needed = true; command_set = true; display_board_only = false; break; case 'p': dump_phy_reg = 1; open_needed = true; command_set = true; display_board_only = false; break; case 'r': send_bus_reset = 1; open_needed = true; command_set = true; display_board_only = false; break; case 's': send_reset_start = str2node(fd, optarg); if ( (send_reset_start < 0) || (send_reset_start > MAX_PHY_CONFIG) ) errx(EX_USAGE, "%s: node out of range: %s\n", __func__, optarg); open_needed = true; command_set = true; display_board_only = false; break; case 't': dump_topology = 1; open_needed = true; command_set = true; display_board_only = false; break; case 'u': if(!command_set) display_board_only = true; current_board = strtol(optarg, NULL, 0); open_needed = true; break; case 'M': switch (optarg[0]) { case 'm': recvfn = mpegtsrecv; break; case 'd': recvfn = dvrecv; break; default: errx(EX_USAGE, "unrecognized method: %s", optarg); } command_set = true; display_board_only = false; break; case 'R': recv_data = malloc(strlen(optarg)+1); if (recv_data == NULL) err(EX_SOFTWARE, "%s:recv_data malloc", __func__); strcpy(recv_data, optarg); open_needed = false; command_set = true; display_board_only = false; break; case 'S': send_data = malloc(strlen(optarg)+1); if (send_data == NULL) err(EX_SOFTWARE, "%s:send_data malloc", __func__); strcpy(send_data, optarg); open_needed = true; command_set = true; display_board_only = false; break; case '?': default: usage(); warnc(EINVAL, "%s: Unknown command line arguments", __func__); return 0; } } /* end while */ /* * Catch the error case when the user * executes the command with non ''-'' * delimited arguments. * Generate the usage() display and exit. */ if (!command_set && !display_board_only) { usage(); warnc(EINVAL, "%s: Unknown command line arguments", __func__); return 0; } /* * If -u is passed, execute * command for that card only. * * If -u is not passed, execute * command for card 0 only. * */ if(open_needed){ snprintf(devbase, sizeof(devbase), "%s%d.0", device_string, current_board); if (open_dev(&fd, devbase) < 0) { err(EX_IOERR, "%s: Error opening firewire controller #%d %s", __func__, current_board, devbase); } } /* * display the nodes on this board "-u" * only */ if (display_board_only) list_dev(fd); /* * dump_phy_reg "-p" */ if (dump_phy_reg) dump_phy_registers(fd); /* * send a BUS_RESET Event "-r" */ if (send_bus_reset) { if(ioctl(fd, FW_IBUSRST, &tmp) < 0) err(EX_IOERR, "%s: Ioctl of bus reset failed for %s", __func__, devbase); } /* * Print out the CROM for this node "-c" */ if (display_crom) { tmp = str2node(fd, crom_string); get_crom(fd, tmp, crom_buf, len); show_crom(crom_buf); free(crom_string); } /* * Hex Dump the CROM for this node "-d" */ if (display_crom_hex) { tmp = str2node(fd, crom_string_hex); get_crom(fd, tmp, crom_buf_hex, len); dump_crom(crom_buf_hex); free(crom_string_hex); } /* * Set Priority Budget to value for this node "-b" */ if (priority_budget >= 0) set_pri_req(fd, priority_budget); /* * Explicitly set the root node of this bus to value "-f" */ if (set_root_node >= 0) send_phy_config(fd, set_root_node, -1); /* * Set the gap count for this card/bus "-g" */ if (set_gap_count >= 0) send_phy_config(fd, -1, set_gap_count); /* * Load a CROM from a file "-l" */ if (load_crom_from_file) show_crom(crom_buf); /* * Set the fwmem target for a node to argument "-m" */ if (set_fwmem_target) { eui.hi = ntohl(*(u_int32_t*)&(target.octet[0])); eui.lo = ntohl(*(u_int32_t*)&(target.octet[4])); #if defined(__FreeBSD__) sysctl_set_int("hw.firewire.fwmem.eui64_hi", eui.hi); sysctl_set_int("hw.firewire.fwmem.eui64_lo", eui.lo); #elif defined(__NetBSD__) sysctl_set_int("hw.fwmem.eui64_hi", eui.hi); sysctl_set_int("hw.fwmem.eui64_lo", eui.lo); #else #warning "You need to add support for your OS" #endif } /* * Send a link on to this board/bus "-o" */ if (send_link_on >= 0) link_on(fd, send_link_on); /* * Send a reset start to this board/bus "-s" */ if (send_reset_start >= 0) reset_start(fd, send_reset_start); /* * Dump the node topology for this board/bus "-t" */ if (dump_topology) show_topology_map(fd); /* * Receive data file from node "-R" */ #define TAG (1<<6) #define CHANNEL 63 if (recv_data != NULL){ if (recvfn == NULL) { /* guess... */ recvfn = detect_recv_fn(fd, TAG | CHANNEL); close(fd); fd = -1; } snprintf(devbase, sizeof(devbase), "%s%d.0", device_string, current_board); if (open_dev(&fd, devbase) < 0) err(EX_IOERR, "%s: Error opening firewire controller #%d %s in recv_data\n", __func__, current_board, devbase); (*recvfn)(fd, recv_data, TAG | CHANNEL, -1); free(recv_data); } /* * Send data file to node "-S" */ if (send_data != NULL){ dvsend(fd, send_data, TAG | CHANNEL, -1); free(send_data); } if (fd > 0) { close(fd); fd = -1; } return 0; } Index: stable/12 =================================================================== --- stable/12 (revision 353576) +++ stable/12 (revision 353577) Property changes on: stable/12 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r353326