Index: head/bin/csh/config_p.h =================================================================== --- head/bin/csh/config_p.h (revision 289676) +++ head/bin/csh/config_p.h (revision 289677) @@ -1,111 +1,111 @@ /* $FreeBSD$ */ /* * config.h -- configure various defines for tcsh * * All source files should #include this FIRST. * * Edit this to match your system type. */ #ifndef _h_config #define _h_config -/****************** System dependant compilation flags ****************/ +/****************** System dependent compilation flags ****************/ /* * POSIX This system supports IEEE Std 1003.1-1988 (POSIX). */ #define POSIX /* * POSIXJOBS This system supports the optional IEEE Std 1003.1-1988 (POSIX) * job control facilities. */ #define POSIXJOBS /* * VFORK This machine has a vfork(). * It used to be that for job control to work, this define * was mandatory. This is not the case any more. * If you think you still need it, but you don't have vfork, * define this anyway and then do #define vfork fork. * I do this anyway on a Sun because of yellow pages brain damage, * [should not be needed under 4.1] * and on the iris4d cause SGI's fork is sufficiently "virtual" * that vfork isn't necessary. (Besides, SGI's vfork is weird). * Note that some machines eg. rs6000 have a vfork, but not * with the berkeley semantics, so we cannot use it there either. */ #define VFORK /* * BSDJOBS You have BSD-style job control (both process groups and * a tty that deals correctly */ #define BSDJOBS /* * BSDTIMES You have BSD-style process time stuff (like rusage) * This may or may not be true. For example, Apple Unix * (OREO) has BSDJOBS but not BSDTIMES. */ #define BSDTIMES /* * BSDLIMIT You have BSD-style resource limit stuff (getrlimit/setrlimit) */ #define BSDLIMIT /* * TERMIO You have struct termio instead of struct sgttyb. * This is usually the case for SYSV systems, where * BSD uses sgttyb. POSIX systems should define this * anyway, even though they use struct termios. */ #define TERMIO /* * SYSVREL Your machine is SYSV based (HPUX, A/UX) * NOTE: don't do this if you are on a Pyramid -- tcsh is * built in a BSD universe. * Set SYSVREL to 1, 2, 3, or 4, depending the version of System V * you are running. Or set it to 0 if you are not SYSV based */ #define SYSVREL 0 /* * YPBUGS Work around Sun YP bugs that cause expansion of ~username * to send command output to /dev/null */ #undef YPBUGS /****************** local defines *********************/ #if defined(__FreeBSD__) #define NLS_BUGS #define BSD_STYLE_COLORLS /* Use LC_MESSAGES locale category to open the message catalog */ #define MCLoadBySet NL_CAT_LOCALE #define BUFSIZE 8192 #define UTMPX_FILE "/var/run/utx.active" #endif #if defined(__bsdi__) /* * _PATH_TCSHELL if you've change the installation location (vix) */ #include # ifdef _BSDI_VERSION >= 199701 # define _PATH_TCSHELL "/bin/tcsh" # undef SYSMALLOC # define SYSMALLOC # else # define _PATH_TCSHELL "/usr/contrib/bin/tcsh" # endif # undef NLS # undef NLS_CATALOGS #elif defined(__APPLE__) # define SYSMALLOC #endif #endif /* _h_config */ Index: head/sbin/devd/devd.cc =================================================================== --- head/sbin/devd/devd.cc (revision 289676) +++ head/sbin/devd/devd.cc (revision 289677) @@ -1,1243 +1,1243 @@ /*- * Copyright (c) 2002-2010 M. Warner Losh. * 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. * * my_system is a variation on lib/libc/stdlib/system.c: * * Copyright (c) 1988, 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. * 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. */ /* * DEVD control daemon. */ // TODO list: // o devd.conf and devd man pages need a lot of help: // - devd needs to document the unix domain socket // - devd.conf needs more details on the supported statements. #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "devd.h" /* C compatible definitions */ #include "devd.hh" /* C++ class definitions */ #define STREAMPIPE "/var/run/devd.pipe" #define SEQPACKETPIPE "/var/run/devd.seqpacket.pipe" #define CF "/etc/devd.conf" #define SYSCTL "hw.bus.devctl_queue" /* * Since the client socket is nonblocking, we must increase its send buffer to * handle brief event storms. On FreeBSD, AF_UNIX sockets don't have a receive * buffer, so the client can't increate the buffersize by itself. * * For example, when creating a ZFS pool, devd emits one 165 character * resource.fs.zfs.statechange message for each vdev in the pool. A 64k * buffer has enough space for almost 400 drives, which would be very large but * not impossibly large pool. A 128k buffer has enough space for 794 drives, * which is more than can fit in a rack with modern technology. */ #define CLIENT_BUFSIZE 131072 using namespace std; typedef struct client { int fd; int socktype; } client_t; extern FILE *yyin; extern int lineno; static const char notify = '!'; static const char nomatch = '?'; static const char attach = '+'; static const char detach = '-'; static struct pidfh *pfh; static int no_daemon = 0; static int daemonize_quick = 0; static int quiet_mode = 0; static unsigned total_events = 0; static volatile sig_atomic_t got_siginfo = 0; static volatile sig_atomic_t romeo_must_die = 0; static const char *configfile = CF; static void devdlog(int priority, const char* message, ...) __printflike(2, 3); static void event_loop(void); static void usage(void); template void delete_and_clear(vector &v) { typename vector::const_iterator i; for (i = v.begin(); i != v.end(); ++i) delete *i; v.clear(); } config cfg; event_proc::event_proc() : _prio(-1) { _epsvec.reserve(4); } event_proc::~event_proc() { delete_and_clear(_epsvec); } void event_proc::add(eps *eps) { _epsvec.push_back(eps); } bool event_proc::matches(config &c) const { vector::const_iterator i; for (i = _epsvec.begin(); i != _epsvec.end(); ++i) if (!(*i)->do_match(c)) return (false); return (true); } bool event_proc::run(config &c) const { vector::const_iterator i; for (i = _epsvec.begin(); i != _epsvec.end(); ++i) if (!(*i)->do_action(c)) return (false); return (true); } action::action(const char *cmd) : _cmd(cmd) { // nothing } action::~action() { // nothing } static int my_system(const char *command) { pid_t pid, savedpid; int pstat; struct sigaction ign, intact, quitact; sigset_t newsigblock, oldsigblock; if (!command) /* just checking... */ return (1); /* * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save * existing signal dispositions. */ ign.sa_handler = SIG_IGN; ::sigemptyset(&ign.sa_mask); ign.sa_flags = 0; ::sigaction(SIGINT, &ign, &intact); ::sigaction(SIGQUIT, &ign, &quitact); ::sigemptyset(&newsigblock); ::sigaddset(&newsigblock, SIGCHLD); ::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); switch (pid = ::fork()) { case -1: /* error */ break; case 0: /* child */ /* * Restore original signal dispositions and exec the command. */ ::sigaction(SIGINT, &intact, NULL); ::sigaction(SIGQUIT, &quitact, NULL); ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); /* * Close the PID file, and all other open descriptors. * Inherit std{in,out,err} only. */ cfg.close_pidfile(); ::closefrom(3); ::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL); ::_exit(127); default: /* parent */ savedpid = pid; do { pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0); } while (pid == -1 && errno == EINTR); break; } ::sigaction(SIGINT, &intact, NULL); ::sigaction(SIGQUIT, &quitact, NULL); ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); return (pid == -1 ? -1 : pstat); } bool action::do_action(config &c) { string s = c.expand_string(_cmd.c_str()); devdlog(LOG_INFO, "Executing '%s'\n", s.c_str()); my_system(s.c_str()); return (true); } match::match(config &c, const char *var, const char *re) : _inv(re[0] == '!'), _var(var), _re(c.expand_string(_inv ? re + 1 : re, "^", "$")) { regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); } match::~match() { regfree(&_regex); } bool match::do_match(config &c) { const string &value = c.get_variable(_var); bool retval; /* * This function gets called WAY too often to justify calling syslog() * each time, even at LOG_DEBUG. Because if syslogd isn't running, it * can consume excessive amounts of systime inside of connect(). Only * log when we're in -d mode. */ if (no_daemon) { devdlog(LOG_DEBUG, "Testing %s=%s against %s, invert=%d\n", _var.c_str(), value.c_str(), _re.c_str(), _inv); } retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); if (_inv == 1) retval = (retval == 0) ? 1 : 0; return (retval); } #include #include #include media::media(config &, const char *var, const char *type) : _var(var), _type(-1) { static struct ifmedia_description media_types[] = { { IFM_ETHER, "Ethernet" }, { IFM_TOKEN, "Tokenring" }, { IFM_FDDI, "FDDI" }, { IFM_IEEE80211, "802.11" }, { IFM_ATM, "ATM" }, { -1, "unknown" }, { 0, NULL }, }; for (int i = 0; media_types[i].ifmt_string != NULL; ++i) if (strcasecmp(type, media_types[i].ifmt_string) == 0) { _type = media_types[i].ifmt_word; break; } } media::~media() { } bool media::do_match(config &c) { string value; struct ifmediareq ifmr; bool retval; int s; // Since we can be called from both a device attach/detach // context where device-name is defined and what we want, // as well as from a link status context, where subsystem is // the name of interest, first try device-name and fall back // to subsystem if none exists. value = c.get_variable("device-name"); if (value.empty()) value = c.get_variable("subsystem"); devdlog(LOG_DEBUG, "Testing media type of %s against 0x%x\n", value.c_str(), _type); retval = false; s = socket(PF_INET, SOCK_DGRAM, 0); if (s >= 0) { memset(&ifmr, 0, sizeof(ifmr)); strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && ifmr.ifm_status & IFM_AVALID) { devdlog(LOG_DEBUG, "%s has media type 0x%x\n", value.c_str(), IFM_TYPE(ifmr.ifm_active)); retval = (IFM_TYPE(ifmr.ifm_active) == _type); } else if (_type == -1) { devdlog(LOG_DEBUG, "%s has unknown media type\n", value.c_str()); retval = true; } close(s); } return (retval); } const string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; const string var_list::nothing = ""; const string & var_list::get_variable(const string &var) const { map::const_iterator i; i = _vars.find(var); if (i == _vars.end()) return (var_list::bogus); return (i->second); } bool var_list::is_set(const string &var) const { return (_vars.find(var) != _vars.end()); } void var_list::set_variable(const string &var, const string &val) { /* * This function gets called WAY too often to justify calling syslog() * each time, even at LOG_DEBUG. Because if syslogd isn't running, it * can consume excessive amounts of systime inside of connect(). Only * log when we're in -d mode. */ if (no_daemon) devdlog(LOG_DEBUG, "setting %s=%s\n", var.c_str(), val.c_str()); _vars[var] = val; } void config::reset(void) { _dir_list.clear(); delete_and_clear(_var_list_table); delete_and_clear(_attach_list); delete_and_clear(_detach_list); delete_and_clear(_nomatch_list); delete_and_clear(_notify_list); } void config::parse_one_file(const char *fn) { devdlog(LOG_DEBUG, "Parsing %s\n", fn); yyin = fopen(fn, "r"); if (yyin == NULL) err(1, "Cannot open config file %s", fn); lineno = 1; if (yyparse() != 0) errx(1, "Cannot parse %s at line %d", fn, lineno); fclose(yyin); } void config::parse_files_in_dir(const char *dirname) { DIR *dirp; struct dirent *dp; char path[PATH_MAX]; devdlog(LOG_DEBUG, "Parsing files in %s\n", dirname); dirp = opendir(dirname); if (dirp == NULL) return; readdir(dirp); /* Skip . */ readdir(dirp); /* Skip .. */ while ((dp = readdir(dirp)) != NULL) { if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { snprintf(path, sizeof(path), "%s/%s", dirname, dp->d_name); parse_one_file(path); } } closedir(dirp); } class epv_greater { public: int operator()(event_proc *const&l1, event_proc *const&l2) const { return (l1->get_priority() > l2->get_priority()); } }; void config::sort_vector(vector &v) { stable_sort(v.begin(), v.end(), epv_greater()); } void config::parse(void) { vector::const_iterator i; parse_one_file(configfile); for (i = _dir_list.begin(); i != _dir_list.end(); ++i) parse_files_in_dir((*i).c_str()); sort_vector(_attach_list); sort_vector(_detach_list); sort_vector(_nomatch_list); sort_vector(_notify_list); } void config::open_pidfile() { pid_t otherpid; if (_pidfile.empty()) return; pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); if (pfh == NULL) { if (errno == EEXIST) errx(1, "devd already running, pid: %d", (int)otherpid); warn("cannot open pid file"); } } void config::write_pidfile() { pidfile_write(pfh); } void config::close_pidfile() { pidfile_close(pfh); } void config::remove_pidfile() { pidfile_remove(pfh); } void config::add_attach(int prio, event_proc *p) { p->set_priority(prio); _attach_list.push_back(p); } void config::add_detach(int prio, event_proc *p) { p->set_priority(prio); _detach_list.push_back(p); } void config::add_directory(const char *dir) { _dir_list.push_back(string(dir)); } void config::add_nomatch(int prio, event_proc *p) { p->set_priority(prio); _nomatch_list.push_back(p); } void config::add_notify(int prio, event_proc *p) { p->set_priority(prio); _notify_list.push_back(p); } void config::set_pidfile(const char *fn) { _pidfile = fn; } void config::push_var_table() { var_list *vl; vl = new var_list(); _var_list_table.push_back(vl); devdlog(LOG_DEBUG, "Pushing table\n"); } void config::pop_var_table() { delete _var_list_table.back(); _var_list_table.pop_back(); devdlog(LOG_DEBUG, "Popping table\n"); } void config::set_variable(const char *var, const char *val) { _var_list_table.back()->set_variable(var, val); } const string & config::get_variable(const string &var) { vector::reverse_iterator i; for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); ++i) { if ((*i)->is_set(var)) return ((*i)->get_variable(var)); } return (var_list::nothing); } bool config::is_id_char(char ch) const { return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || ch == '-')); } void config::expand_one(const char *&src, string &dst) { int count; string buffer; src++; // $$ -> $ if (*src == '$') { dst += *src++; return; } // $(foo) -> $(foo) // Not sure if I want to support this or not, so for now we just pass // it through. if (*src == '(') { dst += '$'; count = 1; /* If the string ends before ) is matched , return. */ while (count > 0 && *src) { if (*src == ')') count--; else if (*src == '(') count++; dst += *src++; } return; } // $[^A-Za-z] -> $\1 if (!isalpha(*src)) { dst += '$'; dst += *src++; return; } // $var -> replace with value do { buffer += *src++; } while (is_id_char(*src)); dst.append(get_variable(buffer)); } const string config::expand_string(const char *src, const char *prepend, const char *append) { const char *var_at; string dst; /* * 128 bytes is enough for 2427 of 2438 expansions that happen * while parsing config files, as tested on 2013-01-30. */ dst.reserve(128); if (prepend != NULL) dst = prepend; for (;;) { var_at = strchr(src, '$'); if (var_at == NULL) { dst.append(src); break; } dst.append(src, var_at - src); src = var_at; expand_one(src, dst); } if (append != NULL) dst.append(append); return (dst); } bool config::chop_var(char *&buffer, char *&lhs, char *&rhs) const { char *walker; if (*buffer == '\0') return (false); walker = lhs = buffer; while (is_id_char(*walker)) walker++; if (*walker != '=') return (false); walker++; // skip = if (*walker == '"') { walker++; // skip " rhs = walker; while (*walker && *walker != '"') walker++; if (*walker != '"') return (false); rhs[-2] = '\0'; *walker++ = '\0'; } else { rhs = walker; while (*walker && !isspace(*walker)) walker++; if (*walker != '\0') *walker++ = '\0'; rhs[-1] = '\0'; } while (isspace(*walker)) walker++; buffer = walker; return (true); } char * config::set_vars(char *buffer) { char *lhs; char *rhs; while (1) { if (!chop_var(buffer, lhs, rhs)) break; set_variable(lhs, rhs); } return (buffer); } void config::find_and_execute(char type) { vector *l; vector::const_iterator i; const char *s; switch (type) { default: return; case notify: l = &_notify_list; s = "notify"; break; case nomatch: l = &_nomatch_list; s = "nomatch"; break; case attach: l = &_attach_list; s = "attach"; break; case detach: l = &_detach_list; s = "detach"; break; } devdlog(LOG_DEBUG, "Processing %s event\n", s); for (i = l->begin(); i != l->end(); ++i) { if ((*i)->matches(*this)) { (*i)->run(*this); break; } } } static void process_event(char *buffer) { char type; char *sp; sp = buffer + 1; devdlog(LOG_INFO, "Processing event '%s'\n", buffer); type = *buffer++; cfg.push_var_table(); // No match doesn't have a device, and the format is a little // different, so handle it separately. switch (type) { case notify: sp = cfg.set_vars(sp); break; case nomatch: //? at location pnp-info on bus sp = strchr(sp, ' '); if (sp == NULL) return; /* Can't happen? */ *sp++ = '\0'; while (isspace(*sp)) sp++; if (strncmp(sp, "at ", 3) == 0) sp += 3; sp = cfg.set_vars(sp); while (isspace(*sp)) sp++; if (strncmp(sp, "on ", 3) == 0) cfg.set_variable("bus", sp + 3); break; case attach: /*FALLTHROUGH*/ case detach: sp = strchr(sp, ' '); if (sp == NULL) return; /* Can't happen? */ *sp++ = '\0'; cfg.set_variable("device-name", buffer); while (isspace(*sp)) sp++; if (strncmp(sp, "at ", 3) == 0) sp += 3; sp = cfg.set_vars(sp); while (isspace(*sp)) sp++; if (strncmp(sp, "on ", 3) == 0) cfg.set_variable("bus", sp + 3); break; } cfg.find_and_execute(type); cfg.pop_var_table(); } int create_socket(const char *name, int socktype) { int fd, slen; struct sockaddr_un sun; if ((fd = socket(PF_LOCAL, socktype, 0)) < 0) err(1, "socket"); bzero(&sun, sizeof(sun)); sun.sun_family = AF_UNIX; strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); slen = SUN_LEN(&sun); unlink(name); if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) err(1, "fcntl"); if (::bind(fd, (struct sockaddr *) & sun, slen) < 0) err(1, "bind"); listen(fd, 4); chown(name, 0, 0); /* XXX - root.wheel */ chmod(name, 0666); return (fd); } -unsigned int max_clients = 10; /* Default, can be overriden on cmdline. */ +unsigned int max_clients = 10; /* Default, can be overridden on cmdline. */ unsigned int num_clients; list clients; void notify_clients(const char *data, int len) { list::iterator i; /* * Deliver the data to all clients. Throw clients overboard at the * first sign of trouble. This reaps clients who've died or closed * their sockets, and also clients who are alive but failing to keep up * (or who are maliciously not reading, to consume buffer space in * kernel memory or tie up the limited number of available connections). */ for (i = clients.begin(); i != clients.end(); ) { int flags; if (i->socktype == SOCK_SEQPACKET) flags = MSG_EOR; else flags = 0; if (send(i->fd, data, len, flags) != len) { --num_clients; close(i->fd); i = clients.erase(i); devdlog(LOG_WARNING, "notify_clients: send() failed; " "dropping unresponsive client\n"); } else ++i; } } void check_clients(void) { int s; struct pollfd pfd; list::iterator i; /* * Check all existing clients to see if any of them have disappeared. * Normally we reap clients when we get an error trying to send them an * event. This check eliminates the problem of an ever-growing list of * zombie clients because we're never writing to them on a system * without frequent device-change activity. */ pfd.events = 0; for (i = clients.begin(); i != clients.end(); ) { pfd.fd = i->fd; s = poll(&pfd, 1, 0); if ((s < 0 && s != EINTR ) || (s > 0 && (pfd.revents & POLLHUP))) { --num_clients; close(i->fd); i = clients.erase(i); devdlog(LOG_NOTICE, "check_clients: " "dropping disconnected client\n"); } else ++i; } } void new_client(int fd, int socktype) { client_t s; int sndbuf_size; /* * First go reap any zombie clients, then accept the connection, and * shut down the read side to stop clients from consuming kernel memory * by sending large buffers full of data we'll never read. */ check_clients(); s.socktype = socktype; s.fd = accept(fd, NULL, NULL); if (s.fd != -1) { sndbuf_size = CLIENT_BUFSIZE; if (setsockopt(s.fd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, sizeof(sndbuf_size))) err(1, "setsockopt"); shutdown(s.fd, SHUT_RD); clients.push_back(s); ++num_clients; } else err(1, "accept"); } static void event_loop(void) { int rv; int fd; char buffer[DEVCTL_MAXBUF]; int once = 0; int stream_fd, seqpacket_fd, max_fd; int accepting; timeval tv; fd_set fds; fd = open(PATH_DEVCTL, O_RDONLY | O_CLOEXEC); if (fd == -1) err(1, "Can't open devctl device %s", PATH_DEVCTL); stream_fd = create_socket(STREAMPIPE, SOCK_STREAM); seqpacket_fd = create_socket(SEQPACKETPIPE, SOCK_SEQPACKET); accepting = 1; max_fd = max(fd, max(stream_fd, seqpacket_fd)) + 1; while (!romeo_must_die) { if (!once && !no_daemon && !daemonize_quick) { // Check to see if we have any events pending. tv.tv_sec = 0; tv.tv_usec = 0; FD_ZERO(&fds); FD_SET(fd, &fds); rv = select(fd + 1, &fds, &fds, &fds, &tv); // No events -> we've processed all pending events if (rv == 0) { devdlog(LOG_DEBUG, "Calling daemon\n"); cfg.remove_pidfile(); cfg.open_pidfile(); daemon(0, 0); cfg.write_pidfile(); once++; } } /* * When we've already got the max number of clients, stop * accepting new connections (don't put the listening sockets in * the set), shrink the accept() queue to reject connections * quickly, and poll the existing clients more often, so that we * notice more quickly when any of them disappear to free up * client slots. */ FD_ZERO(&fds); FD_SET(fd, &fds); if (num_clients < max_clients) { if (!accepting) { listen(stream_fd, max_clients); listen(seqpacket_fd, max_clients); accepting = 1; } FD_SET(stream_fd, &fds); FD_SET(seqpacket_fd, &fds); tv.tv_sec = 60; tv.tv_usec = 0; } else { if (accepting) { listen(stream_fd, 0); listen(seqpacket_fd, 0); accepting = 0; } tv.tv_sec = 2; tv.tv_usec = 0; } rv = select(max_fd, &fds, NULL, NULL, &tv); if (got_siginfo) { devdlog(LOG_NOTICE, "Events received so far=%u\n", total_events); got_siginfo = 0; } if (rv == -1) { if (errno == EINTR) continue; err(1, "select"); } else if (rv == 0) check_clients(); if (FD_ISSET(fd, &fds)) { rv = read(fd, buffer, sizeof(buffer) - 1); if (rv > 0) { total_events++; if (rv == sizeof(buffer) - 1) { devdlog(LOG_WARNING, "Warning: " "available event data exceeded " "buffer space\n"); } notify_clients(buffer, rv); buffer[rv] = '\0'; while (buffer[--rv] == '\n') buffer[rv] = '\0'; process_event(buffer); } else if (rv < 0) { if (errno != EINTR) break; } else { /* EOF */ break; } } if (FD_ISSET(stream_fd, &fds)) new_client(stream_fd, SOCK_STREAM); /* * Aside from the socket type, both sockets use the same * protocol, so we can process clients the same way. */ if (FD_ISSET(seqpacket_fd, &fds)) new_client(seqpacket_fd, SOCK_SEQPACKET); } close(fd); } /* * functions that the parser uses. */ void add_attach(int prio, event_proc *p) { cfg.add_attach(prio, p); } void add_detach(int prio, event_proc *p) { cfg.add_detach(prio, p); } void add_directory(const char *dir) { cfg.add_directory(dir); free(const_cast(dir)); } void add_nomatch(int prio, event_proc *p) { cfg.add_nomatch(prio, p); } void add_notify(int prio, event_proc *p) { cfg.add_notify(prio, p); } event_proc * add_to_event_proc(event_proc *ep, eps *eps) { if (ep == NULL) ep = new event_proc(); ep->add(eps); return (ep); } eps * new_action(const char *cmd) { eps *e = new action(cmd); free(const_cast(cmd)); return (e); } eps * new_match(const char *var, const char *re) { eps *e = new match(cfg, var, re); free(const_cast(var)); free(const_cast(re)); return (e); } eps * new_media(const char *var, const char *re) { eps *e = new media(cfg, var, re); free(const_cast(var)); free(const_cast(re)); return (e); } void set_pidfile(const char *name) { cfg.set_pidfile(name); free(const_cast(name)); } void set_variable(const char *var, const char *val) { cfg.set_variable(var, val); free(const_cast(var)); free(const_cast(val)); } static void gensighand(int) { romeo_must_die = 1; } /* * SIGINFO handler. Will print useful statistics to the syslog or stderr * as appropriate */ static void siginfohand(int) { got_siginfo = 1; } /* * Local logging function. Prints to syslog if we're daemonized; stderr * otherwise. */ static void devdlog(int priority, const char* fmt, ...) { va_list argp; va_start(argp, fmt); if (no_daemon) vfprintf(stderr, fmt, argp); else if ((! quiet_mode) || (priority <= LOG_WARNING)) vsyslog(priority, fmt, argp); va_end(argp); } static void usage() { fprintf(stderr, "usage: %s [-dnq] [-l connlimit] [-f file]\n", getprogname()); exit(1); } static void check_devd_enabled() { int val = 0; size_t len; len = sizeof(val); if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) errx(1, "devctl sysctl missing from kernel!"); if (val == 0) { warnx("Setting " SYSCTL " to 1000"); val = 1000; sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); } } /* * main */ int main(int argc, char **argv) { int ch; check_devd_enabled(); while ((ch = getopt(argc, argv, "df:l:nq")) != -1) { switch (ch) { case 'd': no_daemon = 1; break; case 'f': configfile = optarg; break; case 'l': max_clients = MAX(1, strtoul(optarg, NULL, 0)); break; case 'n': daemonize_quick = 1; break; case 'q': quiet_mode = 1; break; default: usage(); } } cfg.parse(); if (!no_daemon && daemonize_quick) { cfg.open_pidfile(); daemon(0, 0); cfg.write_pidfile(); } signal(SIGPIPE, SIG_IGN); signal(SIGHUP, gensighand); signal(SIGINT, gensighand); signal(SIGTERM, gensighand); signal(SIGINFO, siginfohand); event_loop(); return (0); } Index: head/sbin/fsck_ffs/fsck.h =================================================================== --- head/sbin/fsck_ffs/fsck.h (revision 289676) +++ head/sbin/fsck_ffs/fsck.h (revision 289677) @@ -1,474 +1,474 @@ /* * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by Marshall * Kirk McKusick and Network Associates Laboratories, the Security * Research Division of Network Associates, Inc. under DARPA/SPAWAR * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS * research program. * * 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. * * Copyright (c) 1980, 1986, 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. * 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. * * @(#)fsck.h 8.4 (Berkeley) 5/9/95 * $FreeBSD$ */ #ifndef _FSCK_H_ #define _FSCK_H_ #include #include #include #include #define MAXDUP 10 /* limit on dup blks (per inode) */ #define MAXBAD 10 /* limit on bad blks (per inode) */ #define MINBUFS 10 /* minimum number of buffers required */ #define MAXBUFS 40 /* maximum space to allocate to buffers */ #define INOBUFSIZE 64*1024 /* size of buffer to read inodes in pass1 */ #define ZEROBUFSIZE (dev_bsize * 128) /* size of zero buffer used by -Z */ union dinode { struct ufs1_dinode dp1; struct ufs2_dinode dp2; }; #define DIP(dp, field) \ ((sblock.fs_magic == FS_UFS1_MAGIC) ? \ (dp)->dp1.field : (dp)->dp2.field) #define DIP_SET(dp, field, val) do { \ if (sblock.fs_magic == FS_UFS1_MAGIC) \ (dp)->dp1.field = (val); \ else \ (dp)->dp2.field = (val); \ } while (0) /* * Each inode on the file system is described by the following structure. * The linkcnt is initially set to the value in the inode. Each time it * is found during the descent in passes 2, 3, and 4 the count is * decremented. Any inodes whose count is non-zero after pass 4 needs to * have its link count adjusted by the value remaining in ino_linkcnt. */ struct inostat { char ino_state; /* state of inode, see below */ char ino_type; /* type of inode */ short ino_linkcnt; /* number of links not found */ }; /* * Inode states. */ #define USTATE 0x1 /* inode not allocated */ #define FSTATE 0x2 /* inode is file */ #define FZLINK 0x3 /* inode is file with a link count of zero */ #define DSTATE 0x4 /* inode is directory */ #define DZLINK 0x5 /* inode is directory with a zero link count */ #define DFOUND 0x6 /* directory found during descent */ /* 0x7 UNUSED - see S_IS_DVALID() definition */ #define DCLEAR 0x8 /* directory is to be cleared */ #define FCLEAR 0x9 /* file is to be cleared */ /* DUNFOUND === (state == DSTATE || state == DZLINK) */ #define S_IS_DUNFOUND(state) (((state) & ~0x1) == DSTATE) /* DVALID === (state == DSTATE || state == DZLINK || state == DFOUND) */ #define S_IS_DVALID(state) (((state) & ~0x3) == DSTATE) #define INO_IS_DUNFOUND(ino) S_IS_DUNFOUND(inoinfo(ino)->ino_state) #define INO_IS_DVALID(ino) S_IS_DVALID(inoinfo(ino)->ino_state) /* * Inode state information is contained on per cylinder group lists * which are described by the following structure. */ struct inostatlist { long il_numalloced; /* number of inodes allocated in this cg */ struct inostat *il_stat;/* inostat info for this cylinder group */ } *inostathead; /* * buffer cache structure. */ struct bufarea { TAILQ_ENTRY(bufarea) b_list; /* buffer list */ ufs2_daddr_t b_bno; int b_size; int b_errs; int b_flags; int b_type; union { char *b_buf; /* buffer space */ ufs1_daddr_t *b_indir1; /* UFS1 indirect block */ ufs2_daddr_t *b_indir2; /* UFS2 indirect block */ struct fs *b_fs; /* super block */ struct cg *b_cg; /* cylinder group */ struct ufs1_dinode *b_dinode1; /* UFS1 inode block */ struct ufs2_dinode *b_dinode2; /* UFS2 inode block */ } b_un; char b_dirty; }; #define IBLK(bp, i) \ ((sblock.fs_magic == FS_UFS1_MAGIC) ? \ (bp)->b_un.b_indir1[i] : (bp)->b_un.b_indir2[i]) #define IBLK_SET(bp, i, val) do { \ if (sblock.fs_magic == FS_UFS1_MAGIC) \ (bp)->b_un.b_indir1[i] = (val); \ else \ (bp)->b_un.b_indir2[i] = (val); \ } while (0) /* * Buffer flags */ #define B_INUSE 0x00000001 /* Buffer is in use */ /* * Type of data in buffer */ #define BT_UNKNOWN 0 /* Buffer holds a superblock */ #define BT_SUPERBLK 1 /* Buffer holds a superblock */ #define BT_CYLGRP 2 /* Buffer holds a cylinder group map */ #define BT_LEVEL1 3 /* Buffer holds single level indirect */ #define BT_LEVEL2 4 /* Buffer holds double level indirect */ #define BT_LEVEL3 5 /* Buffer holds triple level indirect */ #define BT_EXTATTR 6 /* Buffer holds external attribute data */ #define BT_INODES 7 /* Buffer holds external attribute data */ #define BT_DIRDATA 8 /* Buffer holds directory data */ #define BT_DATA 9 /* Buffer holds user data */ #define BT_NUMBUFTYPES 10 #define BT_NAMES { \ "unknown", \ "Superblock", \ "Cylinder Group", \ "Single Level Indirect", \ "Double Level Indirect", \ "Triple Level Indirect", \ "External Attribute", \ "Inode Block", \ "Directory Contents", \ "User Data" } extern long readcnt[BT_NUMBUFTYPES]; extern long totalreadcnt[BT_NUMBUFTYPES]; extern struct timespec readtime[BT_NUMBUFTYPES]; extern struct timespec totalreadtime[BT_NUMBUFTYPES]; extern struct timespec startprog; extern struct bufarea sblk; /* file system superblock */ extern struct bufarea *pdirbp; /* current directory contents */ extern struct bufarea *pbp; /* current inode block */ #define dirty(bp) do { \ if (fswritefd < 0) \ pfatal("SETTING DIRTY FLAG IN READ_ONLY MODE\n"); \ else \ (bp)->b_dirty = 1; \ } while (0) #define initbarea(bp, type) do { \ (bp)->b_dirty = 0; \ (bp)->b_bno = (ufs2_daddr_t)-1; \ (bp)->b_flags = 0; \ (bp)->b_type = type; \ } while (0) #define sbdirty() dirty(&sblk) #define sblock (*sblk.b_un.b_fs) enum fixstate {DONTKNOW, NOFIX, FIX, IGNORE}; extern ino_t cursnapshot; struct inodesc { enum fixstate id_fix; /* policy on fixing errors */ int (*id_func)(struct inodesc *); /* function to be applied to blocks of inode */ ino_t id_number; /* inode number described */ ino_t id_parent; /* for DATA nodes, their parent */ ufs_lbn_t id_lbn; /* logical block number of current block */ ufs2_daddr_t id_blkno; /* current block number being examined */ int id_numfrags; /* number of frags contained in block */ off_t id_filesize; /* for DATA nodes, the size of the directory */ ufs2_daddr_t id_entryno;/* for DATA nodes, current entry number */ int id_loc; /* for DATA nodes, current location in dir */ struct direct *id_dirp; /* for DATA nodes, ptr to current entry */ char *id_name; /* for DATA nodes, name to find or enter */ char id_type; /* type of descriptor, DATA or ADDR */ }; /* file types */ #define DATA 1 /* a directory */ #define SNAP 2 /* a snapshot */ #define ADDR 3 /* anything but a directory or a snapshot */ /* * Linked list of duplicate blocks. * * The list is composed of two parts. The first part of the * list (from duplist through the node pointed to by muldup) * contains a single copy of each duplicate block that has been * found. The second part of the list (from muldup to the end) * contains duplicate blocks that have been found more than once. * To check if a block has been found as a duplicate it is only * necessary to search from duplist through muldup. To find the * total number of times that a block has been found as a duplicate * the entire list must be searched for occurrences of the block * in question. The following diagram shows a sample list where * w (found twice), x (found once), y (found three times), and z * (found once) are duplicate block numbers: * * w -> y -> x -> z -> y -> w -> y * ^ ^ * | | * duplist muldup */ struct dups { struct dups *next; ufs2_daddr_t dup; }; struct dups *duplist; /* head of dup list */ struct dups *muldup; /* end of unique duplicate dup block numbers */ /* * Inode cache data structures. */ struct inoinfo { struct inoinfo *i_nexthash; /* next entry in hash chain */ ino_t i_number; /* inode number of this entry */ ino_t i_parent; /* inode number of parent */ ino_t i_dotdot; /* inode number of `..' */ size_t i_isize; /* size of inode */ u_int i_numblks; /* size of block array in bytes */ ufs2_daddr_t i_blks[1]; /* actually longer */ } **inphead, **inpsort; extern long numdirs, dirhash, listmax, inplast; extern long countdirs; /* number of directories we actually found */ #define MIBSIZE 3 /* size of fsck sysctl MIBs */ extern int adjrefcnt[MIBSIZE]; /* MIB command to adjust inode reference cnt */ extern int adjblkcnt[MIBSIZE]; /* MIB command to adjust inode block count */ extern int adjndir[MIBSIZE]; /* MIB command to adjust number of directories */ extern int adjnbfree[MIBSIZE]; /* MIB command to adjust number of free blocks */ extern int adjnifree[MIBSIZE]; /* MIB command to adjust number of free inodes */ extern int adjnffree[MIBSIZE]; /* MIB command to adjust number of free frags */ extern int adjnumclusters[MIBSIZE]; /* MIB command to adjust number of free clusters */ extern int freefiles[MIBSIZE]; /* MIB command to free a set of files */ extern int freedirs[MIBSIZE]; /* MIB command to free a set of directories */ extern int freeblks[MIBSIZE]; /* MIB command to free a set of data blocks */ extern struct fsck_cmd cmd; /* sysctl file system update commands */ extern char snapname[BUFSIZ]; /* when doing snapshots, the name of the file */ extern char *cdevname; /* name of device being checked */ extern long dev_bsize; /* computed value of DEV_BSIZE */ extern long secsize; /* actual disk sector size */ -extern u_int real_dev_bsize; /* actual disk sector size, not overriden */ +extern u_int real_dev_bsize; /* actual disk sector size, not overridden */ extern char nflag; /* assume a no response */ extern char yflag; /* assume a yes response */ extern int bkgrdflag; /* use a snapshot to run on an active system */ extern int bflag; /* location of alternate super block */ extern int debug; /* output debugging info */ extern int Eflag; /* delete empty data blocks */ extern int Zflag; /* zero empty data blocks */ extern int inoopt; /* trim out unused inodes */ extern char ckclean; /* only do work if not cleanly unmounted */ extern int cvtlevel; /* convert to newer file system format */ extern int bkgrdcheck; /* determine if background check is possible */ extern int bkgrdsumadj; /* whether the kernel have ability to adjust superblock summary */ extern char usedsoftdep; /* just fix soft dependency inconsistencies */ extern char preen; /* just fix normal inconsistencies */ extern char rerun; /* rerun fsck. Only used in non-preen mode */ extern int returntosingle; /* 1 => return to single user mode on exit */ extern char resolved; /* cleared if unresolved changes => not clean */ extern char havesb; /* superblock has been read */ extern char skipclean; /* skip clean file systems if preening */ extern int fsmodified; /* 1 => write done to file system */ extern int fsreadfd; /* file descriptor for reading file system */ extern int fswritefd; /* file descriptor for writing file system */ extern int surrender; /* Give up if reads fail */ extern int wantrestart; /* Restart fsck on early termination */ extern ufs2_daddr_t maxfsblock; /* number of blocks in the file system */ extern char *blockmap; /* ptr to primary blk allocation map */ extern ino_t maxino; /* number of inodes in file system */ extern ino_t lfdir; /* lost & found directory inode number */ extern const char *lfname; /* lost & found directory name */ extern int lfmode; /* lost & found directory creation mode */ extern ufs2_daddr_t n_blks; /* number of blocks in use */ extern ino_t n_files; /* number of files in use */ extern volatile sig_atomic_t got_siginfo; /* received a SIGINFO */ extern volatile sig_atomic_t got_sigalarm; /* received a SIGALRM */ #define clearinode(dp) \ if (sblock.fs_magic == FS_UFS1_MAGIC) { \ (dp)->dp1 = ufs1_zino; \ } else { \ (dp)->dp2 = ufs2_zino; \ } extern struct ufs1_dinode ufs1_zino; extern struct ufs2_dinode ufs2_zino; #define setbmap(blkno) setbit(blockmap, blkno) #define testbmap(blkno) isset(blockmap, blkno) #define clrbmap(blkno) clrbit(blockmap, blkno) #define STOP 0x01 #define SKIP 0x02 #define KEEPON 0x04 #define ALTERED 0x08 #define FOUND 0x10 #define EEXIT 8 /* Standard error exit. */ #define ERESTART -1 int flushentry(void); /* * Wrapper for malloc() that flushes the cylinder group cache to try * to get space. */ static inline void* Malloc(size_t size) { void *retval; while ((retval = malloc(size)) == NULL) if (flushentry() == 0) break; return (retval); } /* * Wrapper for calloc() that flushes the cylinder group cache to try * to get space. */ static inline void* Calloc(size_t cnt, size_t size) { void *retval; while ((retval = calloc(cnt, size)) == NULL) if (flushentry() == 0) break; return (retval); } struct fstab; void adjust(struct inodesc *, int lcnt); ufs2_daddr_t allocblk(long frags); ino_t allocdir(ino_t parent, ino_t request, int mode); ino_t allocino(ino_t request, int type); void blkerror(ino_t ino, const char *type, ufs2_daddr_t blk); char *blockcheck(char *name); int blread(int fd, char *buf, ufs2_daddr_t blk, long size); void bufinit(void); void blwrite(int fd, char *buf, ufs2_daddr_t blk, ssize_t size); void blerase(int fd, ufs2_daddr_t blk, long size); void blzero(int fd, ufs2_daddr_t blk, long size); void cacheino(union dinode *dp, ino_t inumber); void catch(int); void catchquit(int); int changeino(ino_t dir, const char *name, ino_t newnum); int check_cgmagic(int cg, struct bufarea *cgbp); int chkrange(ufs2_daddr_t blk, int cnt); void ckfini(int markclean); int ckinode(union dinode *dp, struct inodesc *); void clri(struct inodesc *, const char *type, int flag); int clearentry(struct inodesc *); void direrror(ino_t ino, const char *errmesg); int dirscan(struct inodesc *); int dofix(struct inodesc *, const char *msg); int eascan(struct inodesc *, struct ufs2_dinode *dp); void fileerror(ino_t cwd, ino_t ino, const char *errmesg); void finalIOstats(void); int findino(struct inodesc *); int findname(struct inodesc *); void flush(int fd, struct bufarea *bp); void freeblk(ufs2_daddr_t blkno, long frags); void freeino(ino_t ino); void freeinodebuf(void); void fsutilinit(void); int ftypeok(union dinode *dp); void getblk(struct bufarea *bp, ufs2_daddr_t blk, long size); struct bufarea *cgget(int cg); struct bufarea *getdatablk(ufs2_daddr_t blkno, long size, int type); struct inoinfo *getinoinfo(ino_t inumber); union dinode *getnextinode(ino_t inumber, int rebuildcg); void getpathname(char *namebuf, ino_t curdir, ino_t ino); union dinode *ginode(ino_t inumber); void infohandler(int sig); void alarmhandler(int sig); void inocleanup(void); void inodirty(void); struct inostat *inoinfo(ino_t inum); void IOstats(char *what); int linkup(ino_t orphan, ino_t parentdir, char *name); int makeentry(ino_t parent, ino_t ino, const char *name); void panic(const char *fmt, ...) __printflike(1, 2); void pass1(void); void pass1b(void); int pass1check(struct inodesc *); void pass2(void); void pass3(void); void pass4(void); int pass4check(struct inodesc *); void pass5(void); void pfatal(const char *fmt, ...) __printflike(1, 2); void pinode(ino_t ino); void propagate(void); void pwarn(const char *fmt, ...) __printflike(1, 2); int readsb(int listerr); int reply(const char *question); void rwerror(const char *mesg, ufs2_daddr_t blk); void sblock_init(void); void setinodebuf(ino_t); int setup(char *dev); void gjournal_check(const char *filesys); int suj_check(const char *filesys); void update_maps(struct cg *, struct cg*, int); void fsckinit(void); #endif /* !_FSCK_H_ */ Index: head/sbin/fsck_ffs/globs.c =================================================================== --- head/sbin/fsck_ffs/globs.c (revision 289676) +++ head/sbin/fsck_ffs/globs.c (revision 289677) @@ -1,165 +1,165 @@ /* * Copyright (c) 1980, 1986, 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. * 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 0 #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1986, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/14/95"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include "fsck.h" long readcnt[BT_NUMBUFTYPES]; long totalreadcnt[BT_NUMBUFTYPES]; struct timespec readtime[BT_NUMBUFTYPES]; struct timespec totalreadtime[BT_NUMBUFTYPES]; struct timespec startprog; struct bufarea sblk; /* file system superblock */ struct bufarea *pdirbp; /* current directory contents */ struct bufarea *pbp; /* current inode block */ ino_t cursnapshot; long numdirs, dirhash, listmax, inplast; long countdirs; /* number of directories we actually found */ int adjrefcnt[MIBSIZE]; /* MIB command to adjust inode reference cnt */ int adjblkcnt[MIBSIZE]; /* MIB command to adjust inode block count */ int adjndir[MIBSIZE]; /* MIB command to adjust number of directories */ int adjnbfree[MIBSIZE]; /* MIB command to adjust number of free blocks */ int adjnifree[MIBSIZE]; /* MIB command to adjust number of free inodes */ int adjnffree[MIBSIZE]; /* MIB command to adjust number of free frags */ int adjnumclusters[MIBSIZE]; /* MIB command to adjust number of free clusters */ int freefiles[MIBSIZE]; /* MIB command to free a set of files */ int freedirs[MIBSIZE]; /* MIB command to free a set of directories */ int freeblks[MIBSIZE]; /* MIB command to free a set of data blocks */ struct fsck_cmd cmd; /* sysctl file system update commands */ char snapname[BUFSIZ]; /* when doing snapshots, the name of the file */ char *cdevname; /* name of device being checked */ long dev_bsize; /* computed value of DEV_BSIZE */ long secsize; /* actual disk sector size */ -u_int real_dev_bsize; /* actual disk sector size, not overriden */ +u_int real_dev_bsize; /* actual disk sector size, not overridden */ char nflag; /* assume a no response */ char yflag; /* assume a yes response */ int bkgrdflag; /* use a snapshot to run on an active system */ int bflag; /* location of alternate super block */ int debug; /* output debugging info */ int Eflag; /* delete empty data blocks */ int Zflag; /* zero empty data blocks */ int inoopt; /* trim out unused inodes */ char ckclean; /* only do work if not cleanly unmounted */ int cvtlevel; /* convert to newer file system format */ int bkgrdcheck; /* determine if background check is possible */ int bkgrdsumadj; /* whether the kernel have ability to adjust superblock summary */ char usedsoftdep; /* just fix soft dependency inconsistencies */ char preen; /* just fix normal inconsistencies */ char rerun; /* rerun fsck. Only used in non-preen mode */ int returntosingle; /* 1 => return to single user mode on exit */ char resolved; /* cleared if unresolved changes => not clean */ char havesb; /* superblock has been read */ char skipclean; /* skip clean file systems if preening */ int fsmodified; /* 1 => write done to file system */ int fsreadfd; /* file descriptor for reading file system */ int fswritefd; /* file descriptor for writing file system */ int surrender; /* Give up if reads fail */ int wantrestart; /* Restart fsck on early termination */ ufs2_daddr_t maxfsblock; /* number of blocks in the file system */ char *blockmap; /* ptr to primary blk allocation map */ ino_t maxino; /* number of inodes in file system */ ino_t lfdir; /* lost & found directory inode number */ const char *lfname; /* lost & found directory name */ int lfmode; /* lost & found directory creation mode */ ufs2_daddr_t n_blks; /* number of blocks in use */ ino_t n_files; /* number of files in use */ volatile sig_atomic_t got_siginfo; /* received a SIGINFO */ volatile sig_atomic_t got_sigalarm; /* received a SIGALRM */ struct ufs1_dinode ufs1_zino; struct ufs2_dinode ufs2_zino; void fsckinit(void) { bzero(readcnt, sizeof(long) * BT_NUMBUFTYPES); bzero(totalreadcnt, sizeof(long) * BT_NUMBUFTYPES); bzero(readtime, sizeof(struct timespec) * BT_NUMBUFTYPES); bzero(totalreadtime, sizeof(struct timespec) * BT_NUMBUFTYPES); bzero(&startprog, sizeof(struct timespec));; bzero(&sblk, sizeof(struct bufarea)); pdirbp = NULL; pbp = NULL; cursnapshot = 0; numdirs = dirhash = listmax = inplast = 0; countdirs = 0; bzero(adjrefcnt, sizeof(int) * MIBSIZE); bzero(adjblkcnt, sizeof(int) * MIBSIZE); bzero(adjndir, sizeof(int) * MIBSIZE); bzero(adjnbfree, sizeof(int) * MIBSIZE); bzero(adjnifree, sizeof(int) * MIBSIZE); bzero(adjnffree, sizeof(int) * MIBSIZE); bzero(adjnumclusters, sizeof(int) * MIBSIZE); bzero(freefiles, sizeof(int) * MIBSIZE); bzero(freedirs, sizeof(int) * MIBSIZE); bzero(freeblks, sizeof(int) * MIBSIZE); bzero(&cmd, sizeof(struct fsck_cmd)); bzero(snapname, sizeof(char) * BUFSIZ); cdevname = NULL; dev_bsize = 0; secsize = 0; real_dev_bsize = 0; bkgrdsumadj = 0; usedsoftdep = 0; rerun = 0; returntosingle = 0; resolved = 0; havesb = 0; fsmodified = 0; fsreadfd = 0; fswritefd = 0; maxfsblock = 0; blockmap = NULL; maxino = 0; lfdir = 0; lfname = "lost+found"; lfmode = 0700; n_blks = 0; n_files = 0; got_siginfo = 0; got_sigalarm = 0; bzero(&ufs1_zino, sizeof(struct ufs1_dinode)); bzero(&ufs2_zino, sizeof(struct ufs2_dinode)); } Index: head/sbin/ipfw/tables.c =================================================================== --- head/sbin/ipfw/tables.c (revision 289676) +++ head/sbin/ipfw/tables.c (revision 289677) @@ -1,2023 +1,2023 @@ /* * Copyright (c) 2014 Yandex LLC * Copyright (c) 2014 Alexander V. Chernikov * * Redistribution and use in source forms, with and without modification, * are permitted provided that this entire comment appears intact. * * Redistribution in binary form may occur without any restrictions. * Obviously, it would be nice if you gave credit where credit is due * but requiring it would be too onerous. * * This software is provided ``AS IS'' without any warranties of any kind. * * in-kernel ipfw tables support. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipfw2.h" static void table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add, int quiet, int update, int atomic); static int table_flush(ipfw_obj_header *oh); static int table_destroy(ipfw_obj_header *oh); static int table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i); static int table_do_modify(ipfw_obj_header *oh, ipfw_xtable_info *i); static int table_do_swap(ipfw_obj_header *oh, char *second); static void table_create(ipfw_obj_header *oh, int ac, char *av[]); static void table_modify(ipfw_obj_header *oh, int ac, char *av[]); static void table_lookup(ipfw_obj_header *oh, int ac, char *av[]); static void table_lock(ipfw_obj_header *oh, int lock); static int table_swap(ipfw_obj_header *oh, char *second); static int table_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i); static int table_show_info(ipfw_xtable_info *i, void *arg); static void table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint32_t set, uint16_t uidx); static int table_flush_one(ipfw_xtable_info *i, void *arg); static int table_show_one(ipfw_xtable_info *i, void *arg); static int table_do_get_list(ipfw_xtable_info *i, ipfw_obj_header **poh); static void table_show_list(ipfw_obj_header *oh, int need_header); static void table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent); static void tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *key, int add, uint8_t *ptype, uint32_t *pvmask, ipfw_xtable_info *xi); static void tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *arg, uint8_t type, uint32_t vmask); static void table_show_value(char *buf, size_t bufsize, ipfw_table_value *v, uint32_t vmask, int print_ip); typedef int (table_cb_t)(ipfw_xtable_info *i, void *arg); static int tables_foreach(table_cb_t *f, void *arg, int sort); #ifndef s6_addr32 #define s6_addr32 __u6_addr.__u6_addr32 #endif static struct _s_x tabletypes[] = { { "addr", IPFW_TABLE_ADDR }, { "iface", IPFW_TABLE_INTERFACE }, { "number", IPFW_TABLE_NUMBER }, { "flow", IPFW_TABLE_FLOW }, { NULL, 0 } }; static struct _s_x tablevaltypes[] = { { "skipto", IPFW_VTYPE_SKIPTO }, { "pipe", IPFW_VTYPE_PIPE }, { "fib", IPFW_VTYPE_FIB }, { "nat", IPFW_VTYPE_NAT }, { "dscp", IPFW_VTYPE_DSCP }, { "tag", IPFW_VTYPE_TAG }, { "divert", IPFW_VTYPE_DIVERT }, { "netgraph", IPFW_VTYPE_NETGRAPH }, { "limit", IPFW_VTYPE_LIMIT }, { "ipv4", IPFW_VTYPE_NH4 }, { "ipv6", IPFW_VTYPE_NH6 }, { NULL, 0 } }; static struct _s_x tablecmds[] = { { "add", TOK_ADD }, { "delete", TOK_DEL }, { "create", TOK_CREATE }, { "destroy", TOK_DESTROY }, { "flush", TOK_FLUSH }, { "modify", TOK_MODIFY }, { "swap", TOK_SWAP }, { "info", TOK_INFO }, { "detail", TOK_DETAIL }, { "list", TOK_LIST }, { "lookup", TOK_LOOKUP }, { "atomic", TOK_ATOMIC }, { "lock", TOK_LOCK }, { "unlock", TOK_UNLOCK }, { NULL, 0 } }; static int lookup_host (char *host, struct in_addr *ipaddr) { struct hostent *he; if (!inet_aton(host, ipaddr)) { if ((he = gethostbyname(host)) == NULL) return(-1); *ipaddr = *(struct in_addr *)he->h_addr_list[0]; } return(0); } static int get_token(struct _s_x *table, char *string, char *errbase) { int tcmd; if ((tcmd = match_token_relaxed(table, string)) < 0) errx(EX_USAGE, "%s %s %s", (tcmd == 0) ? "invalid" : "ambiguous", errbase, string); return (tcmd); } /* * This one handles all table-related commands * ipfw table NAME create ... * ipfw table NAME modify ... * ipfw table NAME destroy * ipfw table NAME swap NAME * ipfw table NAME lock * ipfw table NAME unlock * ipfw table NAME add addr[/masklen] [value] * ipfw table NAME add [addr[/masklen] value] [addr[/masklen] value] .. * ipfw table NAME delete addr[/masklen] [addr[/masklen]] .. * ipfw table NAME lookup addr * ipfw table {NAME | all} flush * ipfw table {NAME | all} list * ipfw table {NAME | all} info * ipfw table {NAME | all} detail */ void ipfw_table_handler(int ac, char *av[]) { int do_add, is_all; int atomic, error, tcmd; ipfw_xtable_info i; ipfw_obj_header oh; char *tablename; uint32_t set; void *arg; memset(&oh, 0, sizeof(oh)); is_all = 0; if (co.use_set != 0) set = co.use_set - 1; else set = 0; ac--; av++; NEED1("table needs name"); tablename = *av; if (table_check_name(tablename) == 0) { table_fill_ntlv(&oh.ntlv, *av, set, 1); oh.idx = 1; } else { if (strcmp(tablename, "all") == 0) is_all = 1; else errx(EX_USAGE, "table name %s is invalid", tablename); } ac--; av++; NEED1("table needs command"); tcmd = get_token(tablecmds, *av, "table command"); /* Check if atomic operation was requested */ atomic = 0; if (tcmd == TOK_ATOMIC) { ac--; av++; NEED1("atomic needs command"); tcmd = get_token(tablecmds, *av, "table command"); switch (tcmd) { case TOK_ADD: break; default: errx(EX_USAGE, "atomic is not compatible with %s", *av); } atomic = 1; } switch (tcmd) { case TOK_LIST: case TOK_INFO: case TOK_DETAIL: case TOK_FLUSH: break; default: if (is_all != 0) errx(EX_USAGE, "table name required"); } switch (tcmd) { case TOK_ADD: case TOK_DEL: do_add = **av == 'a'; ac--; av++; table_modify_record(&oh, ac, av, do_add, co.do_quiet, co.do_quiet, atomic); break; case TOK_CREATE: ac--; av++; table_create(&oh, ac, av); break; case TOK_MODIFY: ac--; av++; table_modify(&oh, ac, av); break; case TOK_DESTROY: if (table_destroy(&oh) != 0) err(EX_OSERR, "failed to destroy table %s", tablename); break; case TOK_FLUSH: if (is_all == 0) { if ((error = table_flush(&oh)) != 0) err(EX_OSERR, "failed to flush table %s info", tablename); } else { error = tables_foreach(table_flush_one, &oh, 1); if (error != 0) err(EX_OSERR, "failed to flush tables list"); } break; case TOK_SWAP: ac--; av++; NEED1("second table name required"); table_swap(&oh, *av); break; case TOK_LOCK: case TOK_UNLOCK: table_lock(&oh, (tcmd == TOK_LOCK)); break; case TOK_DETAIL: case TOK_INFO: arg = (tcmd == TOK_DETAIL) ? (void *)1 : NULL; if (is_all == 0) { if ((error = table_get_info(&oh, &i)) != 0) err(EX_OSERR, "failed to request table info"); table_show_info(&i, arg); } else { error = tables_foreach(table_show_info, arg, 1); if (error != 0) err(EX_OSERR, "failed to request tables list"); } break; case TOK_LIST: if (is_all == 0) { ipfw_xtable_info i; if ((error = table_get_info(&oh, &i)) != 0) err(EX_OSERR, "failed to request table info"); table_show_one(&i, NULL); } else { error = tables_foreach(table_show_one, NULL, 1); if (error != 0) err(EX_OSERR, "failed to request tables list"); } break; case TOK_LOOKUP: ac--; av++; table_lookup(&oh, ac, av); break; } } static void table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint32_t set, uint16_t uidx) { ntlv->head.type = IPFW_TLV_TBL_NAME; ntlv->head.length = sizeof(ipfw_obj_ntlv); ntlv->idx = uidx; ntlv->set = set; strlcpy(ntlv->name, name, sizeof(ntlv->name)); } static void table_fill_objheader(ipfw_obj_header *oh, ipfw_xtable_info *i) { oh->idx = 1; table_fill_ntlv(&oh->ntlv, i->tablename, i->set, 1); } static struct _s_x tablenewcmds[] = { { "type", TOK_TYPE }, { "valtype", TOK_VALTYPE }, { "algo", TOK_ALGO }, { "limit", TOK_LIMIT }, { "locked", TOK_LOCK }, { NULL, 0 } }; static struct _s_x flowtypecmds[] = { { "src-ip", IPFW_TFFLAG_SRCIP }, { "proto", IPFW_TFFLAG_PROTO }, { "src-port", IPFW_TFFLAG_SRCPORT }, { "dst-ip", IPFW_TFFLAG_DSTIP }, { "dst-port", IPFW_TFFLAG_DSTPORT }, { NULL, 0 } }; int table_parse_type(uint8_t ttype, char *p, uint8_t *tflags) { uint32_t fset, fclear; char *e; /* Parse type options */ switch(ttype) { case IPFW_TABLE_FLOW: fset = fclear = 0; if (fill_flags(flowtypecmds, p, &e, &fset, &fclear) != 0) errx(EX_USAGE, "unable to parse flow option %s", e); *tflags = fset; break; default: return (EX_USAGE); } return (0); } void table_print_type(char *tbuf, size_t size, uint8_t type, uint8_t tflags) { const char *tname; int l; if ((tname = match_value(tabletypes, type)) == NULL) tname = "unknown"; l = snprintf(tbuf, size, "%s", tname); tbuf += l; size -= l; switch(type) { case IPFW_TABLE_FLOW: if (tflags != 0) { *tbuf++ = ':'; l--; print_flags_buffer(tbuf, size, flowtypecmds, tflags); } break; } } /* * Creates new table * * ipfw table NAME create [ type { addr | iface | number | flow } ] * [ algo algoname ] */ static void table_create(ipfw_obj_header *oh, int ac, char *av[]) { ipfw_xtable_info xi; int error, tcmd, val; uint32_t fset, fclear; char *e, *p; char tbuf[128]; memset(&xi, 0, sizeof(xi)); while (ac > 0) { tcmd = get_token(tablenewcmds, *av, "option"); ac--; av++; switch (tcmd) { case TOK_LIMIT: NEED1("limit value required"); xi.limit = strtol(*av, NULL, 10); ac--; av++; break; case TOK_TYPE: NEED1("table type required"); /* Type may have suboptions after ':' */ if ((p = strchr(*av, ':')) != NULL) *p++ = '\0'; val = match_token(tabletypes, *av); if (val == -1) { concat_tokens(tbuf, sizeof(tbuf), tabletypes, ", "); errx(EX_USAGE, "Unknown tabletype: %s. Supported: %s", *av, tbuf); } xi.type = val; if (p != NULL) { error = table_parse_type(val, p, &xi.tflags); if (error != 0) errx(EX_USAGE, "Unsupported suboptions: %s", p); } ac--; av++; break; case TOK_VALTYPE: NEED1("table value type required"); fset = fclear = 0; val = fill_flags(tablevaltypes, *av, &e, &fset, &fclear); if (val != -1) { xi.vmask = fset; ac--; av++; break; } concat_tokens(tbuf, sizeof(tbuf), tablevaltypes, ", "); errx(EX_USAGE, "Unknown value type: %s. Supported: %s", e, tbuf); break; case TOK_ALGO: NEED1("table algorithm name required"); if (strlen(*av) > sizeof(xi.algoname)) errx(EX_USAGE, "algorithm name too long"); strlcpy(xi.algoname, *av, sizeof(xi.algoname)); ac--; av++; break; case TOK_LOCK: xi.flags |= IPFW_TGFLAGS_LOCKED; break; } } - /* Set some defaults to preserve compability */ + /* Set some defaults to preserve compatibility. */ if (xi.algoname[0] == '\0' && xi.type == 0) xi.type = IPFW_TABLE_ADDR; if (xi.vmask == 0) xi.vmask = IPFW_VTYPE_LEGACY; if ((error = table_do_create(oh, &xi)) != 0) err(EX_OSERR, "Table creation failed"); } /* * Creates new table * * Request: [ ipfw_obj_header ipfw_xtable_info ] * * Returns 0 on success. */ static int table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i) { char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)]; int error; memcpy(tbuf, oh, sizeof(*oh)); memcpy(tbuf + sizeof(*oh), i, sizeof(*i)); oh = (ipfw_obj_header *)tbuf; error = do_set3(IP_FW_TABLE_XCREATE, &oh->opheader, sizeof(tbuf)); return (error); } /* * Modifies existing table * * ipfw table NAME modify [ limit number ] */ static void table_modify(ipfw_obj_header *oh, int ac, char *av[]) { ipfw_xtable_info xi; int tcmd; memset(&xi, 0, sizeof(xi)); while (ac > 0) { tcmd = get_token(tablenewcmds, *av, "option"); ac--; av++; switch (tcmd) { case TOK_LIMIT: NEED1("limit value required"); xi.limit = strtol(*av, NULL, 10); xi.mflags |= IPFW_TMFLAGS_LIMIT; ac--; av++; break; default: errx(EX_USAGE, "cmd is not supported for modificatiob"); } } if (table_do_modify(oh, &xi) != 0) err(EX_OSERR, "Table modification failed"); } /* * Modifies existing table. * * Request: [ ipfw_obj_header ipfw_xtable_info ] * * Returns 0 on success. */ static int table_do_modify(ipfw_obj_header *oh, ipfw_xtable_info *i) { char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)]; int error; memcpy(tbuf, oh, sizeof(*oh)); memcpy(tbuf + sizeof(*oh), i, sizeof(*i)); oh = (ipfw_obj_header *)tbuf; error = do_set3(IP_FW_TABLE_XMODIFY, &oh->opheader, sizeof(tbuf)); return (error); } /* * Locks or unlocks given table */ static void table_lock(ipfw_obj_header *oh, int lock) { ipfw_xtable_info xi; memset(&xi, 0, sizeof(xi)); xi.mflags |= IPFW_TMFLAGS_LOCK; xi.flags |= (lock != 0) ? IPFW_TGFLAGS_LOCKED : 0; if (table_do_modify(oh, &xi) != 0) err(EX_OSERR, "Table %s failed", lock != 0 ? "lock" : "unlock"); } /* * Destroys given table specified by @oh->ntlv. * Returns 0 on success. */ static int table_destroy(ipfw_obj_header *oh) { if (do_set3(IP_FW_TABLE_XDESTROY, &oh->opheader, sizeof(*oh)) != 0) return (-1); return (0); } /* * Flushes given table specified by @oh->ntlv. * Returns 0 on success. */ static int table_flush(ipfw_obj_header *oh) { if (do_set3(IP_FW_TABLE_XFLUSH, &oh->opheader, sizeof(*oh)) != 0) return (-1); return (0); } static int table_do_swap(ipfw_obj_header *oh, char *second) { char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ntlv)]; int error; memset(tbuf, 0, sizeof(tbuf)); memcpy(tbuf, oh, sizeof(*oh)); oh = (ipfw_obj_header *)tbuf; table_fill_ntlv((ipfw_obj_ntlv *)(oh + 1), second, oh->ntlv.set, 1); error = do_set3(IP_FW_TABLE_XSWAP, &oh->opheader, sizeof(tbuf)); return (error); } /* * Swaps given table with @second one. */ static int table_swap(ipfw_obj_header *oh, char *second) { int error; if (table_check_name(second) != 0) errx(EX_USAGE, "table name %s is invalid", second); error = table_do_swap(oh, second); switch (error) { case EINVAL: errx(EX_USAGE, "Unable to swap table: check types"); case EFBIG: errx(EX_USAGE, "Unable to swap table: check limits"); } return (0); } /* * Retrieves table in given table specified by @oh->ntlv. * it inside @i. * Returns 0 on success. */ static int table_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i) { char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)]; size_t sz; sz = sizeof(tbuf); memset(tbuf, 0, sizeof(tbuf)); memcpy(tbuf, oh, sizeof(*oh)); oh = (ipfw_obj_header *)tbuf; if (do_get3(IP_FW_TABLE_XINFO, &oh->opheader, &sz) != 0) return (errno); if (sz < sizeof(tbuf)) return (EINVAL); *i = *(ipfw_xtable_info *)(oh + 1); return (0); } static struct _s_x tablealgoclass[] = { { "hash", IPFW_TACLASS_HASH }, { "array", IPFW_TACLASS_ARRAY }, { "radix", IPFW_TACLASS_RADIX }, { NULL, 0 } }; struct ta_cldata { uint8_t taclass; uint8_t spare4; uint16_t itemsize; uint16_t itemsize6; uint32_t size; uint32_t count; }; /* * Print global/per-AF table @i algorithm info. */ static void table_show_tainfo(ipfw_xtable_info *i, struct ta_cldata *d, const char *af, const char *taclass) { switch (d->taclass) { case IPFW_TACLASS_HASH: case IPFW_TACLASS_ARRAY: printf(" %salgorithm %s info\n", af, taclass); if (d->itemsize == d->itemsize6) printf(" size: %u items: %u itemsize: %u\n", d->size, d->count, d->itemsize); else printf(" size: %u items: %u " "itemsize4: %u itemsize6: %u\n", d->size, d->count, d->itemsize, d->itemsize6); break; case IPFW_TACLASS_RADIX: printf(" %salgorithm %s info\n", af, taclass); if (d->itemsize == d->itemsize6) printf(" items: %u itemsize: %u\n", d->count, d->itemsize); else printf(" items: %u " "itemsize4: %u itemsize6: %u\n", d->count, d->itemsize, d->itemsize6); break; default: printf(" algo class: %s\n", taclass); } } static void table_print_valheader(char *buf, size_t bufsize, uint32_t vmask) { if (vmask == IPFW_VTYPE_LEGACY) { snprintf(buf, bufsize, "legacy"); return; } memset(buf, 0, bufsize); print_flags_buffer(buf, bufsize, tablevaltypes, vmask); } /* * Prints table info struct @i in human-readable form. */ static int table_show_info(ipfw_xtable_info *i, void *arg) { const char *vtype; ipfw_ta_tinfo *tainfo; int afdata, afitem; struct ta_cldata d; char ttype[64], tvtype[64]; table_print_type(ttype, sizeof(ttype), i->type, i->tflags); table_print_valheader(tvtype, sizeof(tvtype), i->vmask); printf("--- table(%s), set(%u) ---\n", i->tablename, i->set); if ((i->flags & IPFW_TGFLAGS_LOCKED) != 0) printf(" kindex: %d, type: %s, locked\n", i->kidx, ttype); else printf(" kindex: %d, type: %s\n", i->kidx, ttype); printf(" references: %u, valtype: %s\n", i->refcnt, tvtype); printf(" algorithm: %s\n", i->algoname); printf(" items: %u, size: %u\n", i->count, i->size); if (i->limit > 0) printf(" limit: %u\n", i->limit); /* Print algo-specific info if requested & set */ if (arg == NULL) return (0); if ((i->ta_info.flags & IPFW_TATFLAGS_DATA) == 0) return (0); tainfo = &i->ta_info; afdata = 0; afitem = 0; if (tainfo->flags & IPFW_TATFLAGS_AFDATA) afdata = 1; if (tainfo->flags & IPFW_TATFLAGS_AFITEM) afitem = 1; memset(&d, 0, sizeof(d)); d.taclass = tainfo->taclass4; d.size = tainfo->size4; d.count = tainfo->count4; d.itemsize = tainfo->itemsize4; if (afdata == 0 && afitem != 0) d.itemsize6 = tainfo->itemsize6; else d.itemsize6 = d.itemsize; if ((vtype = match_value(tablealgoclass, d.taclass)) == NULL) vtype = "unknown"; if (afdata == 0) { table_show_tainfo(i, &d, "", vtype); } else { table_show_tainfo(i, &d, "IPv4 ", vtype); memset(&d, 0, sizeof(d)); d.taclass = tainfo->taclass6; if ((vtype = match_value(tablealgoclass, d.taclass)) == NULL) vtype = "unknown"; d.size = tainfo->size6; d.count = tainfo->count6; d.itemsize = tainfo->itemsize6; d.itemsize6 = d.itemsize; table_show_tainfo(i, &d, "IPv6 ", vtype); } return (0); } /* * Function wrappers which can be used either * as is or as foreach function parameter. */ static int table_show_one(ipfw_xtable_info *i, void *arg) { ipfw_obj_header *oh; int error; if ((error = table_do_get_list(i, &oh)) != 0) { err(EX_OSERR, "Error requesting table %s list", i->tablename); return (error); } table_show_list(oh, 1); free(oh); return (0); } static int table_flush_one(ipfw_xtable_info *i, void *arg) { ipfw_obj_header *oh; oh = (ipfw_obj_header *)arg; table_fill_ntlv(&oh->ntlv, i->tablename, i->set, 1); return (table_flush(oh)); } static int table_do_modify_record(int cmd, ipfw_obj_header *oh, ipfw_obj_tentry *tent, int count, int atomic) { ipfw_obj_ctlv *ctlv; ipfw_obj_tentry *tent_base; caddr_t pbuf; char xbuf[sizeof(*oh) + sizeof(ipfw_obj_ctlv) + sizeof(*tent)]; int error, i; size_t sz; sz = sizeof(*ctlv) + sizeof(*tent) * count; if (count == 1) { memset(xbuf, 0, sizeof(xbuf)); pbuf = xbuf; } else { if ((pbuf = calloc(1, sizeof(*oh) + sz)) == NULL) return (ENOMEM); } memcpy(pbuf, oh, sizeof(*oh)); oh = (ipfw_obj_header *)pbuf; oh->opheader.version = 1; ctlv = (ipfw_obj_ctlv *)(oh + 1); ctlv->count = count; ctlv->head.length = sz; if (atomic != 0) ctlv->flags |= IPFW_CTF_ATOMIC; tent_base = tent; memcpy(ctlv + 1, tent, sizeof(*tent) * count); tent = (ipfw_obj_tentry *)(ctlv + 1); for (i = 0; i < count; i++, tent++) { tent->head.length = sizeof(ipfw_obj_tentry); tent->idx = oh->idx; } sz += sizeof(*oh); error = do_get3(cmd, &oh->opheader, &sz); tent = (ipfw_obj_tentry *)(ctlv + 1); /* Copy result back to provided buffer */ memcpy(tent_base, ctlv + 1, sizeof(*tent) * count); if (pbuf != xbuf) free(pbuf); return (error); } static void table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add, int quiet, int update, int atomic) { ipfw_obj_tentry *ptent, tent, *tent_buf; ipfw_xtable_info xi; uint8_t type; uint32_t vmask; int cmd, count, error, i, ignored; char *texterr, *etxt, *px; if (ac == 0) errx(EX_USAGE, "address required"); if (add != 0) { cmd = IP_FW_TABLE_XADD; texterr = "Adding record failed"; } else { cmd = IP_FW_TABLE_XDEL; texterr = "Deleting record failed"; } /* * Calculate number of entries: * Assume [key val] x N for add * and * key x N for delete */ count = (add != 0) ? ac / 2 + 1 : ac; if (count <= 1) { /* Adding single entry with/without value */ memset(&tent, 0, sizeof(tent)); tent_buf = &tent; } else { if ((tent_buf = calloc(count, sizeof(tent))) == NULL) errx(EX_OSERR, "Unable to allocate memory for all entries"); } ptent = tent_buf; memset(&xi, 0, sizeof(xi)); count = 0; while (ac > 0) { tentry_fill_key(oh, ptent, *av, add, &type, &vmask, &xi); /* - * compability layer: auto-create table if not exists + * Compatibility layer: auto-create table if not exists. */ if (xi.tablename[0] == '\0') { xi.type = type; xi.vmask = vmask; strlcpy(xi.tablename, oh->ntlv.name, sizeof(xi.tablename)); fprintf(stderr, "DEPRECATED: inserting data into " "non-existent table %s. (auto-created)\n", xi.tablename); table_do_create(oh, &xi); } oh->ntlv.type = type; ac--; av++; if (add != 0 && ac > 0) { tentry_fill_value(oh, ptent, *av, type, vmask); ac--; av++; } if (update != 0) ptent->head.flags |= IPFW_TF_UPDATE; count++; ptent++; } error = table_do_modify_record(cmd, oh, tent_buf, count, atomic); quiet = 0; /* * Compatibility stuff: do not yell on duplicate keys or * failed deletions. */ if (error == 0 || (error == EEXIST && add != 0) || (error == ENOENT && add == 0)) { if (quiet != 0) { if (tent_buf != &tent) free(tent_buf); return; } } /* Report results back */ ptent = tent_buf; for (i = 0; i < count; ptent++, i++) { ignored = 0; switch (ptent->result) { case IPFW_TR_ADDED: px = "added"; break; case IPFW_TR_DELETED: px = "deleted"; break; case IPFW_TR_UPDATED: px = "updated"; break; case IPFW_TR_LIMIT: px = "limit"; ignored = 1; break; case IPFW_TR_ERROR: px = "error"; ignored = 1; break; case IPFW_TR_NOTFOUND: px = "notfound"; ignored = 1; break; case IPFW_TR_EXISTS: px = "exists"; ignored = 1; break; case IPFW_TR_IGNORED: px = "ignored"; ignored = 1; break; default: px = "unknown"; ignored = 1; } if (error != 0 && atomic != 0 && ignored == 0) printf("%s(reverted): ", px); else printf("%s: ", px); table_show_entry(&xi, ptent); } if (tent_buf != &tent) free(tent_buf); if (error == 0) return; /* Get real OS error */ error = errno; /* Try to provide more human-readable error */ switch (error) { case EEXIST: etxt = "record already exists"; break; case EFBIG: etxt = "limit hit"; break; case ESRCH: etxt = "table not found"; break; case ENOENT: etxt = "record not found"; break; case EACCES: etxt = "table is locked"; break; default: etxt = strerror(error); } errx(EX_OSERR, "%s: %s", texterr, etxt); } static int table_do_lookup(ipfw_obj_header *oh, char *key, ipfw_xtable_info *xi, ipfw_obj_tentry *xtent) { char xbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_obj_tentry)]; ipfw_obj_tentry *tent; uint8_t type; uint32_t vmask; size_t sz; memcpy(xbuf, oh, sizeof(*oh)); oh = (ipfw_obj_header *)xbuf; tent = (ipfw_obj_tentry *)(oh + 1); memset(tent, 0, sizeof(*tent)); tent->head.length = sizeof(*tent); tent->idx = 1; tentry_fill_key(oh, tent, key, 0, &type, &vmask, xi); oh->ntlv.type = type; sz = sizeof(xbuf); if (do_get3(IP_FW_TABLE_XFIND, &oh->opheader, &sz) != 0) return (errno); if (sz < sizeof(xbuf)) return (EINVAL); *xtent = *tent; return (0); } static void table_lookup(ipfw_obj_header *oh, int ac, char *av[]) { ipfw_obj_tentry xtent; ipfw_xtable_info xi; char key[64]; int error; if (ac == 0) errx(EX_USAGE, "address required"); strlcpy(key, *av, sizeof(key)); memset(&xi, 0, sizeof(xi)); error = table_do_lookup(oh, key, &xi, &xtent); switch (error) { case 0: break; case ESRCH: errx(EX_UNAVAILABLE, "Table %s not found", oh->ntlv.name); case ENOENT: errx(EX_UNAVAILABLE, "Entry %s not found", *av); case ENOTSUP: errx(EX_UNAVAILABLE, "Table %s algo does not support " "\"lookup\" method", oh->ntlv.name); default: err(EX_OSERR, "getsockopt(IP_FW_TABLE_XFIND)"); } table_show_entry(&xi, &xtent); } static void tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type, uint8_t tflags) { char *p, *pp; int mask, af; struct in6_addr *paddr, tmp; struct tflow_entry *tfe; uint32_t key, *pkey; uint16_t port; struct protoent *pent; struct servent *sent; int masklen; masklen = 0; af = 0; paddr = (struct in6_addr *)&tentry->k; switch (type) { case IPFW_TABLE_ADDR: /* Remove / if exists */ if ((p = strchr(arg, '/')) != NULL) { *p = '\0'; mask = atoi(p + 1); } if (inet_pton(AF_INET, arg, paddr) == 1) { if (p != NULL && mask > 32) errx(EX_DATAERR, "bad IPv4 mask width: %s", p + 1); masklen = p ? mask : 32; af = AF_INET; } else if (inet_pton(AF_INET6, arg, paddr) == 1) { if (IN6_IS_ADDR_V4COMPAT(paddr)) errx(EX_DATAERR, "Use IPv4 instead of v4-compatible"); if (p != NULL && mask > 128) errx(EX_DATAERR, "bad IPv6 mask width: %s", p + 1); masklen = p ? mask : 128; af = AF_INET6; } else { /* Assume FQDN */ if (lookup_host(arg, (struct in_addr *)paddr) != 0) errx(EX_NOHOST, "hostname ``%s'' unknown", arg); masklen = 32; type = IPFW_TABLE_ADDR; af = AF_INET; } break; case IPFW_TABLE_INTERFACE: /* Assume interface name. Copy significant data only */ mask = MIN(strlen(arg), IF_NAMESIZE - 1); memcpy(paddr, arg, mask); /* Set mask to exact match */ masklen = 8 * IF_NAMESIZE; break; case IPFW_TABLE_NUMBER: /* Port or any other key */ key = strtol(arg, &p, 10); if (*p != '\0') errx(EX_DATAERR, "Invalid number: %s", arg); pkey = (uint32_t *)paddr; *pkey = key; masklen = 32; break; case IPFW_TABLE_FLOW: /* Assume [src-ip][,proto][,src-port][,dst-ip][,dst-port] */ tfe = &tentry->k.flow; af = 0; /* Handle */ if ((tflags & IPFW_TFFLAG_SRCIP) != 0) { if ((p = strchr(arg, ',')) != NULL) *p++ = '\0'; /* Determine family using temporary storage */ if (inet_pton(AF_INET, arg, &tmp) == 1) { if (af != 0 && af != AF_INET) errx(EX_DATAERR, "Inconsistent address family\n"); af = AF_INET; memcpy(&tfe->a.a4.sip, &tmp, 4); } else if (inet_pton(AF_INET6, arg, &tmp) == 1) { if (af != 0 && af != AF_INET6) errx(EX_DATAERR, "Inconsistent address family\n"); af = AF_INET6; memcpy(&tfe->a.a6.sip6, &tmp, 16); } arg = p; } /* Handle */ if ((tflags & IPFW_TFFLAG_PROTO) != 0) { if (arg == NULL) errx(EX_DATAERR, "invalid key: proto missing"); if ((p = strchr(arg, ',')) != NULL) *p++ = '\0'; key = strtol(arg, &pp, 10); if (*pp != '\0') { if ((pent = getprotobyname(arg)) == NULL) errx(EX_DATAERR, "Unknown proto: %s", arg); else key = pent->p_proto; } if (key > 255) errx(EX_DATAERR, "Bad protocol number: %u",key); tfe->proto = key; arg = p; } /* Handle */ if ((tflags & IPFW_TFFLAG_SRCPORT) != 0) { if (arg == NULL) errx(EX_DATAERR, "invalid key: src port missing"); if ((p = strchr(arg, ',')) != NULL) *p++ = '\0'; if ((port = htons(strtol(arg, NULL, 10))) == 0) { if ((sent = getservbyname(arg, NULL)) == NULL) errx(EX_DATAERR, "Unknown service: %s", arg); else key = sent->s_port; } tfe->sport = port; arg = p; } /* Handle */ if ((tflags & IPFW_TFFLAG_DSTIP) != 0) { if (arg == NULL) errx(EX_DATAERR, "invalid key: dst ip missing"); if ((p = strchr(arg, ',')) != NULL) *p++ = '\0'; /* Determine family using temporary storage */ if (inet_pton(AF_INET, arg, &tmp) == 1) { if (af != 0 && af != AF_INET) errx(EX_DATAERR, "Inconsistent address family"); af = AF_INET; memcpy(&tfe->a.a4.dip, &tmp, 4); } else if (inet_pton(AF_INET6, arg, &tmp) == 1) { if (af != 0 && af != AF_INET6) errx(EX_DATAERR, "Inconsistent address family"); af = AF_INET6; memcpy(&tfe->a.a6.dip6, &tmp, 16); } arg = p; } /* Handle */ if ((tflags & IPFW_TFFLAG_DSTPORT) != 0) { if (arg == NULL) errx(EX_DATAERR, "invalid key: dst port missing"); if ((p = strchr(arg, ',')) != NULL) *p++ = '\0'; if ((port = htons(strtol(arg, NULL, 10))) == 0) { if ((sent = getservbyname(arg, NULL)) == NULL) errx(EX_DATAERR, "Unknown service: %s", arg); else key = sent->s_port; } tfe->dport = port; arg = p; } tfe->af = af; break; default: errx(EX_DATAERR, "Unsupported table type: %d", type); } tentry->subtype = af; tentry->masklen = masklen; } /* * Tries to guess table key type. * This procedure is used in legacy table auto-create * code AND in `ipfw -n` ruleset checking. * * Imported from old table_fill_xentry() parse code. */ static int guess_key_type(char *key, uint8_t *ptype) { char *p; struct in6_addr addr; uint32_t kv; if (ishexnumber(*key) != 0 || *key == ':') { /* Remove / if exists */ if ((p = strchr(key, '/')) != NULL) *p = '\0'; if ((inet_pton(AF_INET, key, &addr) == 1) || (inet_pton(AF_INET6, key, &addr) == 1)) { *ptype = IPFW_TABLE_CIDR; if (p != NULL) *p = '/'; return (0); } else { /* Port or any other key */ /* Skip non-base 10 entries like 'fa1' */ kv = strtol(key, &p, 10); if (*p == '\0') { *ptype = IPFW_TABLE_NUMBER; return (0); } else if ((p != key) && (*p == '.')) { /* * Warn on IPv4 address strings * which are "valid" for inet_aton() but not * in inet_pton(). * * Typical examples: '10.5' or '10.0.0.05' */ return (1); } } } if (strchr(key, '.') == NULL) { *ptype = IPFW_TABLE_INTERFACE; return (0); } if (lookup_host(key, (struct in_addr *)&addr) != 0) return (1); *ptype = IPFW_TABLE_CIDR; return (0); } static void tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *key, int add, uint8_t *ptype, uint32_t *pvmask, ipfw_xtable_info *xi) { uint8_t type, tflags; uint32_t vmask; int error; type = 0; tflags = 0; vmask = 0; if (xi->tablename[0] == '\0') error = table_get_info(oh, xi); else error = 0; if (error == 0) { if (co.test_only == 0) { /* Table found */ type = xi->type; tflags = xi->tflags; vmask = xi->vmask; } else { /* - * we're running `ipfw -n` - * Compability layer: try to guess key type + * We're running `ipfw -n` + * Compatibility layer: try to guess key type * before failing. */ if (guess_key_type(key, &type) != 0) { /* Inknown key */ errx(EX_USAGE, "Cannot guess " "key '%s' type", key); } vmask = IPFW_VTYPE_LEGACY; } } else { if (error != ESRCH) errx(EX_OSERR, "Error requesting table %s info", oh->ntlv.name); if (add == 0) errx(EX_DATAERR, "Table %s does not exist", oh->ntlv.name); /* * Table does not exist - * Compability layer: try to guess key type before failing. + * Compatibility layer: try to guess key type before failing. */ if (guess_key_type(key, &type) != 0) { /* Inknown key */ errx(EX_USAGE, "Table %s does not exist, cannot guess " "key '%s' type", oh->ntlv.name, key); } vmask = IPFW_VTYPE_LEGACY; } tentry_fill_key_type(key, tent, type, tflags); *ptype = type; *pvmask = vmask; } static void set_legacy_value(uint32_t val, ipfw_table_value *v) { v->tag = val; v->pipe = val; v->divert = val; v->skipto = val; v->netgraph = val; v->fib = val; v->nat = val; v->nh4 = val; v->dscp = (uint8_t)val; v->limit = val; } static void tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *arg, uint8_t type, uint32_t vmask) { struct addrinfo hints, *res; uint32_t a4, flag, val; ipfw_table_value *v; uint32_t i; int dval; char *comma, *e, *etype, *n, *p; v = &tent->v.value; /* Compat layer: keep old behavior for legacy value types */ if (vmask == IPFW_VTYPE_LEGACY) { /* Try to interpret as number first */ val = strtoul(arg, &p, 0); if (*p == '\0') { set_legacy_value(val, v); return; } if (inet_pton(AF_INET, arg, &val) == 1) { set_legacy_value(ntohl(val), v); return; } /* Try hostname */ if (lookup_host(arg, (struct in_addr *)&val) == 0) { set_legacy_value(val, v); return; } errx(EX_OSERR, "Unable to parse value %s", arg); } /* * Shorthands: handle single value if vmask consists * of numbers only. e.g.: * vmask = "fib,skipto" -> treat input "1" as "1,1" */ n = arg; etype = NULL; for (i = 1; i < (1 << 31); i *= 2) { if ((flag = (vmask & i)) == 0) continue; vmask &= ~flag; if ((comma = strchr(n, ',')) != NULL) *comma = '\0'; switch (flag) { case IPFW_VTYPE_TAG: v->tag = strtol(n, &e, 10); if (*e != '\0') etype = "tag"; break; case IPFW_VTYPE_PIPE: v->pipe = strtol(n, &e, 10); if (*e != '\0') etype = "pipe"; break; case IPFW_VTYPE_DIVERT: v->divert = strtol(n, &e, 10); if (*e != '\0') etype = "divert"; break; case IPFW_VTYPE_SKIPTO: v->skipto = strtol(n, &e, 10); if (*e != '\0') etype = "skipto"; break; case IPFW_VTYPE_NETGRAPH: v->netgraph = strtol(n, &e, 10); if (*e != '\0') etype = "netgraph"; break; case IPFW_VTYPE_FIB: v->fib = strtol(n, &e, 10); if (*e != '\0') etype = "fib"; break; case IPFW_VTYPE_NAT: v->nat = strtol(n, &e, 10); if (*e != '\0') etype = "nat"; break; case IPFW_VTYPE_LIMIT: v->limit = strtol(n, &e, 10); if (*e != '\0') etype = "limit"; break; case IPFW_VTYPE_NH4: if (strchr(n, '.') != NULL && inet_pton(AF_INET, n, &a4) == 1) { v->nh4 = ntohl(a4); break; } if (lookup_host(n, (struct in_addr *)&v->nh4) == 0) break; etype = "ipv4"; break; case IPFW_VTYPE_DSCP: if (isalpha(*n)) { if ((dval = match_token(f_ipdscp, n)) != -1) { v->dscp = dval; break; } else etype = "DSCP code"; } else { v->dscp = strtol(n, &e, 10); if (v->dscp > 63 || *e != '\0') etype = "DSCP value"; } break; case IPFW_VTYPE_NH6: if (strchr(n, ':') != NULL) { memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; hints.ai_flags = AI_NUMERICHOST; if (getaddrinfo(n, NULL, &hints, &res) == 0) { v->nh6 = ((struct sockaddr_in6 *) res->ai_addr)->sin6_addr; v->zoneid = ((struct sockaddr_in6 *) res->ai_addr)->sin6_scope_id; freeaddrinfo(res); break; } } etype = "ipv6"; break; } if (etype != NULL) errx(EX_USAGE, "Unable to parse %s as %s", n, etype); if (comma != NULL) *comma++ = ','; if ((n = comma) != NULL) continue; /* End of input. */ if (vmask != 0) errx(EX_USAGE, "Not enough fields inside value"); } } /* * Compare table names. * Honor number comparison. */ static int tablename_cmp(const void *a, const void *b) { ipfw_xtable_info *ia, *ib; ia = (ipfw_xtable_info *)a; ib = (ipfw_xtable_info *)b; return (stringnum_cmp(ia->tablename, ib->tablename)); } /* * Retrieves table list from kernel, * optionally sorts it and calls requested function for each table. * Returns 0 on success. */ static int tables_foreach(table_cb_t *f, void *arg, int sort) { ipfw_obj_lheader *olh; ipfw_xtable_info *info; size_t sz; int i, error; /* Start with reasonable default */ sz = sizeof(*olh) + 16 * sizeof(ipfw_xtable_info); for (;;) { if ((olh = calloc(1, sz)) == NULL) return (ENOMEM); olh->size = sz; if (do_get3(IP_FW_TABLES_XLIST, &olh->opheader, &sz) != 0) { sz = olh->size; free(olh); if (errno != ENOMEM) return (errno); continue; } if (sort != 0) qsort(olh + 1, olh->count, olh->objsize, tablename_cmp); info = (ipfw_xtable_info *)(olh + 1); for (i = 0; i < olh->count; i++) { error = f(info, arg); /* Ignore errors for now */ info = (ipfw_xtable_info *)((caddr_t)info + olh->objsize); } free(olh); break; } return (0); } /* * Retrieves all entries for given table @i in * eXtended format. Allocate buffer large enough * to store result. Called needs to free it later. * * Returns 0 on success. */ static int table_do_get_list(ipfw_xtable_info *i, ipfw_obj_header **poh) { ipfw_obj_header *oh; size_t sz; int c; sz = 0; oh = NULL; for (c = 0; c < 8; c++) { if (sz < i->size) sz = i->size + 44; if (oh != NULL) free(oh); if ((oh = calloc(1, sz)) == NULL) continue; table_fill_objheader(oh, i); oh->opheader.version = 1; /* Current version */ if (do_get3(IP_FW_TABLE_XLIST, &oh->opheader, &sz) == 0) { *poh = oh; return (0); } if (errno != ENOMEM) break; } free(oh); return (errno); } /* * Shows all entries from @oh in human-readable format */ static void table_show_list(ipfw_obj_header *oh, int need_header) { ipfw_obj_tentry *tent; uint32_t count; ipfw_xtable_info *i; i = (ipfw_xtable_info *)(oh + 1); tent = (ipfw_obj_tentry *)(i + 1); if (need_header) printf("--- table(%s), set(%u) ---\n", i->tablename, i->set); count = i->count; while (count > 0) { table_show_entry(i, tent); tent = (ipfw_obj_tentry *)((caddr_t)tent + tent->head.length); count--; } } static void table_show_value(char *buf, size_t bufsize, ipfw_table_value *v, uint32_t vmask, int print_ip) { char abuf[INET6_ADDRSTRLEN + IF_NAMESIZE + 2]; struct sockaddr_in6 sa6; uint32_t flag, i, l; size_t sz; struct in_addr a4; sz = bufsize; /* * Some shorthands for printing values: * legacy assumes all values are equal, so keep the first one. */ if (vmask == IPFW_VTYPE_LEGACY) { if (print_ip != 0) { flag = htonl(v->tag); inet_ntop(AF_INET, &flag, buf, sz); } else snprintf(buf, sz, "%u", v->tag); return; } for (i = 1; i < (1 << 31); i *= 2) { if ((flag = (vmask & i)) == 0) continue; l = 0; switch (flag) { case IPFW_VTYPE_TAG: l = snprintf(buf, sz, "%u,", v->tag); break; case IPFW_VTYPE_PIPE: l = snprintf(buf, sz, "%u,", v->pipe); break; case IPFW_VTYPE_DIVERT: l = snprintf(buf, sz, "%d,", v->divert); break; case IPFW_VTYPE_SKIPTO: l = snprintf(buf, sz, "%d,", v->skipto); break; case IPFW_VTYPE_NETGRAPH: l = snprintf(buf, sz, "%u,", v->netgraph); break; case IPFW_VTYPE_FIB: l = snprintf(buf, sz, "%u,", v->fib); break; case IPFW_VTYPE_NAT: l = snprintf(buf, sz, "%u,", v->nat); break; case IPFW_VTYPE_LIMIT: l = snprintf(buf, sz, "%u,", v->limit); break; case IPFW_VTYPE_NH4: a4.s_addr = htonl(v->nh4); inet_ntop(AF_INET, &a4, abuf, sizeof(abuf)); l = snprintf(buf, sz, "%s,", abuf); break; case IPFW_VTYPE_DSCP: l = snprintf(buf, sz, "%d,", v->dscp); break; case IPFW_VTYPE_NH6: sa6.sin6_family = AF_INET6; sa6.sin6_len = sizeof(sa6); sa6.sin6_addr = v->nh6; sa6.sin6_port = 0; sa6.sin6_scope_id = v->zoneid; if (getnameinfo((const struct sockaddr *)&sa6, sa6.sin6_len, abuf, sizeof(abuf), NULL, 0, NI_NUMERICHOST) == 0) l = snprintf(buf, sz, "%s,", abuf); break; } buf += l; sz -= l; } if (sz != bufsize) *(buf - 1) = '\0'; } static void table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent) { char *comma, tbuf[128], pval[128]; void *paddr; struct tflow_entry *tfe; table_show_value(pval, sizeof(pval), &tent->v.value, i->vmask, co.do_value_as_ip); switch (i->type) { case IPFW_TABLE_ADDR: /* IPv4 or IPv6 prefixes */ inet_ntop(tent->subtype, &tent->k, tbuf, sizeof(tbuf)); printf("%s/%u %s\n", tbuf, tent->masklen, pval); break; case IPFW_TABLE_INTERFACE: /* Interface names */ printf("%s %s\n", tent->k.iface, pval); break; case IPFW_TABLE_NUMBER: /* numbers */ printf("%u %s\n", tent->k.key, pval); break; case IPFW_TABLE_FLOW: /* flows */ tfe = &tent->k.flow; comma = ""; if ((i->tflags & IPFW_TFFLAG_SRCIP) != 0) { if (tfe->af == AF_INET) paddr = &tfe->a.a4.sip; else paddr = &tfe->a.a6.sip6; inet_ntop(tfe->af, paddr, tbuf, sizeof(tbuf)); printf("%s%s", comma, tbuf); comma = ","; } if ((i->tflags & IPFW_TFFLAG_PROTO) != 0) { printf("%s%d", comma, tfe->proto); comma = ","; } if ((i->tflags & IPFW_TFFLAG_SRCPORT) != 0) { printf("%s%d", comma, ntohs(tfe->sport)); comma = ","; } if ((i->tflags & IPFW_TFFLAG_DSTIP) != 0) { if (tfe->af == AF_INET) paddr = &tfe->a.a4.dip; else paddr = &tfe->a.a6.dip6; inet_ntop(tfe->af, paddr, tbuf, sizeof(tbuf)); printf("%s%s", comma, tbuf); comma = ","; } if ((i->tflags & IPFW_TFFLAG_DSTPORT) != 0) { printf("%s%d", comma, ntohs(tfe->dport)); comma = ","; } printf(" %s\n", pval); } } static int table_do_get_stdlist(uint16_t opcode, ipfw_obj_lheader **polh) { ipfw_obj_lheader req, *olh; size_t sz; memset(&req, 0, sizeof(req)); sz = sizeof(req); if (do_get3(opcode, &req.opheader, &sz) != 0) if (errno != ENOMEM) return (errno); sz = req.size; if ((olh = calloc(1, sz)) == NULL) return (ENOMEM); olh->size = sz; if (do_get3(opcode, &olh->opheader, &sz) != 0) { free(olh); return (errno); } *polh = olh; return (0); } static int table_do_get_algolist(ipfw_obj_lheader **polh) { return (table_do_get_stdlist(IP_FW_TABLES_ALIST, polh)); } static int table_do_get_vlist(ipfw_obj_lheader **polh) { return (table_do_get_stdlist(IP_FW_TABLE_VLIST, polh)); } void ipfw_list_ta(int ac, char *av[]) { ipfw_obj_lheader *olh; ipfw_ta_info *info; int error, i; const char *atype; error = table_do_get_algolist(&olh); if (error != 0) err(EX_OSERR, "Unable to request algorithm list"); info = (ipfw_ta_info *)(olh + 1); for (i = 0; i < olh->count; i++) { if ((atype = match_value(tabletypes, info->type)) == NULL) atype = "unknown"; printf("--- %s ---\n", info->algoname); printf(" type: %s\n refcount: %u\n", atype, info->refcnt); info = (ipfw_ta_info *)((caddr_t)info + olh->objsize); } free(olh); } /* Copy of current kernel table_value structure */ struct _table_value { uint32_t tag; /* O_TAG/O_TAGGED */ uint32_t pipe; /* O_PIPE/O_QUEUE */ uint16_t divert; /* O_DIVERT/O_TEE */ uint16_t skipto; /* skipto, CALLRET */ uint32_t netgraph; /* O_NETGRAPH/O_NGTEE */ uint32_t fib; /* O_SETFIB */ uint32_t nat; /* O_NAT */ uint32_t nh4; uint8_t dscp; uint8_t spare0; uint16_t spare1; /* -- 32 bytes -- */ struct in6_addr nh6; uint32_t limit; /* O_LIMIT */ uint32_t zoneid; uint64_t refcnt; /* Number of references */ }; int compare_values(const void *_a, const void *_b) { struct _table_value *a, *b; a = (struct _table_value *)_a; b = (struct _table_value *)_b; if (a->spare1 < b->spare1) return (-1); else if (a->spare1 > b->spare1) return (1); return (0); } void ipfw_list_values(int ac, char *av[]) { ipfw_obj_lheader *olh; struct _table_value *v; int error, i; uint32_t vmask; char buf[128]; error = table_do_get_vlist(&olh); if (error != 0) err(EX_OSERR, "Unable to request value list"); vmask = 0x7FFFFFFF; /* Similar to IPFW_VTYPE_LEGACY */ table_print_valheader(buf, sizeof(buf), vmask); printf("HEADER: %s\n", buf); v = (struct _table_value *)(olh + 1); qsort(v, olh->count, olh->objsize, compare_values); for (i = 0; i < olh->count; i++) { table_show_value(buf, sizeof(buf), (ipfw_table_value *)v, vmask, 0); printf("[%u] refs=%lu %s\n", v->spare1, (u_long)v->refcnt, buf); v = (struct _table_value *)((caddr_t)v + olh->objsize); } free(olh); } int table_check_name(char *tablename) { int c, i, l; /* * Check if tablename is null-terminated and contains * valid symbols only. Valid mask is: * [a-zA-Z0-9\-_\.]{1,63} */ l = strlen(tablename); if (l == 0 || l >= 64) return (EINVAL); for (i = 0; i < l; i++) { c = tablename[i]; if (isalpha(c) || isdigit(c) || c == '_' || c == '-' || c == '.') continue; return (EINVAL); } /* Restrict some 'special' names */ if (strcmp(tablename, "all") == 0) return (EINVAL); return (0); } Index: head/sbin/natd/natd.c =================================================================== --- head/sbin/natd/natd.c (revision 289676) +++ head/sbin/natd/natd.c (revision 289677) @@ -1,2043 +1,2043 @@ /* * natd - Network Address Translation Daemon for FreeBSD. * * This software is provided free of charge, with no * warranty of any kind, either expressed or implied. * Use at your own risk. * * You may copy, modify and distribute this software (natd.c) freely. * * Ari Suutari */ #include __FBSDID("$FreeBSD$"); #define SYSLOG_NAMES #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "natd.h" struct instance { const char *name; struct libalias *la; LIST_ENTRY(instance) list; int ifIndex; int assignAliasAddr; char* ifName; int logDropped; u_short inPort; u_short outPort; u_short inOutPort; struct in_addr aliasAddr; int ifMTU; int aliasOverhead; int dropIgnoredIncoming; int divertIn; int divertOut; int divertInOut; }; static LIST_HEAD(, instance) root = LIST_HEAD_INITIALIZER(root); struct libalias *mla; static struct instance *mip; static int ninstance = 1; /* * Default values for input and output * divert socket ports. */ #define DEFAULT_SERVICE "natd" /* * Definition of a port range, and macros to deal with values. * FORMAT: HI 16-bits == first port in range, 0 == all ports. * LO 16-bits == number of ports in range * NOTES: - Port values are not stored in network byte order. */ typedef u_long port_range; #define GETLOPORT(x) ((x) >> 0x10) #define GETNUMPORTS(x) ((x) & 0x0000ffff) #define GETHIPORT(x) (GETLOPORT((x)) + GETNUMPORTS((x))) /* Set y to be the low-port value in port_range variable x. */ #define SETLOPORT(x,y) ((x) = ((x) & 0x0000ffff) | ((y) << 0x10)) /* Set y to be the number of ports in port_range variable x. */ #define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y)) /* * Function prototypes. */ static void DoAliasing (int fd, int direction); static void DaemonMode (void); static void HandleRoutingInfo (int fd); static void Usage (void); static char* FormatPacket (struct ip*); static void PrintPacket (struct ip*); static void SyslogPacket (struct ip*, int priority, const char *label); static int SetAliasAddressFromIfName (const char *ifName); static void InitiateShutdown (int); static void Shutdown (int); static void RefreshAddr (int); static void ParseOption (const char* option, const char* parms); static void ReadConfigFile (const char* fileName); static void SetupPortRedirect (const char* parms); static void SetupProtoRedirect(const char* parms); static void SetupAddressRedirect (const char* parms); static void StrToAddr (const char* str, struct in_addr* addr); static u_short StrToPort (const char* str, const char* proto); static int StrToPortRange (const char* str, const char* proto, port_range *portRange); static int StrToProto (const char* str); static int StrToAddrAndPortRange (char* str, struct in_addr* addr, char* proto, port_range *portRange); static void ParseArgs (int argc, char** argv); static void SetupPunchFW(const char *strValue); static void SetupSkinnyPort(const char *strValue); static void NewInstance(const char *name); static void DoGlobal (int fd); static int CheckIpfwRulenum(unsigned int rnum); /* * Globals. */ static int verbose; static int background; static int running; static int logFacility; static int dynamicMode; static int icmpSock; static int logIpfwDenied; static const char* pidName; static int routeSock; static int globalPort; static int divertGlobal; static int exitDelay; int main (int argc, char** argv) { struct sockaddr_in addr; fd_set readMask; int fdMax; int rval; /* * Initialize packet aliasing software. * Done already here to be able to alter option bits * during command line and configuration file processing. */ NewInstance("default"); /* * Parse options. */ verbose = 0; background = 0; running = 1; dynamicMode = 0; logFacility = LOG_DAEMON; logIpfwDenied = -1; pidName = PIDFILE; routeSock = -1; icmpSock = -1; fdMax = -1; divertGlobal = -1; exitDelay = EXIT_DELAY; ParseArgs (argc, argv); /* * Log ipfw(8) denied packets by default in verbose mode. */ if (logIpfwDenied == -1) logIpfwDenied = verbose; /* * Open syslog channel. */ openlog ("natd", LOG_CONS | LOG_PID | (verbose ? LOG_PERROR : 0), logFacility); LIST_FOREACH(mip, &root, list) { mla = mip->la; /* * If not doing the transparent proxying only, * check that valid aliasing address has been given. */ if (mip->aliasAddr.s_addr == INADDR_NONE && mip->ifName == NULL && !(LibAliasSetMode(mla, 0,0) & PKT_ALIAS_PROXY_ONLY)) errx (1, "instance %s: aliasing address not given", mip->name); if (mip->aliasAddr.s_addr != INADDR_NONE && mip->ifName != NULL) errx (1, "both alias address and interface " "name are not allowed"); /* * Check that valid port number is known. */ if (mip->inPort != 0 || mip->outPort != 0) if (mip->inPort == 0 || mip->outPort == 0) errx (1, "both input and output ports are required"); if (mip->inPort == 0 && mip->outPort == 0 && mip->inOutPort == 0) ParseOption ("port", DEFAULT_SERVICE); /* * Check if ignored packets should be dropped. */ mip->dropIgnoredIncoming = LibAliasSetMode (mla, 0, 0); mip->dropIgnoredIncoming &= PKT_ALIAS_DENY_INCOMING; /* * Create divert sockets. Use only one socket if -p was specified * on command line. Otherwise, create separate sockets for - * outgoing and incoming connnections. + * outgoing and incoming connections. */ if (mip->inOutPort) { mip->divertInOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); if (mip->divertInOut == -1) Quit ("Unable to create divert socket."); if (mip->divertInOut > fdMax) fdMax = mip->divertInOut; mip->divertIn = -1; mip->divertOut = -1; /* * Bind socket. */ addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = mip->inOutPort; if (bind (mip->divertInOut, (struct sockaddr*) &addr, sizeof addr) == -1) Quit ("Unable to bind divert socket."); } else { mip->divertIn = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); if (mip->divertIn == -1) Quit ("Unable to create incoming divert socket."); if (mip->divertIn > fdMax) fdMax = mip->divertIn; mip->divertOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); if (mip->divertOut == -1) Quit ("Unable to create outgoing divert socket."); if (mip->divertOut > fdMax) fdMax = mip->divertOut; mip->divertInOut = -1; /* * Bind divert sockets. */ addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = mip->inPort; if (bind (mip->divertIn, (struct sockaddr*) &addr, sizeof addr) == -1) Quit ("Unable to bind incoming divert socket."); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = mip->outPort; if (bind (mip->divertOut, (struct sockaddr*) &addr, sizeof addr) == -1) Quit ("Unable to bind outgoing divert socket."); } /* * Create routing socket if interface name specified and in dynamic mode. */ if (mip->ifName) { if (dynamicMode) { if (routeSock == -1) routeSock = socket (PF_ROUTE, SOCK_RAW, 0); if (routeSock == -1) Quit ("Unable to create routing info socket."); if (routeSock > fdMax) fdMax = routeSock; mip->assignAliasAddr = 1; } else { do { rval = SetAliasAddressFromIfName (mip->ifName); if (background == 0 || dynamicMode == 0) break; if (rval == EAGAIN) sleep(1); } while (rval == EAGAIN); if (rval != 0) exit(1); } } } if (globalPort) { divertGlobal = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); if (divertGlobal == -1) Quit ("Unable to create divert socket."); if (divertGlobal > fdMax) fdMax = divertGlobal; /* * Bind socket. */ addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = globalPort; if (bind (divertGlobal, (struct sockaddr*) &addr, sizeof addr) == -1) Quit ("Unable to bind global divert socket."); } /* * Create socket for sending ICMP messages. */ icmpSock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); if (icmpSock == -1) Quit ("Unable to create ICMP socket."); /* * And disable reads for the socket, otherwise it slowly fills * up with received icmps which we do not use. */ shutdown(icmpSock, SHUT_RD); /* * Become a daemon unless verbose mode was requested. */ if (!verbose) DaemonMode (); /* * Catch signals to manage shutdown and * refresh of interface address. */ siginterrupt(SIGTERM, 1); siginterrupt(SIGHUP, 1); if (exitDelay) signal(SIGTERM, InitiateShutdown); else signal(SIGTERM, Shutdown); signal (SIGHUP, RefreshAddr); /* * Set alias address if it has been given. */ mip = LIST_FIRST(&root); /* XXX: simon */ LIST_FOREACH(mip, &root, list) { mla = mip->la; if (mip->aliasAddr.s_addr != INADDR_NONE) LibAliasSetAddress (mla, mip->aliasAddr); } while (running) { mip = LIST_FIRST(&root); /* XXX: simon */ if (mip->divertInOut != -1 && !mip->ifName && ninstance == 1) { /* * When using only one socket, just call * DoAliasing repeatedly to process packets. */ DoAliasing (mip->divertInOut, DONT_KNOW); continue; } /* * Build read mask from socket descriptors to select. */ FD_ZERO (&readMask); /* * Check if new packets are available. */ LIST_FOREACH(mip, &root, list) { if (mip->divertIn != -1) FD_SET (mip->divertIn, &readMask); if (mip->divertOut != -1) FD_SET (mip->divertOut, &readMask); if (mip->divertInOut != -1) FD_SET (mip->divertInOut, &readMask); } /* * Routing info is processed always. */ if (routeSock != -1) FD_SET (routeSock, &readMask); if (divertGlobal != -1) FD_SET (divertGlobal, &readMask); if (select (fdMax + 1, &readMask, NULL, NULL, NULL) == -1) { if (errno == EINTR) continue; Quit ("Select failed."); } if (divertGlobal != -1) if (FD_ISSET (divertGlobal, &readMask)) DoGlobal (divertGlobal); LIST_FOREACH(mip, &root, list) { mla = mip->la; if (mip->divertIn != -1) if (FD_ISSET (mip->divertIn, &readMask)) DoAliasing (mip->divertIn, INPUT); if (mip->divertOut != -1) if (FD_ISSET (mip->divertOut, &readMask)) DoAliasing (mip->divertOut, OUTPUT); if (mip->divertInOut != -1) if (FD_ISSET (mip->divertInOut, &readMask)) DoAliasing (mip->divertInOut, DONT_KNOW); } if (routeSock != -1) if (FD_ISSET (routeSock, &readMask)) HandleRoutingInfo (routeSock); } if (background) unlink (pidName); return 0; } static void DaemonMode(void) { FILE* pidFile; daemon (0, 0); background = 1; pidFile = fopen (pidName, "w"); if (pidFile) { fprintf (pidFile, "%d\n", getpid ()); fclose (pidFile); } } static void ParseArgs (int argc, char** argv) { int arg; char* opt; char parmBuf[256]; int len; /* bounds checking */ for (arg = 1; arg < argc; arg++) { opt = argv[arg]; if (*opt != '-') { warnx ("invalid option %s", opt); Usage (); } parmBuf[0] = '\0'; len = 0; while (arg < argc - 1) { if (argv[arg + 1][0] == '-') break; if (len) { strncat (parmBuf, " ", sizeof(parmBuf) - (len + 1)); len += strlen(parmBuf + len); } ++arg; strncat (parmBuf, argv[arg], sizeof(parmBuf) - (len + 1)); len += strlen(parmBuf + len); } ParseOption (opt + 1, (len ? parmBuf : NULL)); } } static void DoGlobal (int fd) { int bytes; int origBytes; char buf[IP_MAXPACKET]; struct sockaddr_in addr; int wrote; socklen_t addrSize; struct ip* ip; char msgBuf[80]; /* * Get packet from socket. */ addrSize = sizeof addr; origBytes = recvfrom (fd, buf, sizeof buf, 0, (struct sockaddr*) &addr, &addrSize); if (origBytes == -1) { if (errno != EINTR) Warn ("read from divert socket failed"); return; } #if 0 if (mip->assignAliasAddr) { if (SetAliasAddressFromIfName (mip->ifName) != 0) exit(1); mip->assignAliasAddr = 0; } #endif /* * This is an IP packet. */ ip = (struct ip*) buf; if (verbose) { /* * Print packet direction and protocol type. */ printf ("Glb "); switch (ip->ip_p) { case IPPROTO_TCP: printf ("[TCP] "); break; case IPPROTO_UDP: printf ("[UDP] "); break; case IPPROTO_ICMP: printf ("[ICMP] "); break; default: printf ("[%d] ", ip->ip_p); break; } /* * Print addresses. */ PrintPacket (ip); } LIST_FOREACH(mip, &root, list) { mla = mip->la; if (LibAliasOutTry (mla, buf, IP_MAXPACKET, 0) != PKT_ALIAS_IGNORED) break; } /* * Length might have changed during aliasing. */ bytes = ntohs (ip->ip_len); /* * Update alias overhead size for outgoing packets. */ if (mip != NULL && bytes - origBytes > mip->aliasOverhead) mip->aliasOverhead = bytes - origBytes; if (verbose) { /* * Print addresses after aliasing. */ printf (" aliased to\n"); printf (" "); PrintPacket (ip); printf ("\n"); } /* * Put packet back for processing. */ wrote = sendto (fd, buf, bytes, 0, (struct sockaddr*) &addr, sizeof addr); if (wrote != bytes) { if (errno == EMSGSIZE) { if (mip->ifMTU != -1) SendNeedFragIcmp (icmpSock, (struct ip*) buf, mip->ifMTU - mip->aliasOverhead); } else if (errno == EACCES && logIpfwDenied) { sprintf (msgBuf, "failed to write packet back"); Warn (msgBuf); } } } static void DoAliasing (int fd, int direction) { int bytes; int origBytes; char buf[IP_MAXPACKET]; struct sockaddr_in addr; int wrote; int status; socklen_t addrSize; struct ip* ip; char msgBuf[80]; int rval; if (mip->assignAliasAddr) { do { rval = SetAliasAddressFromIfName (mip->ifName); if (background == 0 || dynamicMode == 0) break; if (rval == EAGAIN) sleep(1); } while (rval == EAGAIN); if (rval != 0) exit(1); mip->assignAliasAddr = 0; } /* * Get packet from socket. */ addrSize = sizeof addr; origBytes = recvfrom (fd, buf, sizeof buf, 0, (struct sockaddr*) &addr, &addrSize); if (origBytes == -1) { if (errno != EINTR) Warn ("read from divert socket failed"); return; } /* * This is an IP packet. */ ip = (struct ip*) buf; if (direction == DONT_KNOW) { if (addr.sin_addr.s_addr == INADDR_ANY) direction = OUTPUT; else direction = INPUT; } if (verbose) { /* * Print packet direction and protocol type. */ printf (direction == OUTPUT ? "Out " : "In "); if (ninstance > 1) printf ("{%s}", mip->name); switch (ip->ip_p) { case IPPROTO_TCP: printf ("[TCP] "); break; case IPPROTO_UDP: printf ("[UDP] "); break; case IPPROTO_ICMP: printf ("[ICMP] "); break; default: printf ("[%d] ", ip->ip_p); break; } /* * Print addresses. */ PrintPacket (ip); } if (direction == OUTPUT) { /* * Outgoing packets. Do aliasing. */ LibAliasOut (mla, buf, IP_MAXPACKET); } else { /* * Do aliasing. */ status = LibAliasIn (mla, buf, IP_MAXPACKET); if (status == PKT_ALIAS_IGNORED && mip->dropIgnoredIncoming) { if (verbose) printf (" dropped.\n"); if (mip->logDropped) SyslogPacket (ip, LOG_WARNING, "denied"); return; } } /* * Length might have changed during aliasing. */ bytes = ntohs (ip->ip_len); /* * Update alias overhead size for outgoing packets. */ if (direction == OUTPUT && bytes - origBytes > mip->aliasOverhead) mip->aliasOverhead = bytes - origBytes; if (verbose) { /* * Print addresses after aliasing. */ printf (" aliased to\n"); printf (" "); PrintPacket (ip); printf ("\n"); } /* * Put packet back for processing. */ wrote = sendto (fd, buf, bytes, 0, (struct sockaddr*) &addr, sizeof addr); if (wrote != bytes) { if (errno == EMSGSIZE) { if (direction == OUTPUT && mip->ifMTU != -1) SendNeedFragIcmp (icmpSock, (struct ip*) buf, mip->ifMTU - mip->aliasOverhead); } else if (errno == EACCES && logIpfwDenied) { sprintf (msgBuf, "failed to write packet back"); Warn (msgBuf); } } } static void HandleRoutingInfo (int fd) { int bytes; struct if_msghdr ifMsg; /* * Get packet from socket. */ bytes = read (fd, &ifMsg, sizeof ifMsg); if (bytes == -1) { Warn ("read from routing socket failed"); return; } if (ifMsg.ifm_version != RTM_VERSION) { Warn ("unexpected packet read from routing socket"); return; } if (verbose) printf ("Routing message %#x received.\n", ifMsg.ifm_type); if ((ifMsg.ifm_type == RTM_NEWADDR || ifMsg.ifm_type == RTM_IFINFO)) { LIST_FOREACH(mip, &root, list) { mla = mip->la; if (ifMsg.ifm_index == mip->ifIndex) { if (verbose) printf("Interface address/MTU has probably changed.\n"); mip->assignAliasAddr = 1; } } } } static void PrintPacket (struct ip* ip) { printf ("%s", FormatPacket (ip)); } static void SyslogPacket (struct ip* ip, int priority, const char *label) { syslog (priority, "%s %s", label, FormatPacket (ip)); } static char* FormatPacket (struct ip* ip) { static char buf[256]; struct tcphdr* tcphdr; struct udphdr* udphdr; struct icmp* icmphdr; char src[20]; char dst[20]; strcpy (src, inet_ntoa (ip->ip_src)); strcpy (dst, inet_ntoa (ip->ip_dst)); switch (ip->ip_p) { case IPPROTO_TCP: tcphdr = (struct tcphdr*) ((char*) ip + (ip->ip_hl << 2)); sprintf (buf, "[TCP] %s:%d -> %s:%d", src, ntohs (tcphdr->th_sport), dst, ntohs (tcphdr->th_dport)); break; case IPPROTO_UDP: udphdr = (struct udphdr*) ((char*) ip + (ip->ip_hl << 2)); sprintf (buf, "[UDP] %s:%d -> %s:%d", src, ntohs (udphdr->uh_sport), dst, ntohs (udphdr->uh_dport)); break; case IPPROTO_ICMP: icmphdr = (struct icmp*) ((char*) ip + (ip->ip_hl << 2)); sprintf (buf, "[ICMP] %s -> %s %u(%u)", src, dst, icmphdr->icmp_type, icmphdr->icmp_code); break; default: sprintf (buf, "[%d] %s -> %s ", ip->ip_p, src, dst); break; } return buf; } static int SetAliasAddressFromIfName(const char *ifn) { size_t needed; int mib[6]; char *buf, *lim, *next; struct if_msghdr *ifm; struct ifa_msghdr *ifam; struct sockaddr_dl *sdl; struct sockaddr_in *sin; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET; /* Only IP addresses please */ mib[4] = NET_RT_IFLIST; mib[5] = 0; /* ifIndex??? */ /* * Get interface data. */ if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1) err(1, "iflist-sysctl-estimate"); if ((buf = malloc(needed)) == NULL) errx(1, "malloc failed"); if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1 && errno != ENOMEM) err(1, "iflist-sysctl-get"); lim = buf + needed; /* * Loop through interfaces until one with * given name is found. This is done to * find correct interface index for routing * message processing. */ mip->ifIndex = 0; next = buf; while (next < lim) { ifm = (struct if_msghdr *)next; next += ifm->ifm_msglen; if (ifm->ifm_version != RTM_VERSION) { if (verbose) warnx("routing message version %d " "not understood", ifm->ifm_version); continue; } if (ifm->ifm_type == RTM_IFINFO) { sdl = (struct sockaddr_dl *)(ifm + 1); if (strlen(ifn) == sdl->sdl_nlen && strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) { mip->ifIndex = ifm->ifm_index; mip->ifMTU = ifm->ifm_data.ifi_mtu; break; } } } if (!mip->ifIndex) errx(1, "unknown interface name %s", ifn); /* * Get interface address. */ sin = NULL; while (next < lim) { ifam = (struct ifa_msghdr *)next; next += ifam->ifam_msglen; if (ifam->ifam_version != RTM_VERSION) { if (verbose) warnx("routing message version %d " "not understood", ifam->ifam_version); continue; } if (ifam->ifam_type != RTM_NEWADDR) break; if (ifam->ifam_addrs & RTA_IFA) { int i; char *cp = (char *)(ifam + 1); for (i = 1; i < RTA_IFA; i <<= 1) if (ifam->ifam_addrs & i) cp += SA_SIZE((struct sockaddr *)cp); if (((struct sockaddr *)cp)->sa_family == AF_INET) { sin = (struct sockaddr_in *)cp; break; } } } if (sin == NULL) { warnx("%s: cannot get interface address", ifn); free(buf); return EAGAIN; } LibAliasSetAddress(mla, sin->sin_addr); syslog(LOG_INFO, "Aliasing to %s, mtu %d bytes", inet_ntoa(sin->sin_addr), mip->ifMTU); free(buf); return 0; } void Quit (const char* msg) { Warn (msg); exit (1); } void Warn (const char* msg) { if (background) syslog (LOG_ALERT, "%s (%m)", msg); else warn ("%s", msg); } static void RefreshAddr (int sig __unused) { LibAliasRefreshModules(); if (mip != NULL && mip->ifName != NULL) mip->assignAliasAddr = 1; } static void InitiateShutdown (int sig __unused) { /* * Start timer to allow kernel gracefully * shutdown existing connections when system * is shut down. */ siginterrupt(SIGALRM, 1); signal (SIGALRM, Shutdown); ualarm(exitDelay*1000, 1000); } static void Shutdown (int sig __unused) { running = 0; } /* * Different options recognized by this program. */ enum Option { LibAliasOption, Instance, Verbose, InPort, OutPort, Port, GlobalPort, AliasAddress, TargetAddress, InterfaceName, RedirectPort, RedirectProto, RedirectAddress, ConfigFile, DynamicMode, ProxyRule, LogDenied, LogFacility, PunchFW, SkinnyPort, LogIpfwDenied, PidFile, ExitDelay }; enum Param { YesNo, Numeric, String, None, Address, Service }; /* * Option information structure (used by ParseOption). */ struct OptionInfo { enum Option type; int packetAliasOpt; enum Param parm; const char* parmDescription; const char* description; const char* name; const char* shortName; }; /* * Table of known options. */ static struct OptionInfo optionTable[] = { { LibAliasOption, PKT_ALIAS_UNREGISTERED_ONLY, YesNo, "[yes|no]", "alias only unregistered addresses", "unregistered_only", "u" }, { LibAliasOption, PKT_ALIAS_LOG, YesNo, "[yes|no]", "enable logging", "log", "l" }, { LibAliasOption, PKT_ALIAS_PROXY_ONLY, YesNo, "[yes|no]", "proxy only", "proxy_only", NULL }, { LibAliasOption, PKT_ALIAS_REVERSE, YesNo, "[yes|no]", "operate in reverse mode", "reverse", NULL }, { LibAliasOption, PKT_ALIAS_DENY_INCOMING, YesNo, "[yes|no]", "allow incoming connections", "deny_incoming", "d" }, { LibAliasOption, PKT_ALIAS_USE_SOCKETS, YesNo, "[yes|no]", "use sockets to inhibit port conflict", "use_sockets", "s" }, { LibAliasOption, PKT_ALIAS_SAME_PORTS, YesNo, "[yes|no]", "try to keep original port numbers for connections", "same_ports", "m" }, { Verbose, 0, YesNo, "[yes|no]", "verbose mode, dump packet information", "verbose", "v" }, { DynamicMode, 0, YesNo, "[yes|no]", "dynamic mode, automatically detect interface address changes", "dynamic", NULL }, { InPort, 0, Service, "number|service_name", "set port for incoming packets", "in_port", "i" }, { OutPort, 0, Service, "number|service_name", "set port for outgoing packets", "out_port", "o" }, { Port, 0, Service, "number|service_name", "set port (defaults to natd/divert)", "port", "p" }, { GlobalPort, 0, Service, "number|service_name", "set globalport", "globalport", NULL }, { AliasAddress, 0, Address, "x.x.x.x", "address to use for aliasing", "alias_address", "a" }, { TargetAddress, 0, Address, "x.x.x.x", "address to use for incoming sessions", "target_address", "t" }, { InterfaceName, 0, String, "network_if_name", "take aliasing address from interface", "interface", "n" }, { ProxyRule, 0, String, "[type encode_ip_hdr|encode_tcp_stream] port xxxx server " "a.b.c.d:yyyy", "add transparent proxying / destination NAT", "proxy_rule", NULL }, { RedirectPort, 0, String, "tcp|udp local_addr:local_port_range[,...] [public_addr:]public_port_range" " [remote_addr[:remote_port_range]]", "redirect a port (or ports) for incoming traffic", "redirect_port", NULL }, { RedirectProto, 0, String, "proto local_addr [public_addr] [remote_addr]", "redirect packets of a given proto", "redirect_proto", NULL }, { RedirectAddress, 0, String, "local_addr[,...] public_addr", "define mapping between local and public addresses", "redirect_address", NULL }, { ConfigFile, 0, String, "file_name", "read options from configuration file", "config", "f" }, { LogDenied, 0, YesNo, "[yes|no]", "enable logging of denied incoming packets", "log_denied", NULL }, { LogFacility, 0, String, "facility", "name of syslog facility to use for logging", "log_facility", NULL }, { PunchFW, 0, String, "basenumber:count", "punch holes in the firewall for incoming FTP/IRC DCC connections", "punch_fw", NULL }, { SkinnyPort, 0, String, "port", "set the TCP port for use with the Skinny Station protocol", "skinny_port", NULL }, { LogIpfwDenied, 0, YesNo, "[yes|no]", "log packets converted by natd, but denied by ipfw", "log_ipfw_denied", NULL }, { PidFile, 0, String, "file_name", "store PID in an alternate file", "pid_file", "P" }, { Instance, 0, String, "instance name", "name of aliasing engine instance", "instance", NULL }, { ExitDelay, 0, Numeric, "ms", "delay in ms before daemon exit after signal", "exit_delay", NULL }, }; static void ParseOption (const char* option, const char* parms) { int i; struct OptionInfo* info; int yesNoValue; int aliasValue; int numValue; u_short uNumValue; const char* strValue; struct in_addr addrValue; int max; char* end; const CODE* fac_record = NULL; /* * Find option from table. */ max = sizeof (optionTable) / sizeof (struct OptionInfo); for (i = 0, info = optionTable; i < max; i++, info++) { if (!strcmp (info->name, option)) break; if (info->shortName) if (!strcmp (info->shortName, option)) break; } if (i >= max) { warnx ("unknown option %s", option); Usage (); } uNumValue = 0; yesNoValue = 0; numValue = 0; strValue = NULL; /* * Check parameters. */ switch (info->parm) { case YesNo: if (!parms) parms = "yes"; if (!strcmp (parms, "yes")) yesNoValue = 1; else if (!strcmp (parms, "no")) yesNoValue = 0; else errx (1, "%s needs yes/no parameter", option); break; case Service: if (!parms) errx (1, "%s needs service name or " "port number parameter", option); uNumValue = StrToPort (parms, "divert"); break; case Numeric: if (parms) numValue = strtol (parms, &end, 10); else end = NULL; if (end == parms) errx (1, "%s needs numeric parameter", option); break; case String: strValue = parms; if (!strValue) errx (1, "%s needs parameter", option); break; case None: if (parms) errx (1, "%s does not take parameters", option); break; case Address: if (!parms) errx (1, "%s needs address/host parameter", option); StrToAddr (parms, &addrValue); break; } switch (info->type) { case LibAliasOption: aliasValue = yesNoValue ? info->packetAliasOpt : 0; LibAliasSetMode (mla, aliasValue, info->packetAliasOpt); break; case Verbose: verbose = yesNoValue; break; case DynamicMode: dynamicMode = yesNoValue; break; case InPort: mip->inPort = uNumValue; break; case OutPort: mip->outPort = uNumValue; break; case Port: mip->inOutPort = uNumValue; break; case GlobalPort: globalPort = uNumValue; break; case AliasAddress: memcpy (&mip->aliasAddr, &addrValue, sizeof (struct in_addr)); break; case TargetAddress: LibAliasSetTarget(mla, addrValue); break; case RedirectPort: SetupPortRedirect (strValue); break; case RedirectProto: SetupProtoRedirect(strValue); break; case RedirectAddress: SetupAddressRedirect (strValue); break; case ProxyRule: LibAliasProxyRule (mla, strValue); break; case InterfaceName: if (mip->ifName) free (mip->ifName); mip->ifName = strdup (strValue); break; case ConfigFile: ReadConfigFile (strValue); break; case LogDenied: mip->logDropped = yesNoValue; break; case LogFacility: fac_record = facilitynames; while (fac_record->c_name != NULL) { if (!strcmp (fac_record->c_name, strValue)) { logFacility = fac_record->c_val; break; } else fac_record++; } if(fac_record->c_name == NULL) errx(1, "Unknown log facility name: %s", strValue); break; case PunchFW: SetupPunchFW(strValue); break; case SkinnyPort: SetupSkinnyPort(strValue); break; case LogIpfwDenied: logIpfwDenied = yesNoValue; break; case PidFile: pidName = strdup (strValue); break; case Instance: NewInstance(strValue); break; case ExitDelay: if (numValue < 0 || numValue > MAX_EXIT_DELAY) errx(1, "Incorrect exit delay: %d", numValue); exitDelay = numValue; break; } } void ReadConfigFile (const char* fileName) { FILE* file; char *buf; size_t len; char *ptr, *p; char* option; file = fopen (fileName, "r"); if (!file) err(1, "cannot open config file %s", fileName); while ((buf = fgetln(file, &len)) != NULL) { if (buf[len - 1] == '\n') buf[len - 1] = '\0'; else errx(1, "config file format error: " "last line should end with newline"); /* * Check for comments, strip off trailing spaces. */ if ((ptr = strchr(buf, '#'))) *ptr = '\0'; for (ptr = buf; isspace(*ptr); ++ptr) continue; if (*ptr == '\0') continue; for (p = strchr(buf, '\0'); isspace(*--p);) continue; *++p = '\0'; /* * Extract option name. */ option = ptr; while (*ptr && !isspace (*ptr)) ++ptr; if (*ptr != '\0') { *ptr = '\0'; ++ptr; } /* * Skip white space between name and parms. */ while (*ptr && isspace (*ptr)) ++ptr; ParseOption (option, *ptr ? ptr : NULL); } fclose (file); } static void Usage(void) { int i; int max; struct OptionInfo* info; fprintf (stderr, "Recognized options:\n\n"); max = sizeof (optionTable) / sizeof (struct OptionInfo); for (i = 0, info = optionTable; i < max; i++, info++) { fprintf (stderr, "-%-20s %s\n", info->name, info->parmDescription); if (info->shortName) fprintf (stderr, "-%-20s %s\n", info->shortName, info->parmDescription); fprintf (stderr, " %s\n\n", info->description); } exit (1); } void SetupPortRedirect (const char* parms) { char *buf; char* ptr; char* serverPool; struct in_addr localAddr; struct in_addr publicAddr; struct in_addr remoteAddr; port_range portRange; u_short localPort = 0; u_short publicPort = 0; u_short remotePort = 0; u_short numLocalPorts = 0; u_short numPublicPorts = 0; u_short numRemotePorts = 0; int proto; char* protoName; char* separator; int i; struct alias_link *aliaslink = NULL; buf = strdup (parms); if (!buf) errx (1, "redirect_port: strdup() failed"); /* * Extract protocol. */ protoName = strtok (buf, " \t"); if (!protoName) errx (1, "redirect_port: missing protocol"); proto = StrToProto (protoName); /* * Extract local address. */ ptr = strtok (NULL, " \t"); if (!ptr) errx (1, "redirect_port: missing local address"); separator = strchr(ptr, ','); if (separator) { /* LSNAT redirection syntax. */ localAddr.s_addr = INADDR_NONE; localPort = ~0; numLocalPorts = 1; serverPool = ptr; } else { if ( StrToAddrAndPortRange (ptr, &localAddr, protoName, &portRange) != 0 ) errx (1, "redirect_port: invalid local port range"); localPort = GETLOPORT(portRange); numLocalPorts = GETNUMPORTS(portRange); serverPool = NULL; } /* * Extract public port and optionally address. */ ptr = strtok (NULL, " \t"); if (!ptr) errx (1, "redirect_port: missing public port"); separator = strchr (ptr, ':'); if (separator) { if (StrToAddrAndPortRange (ptr, &publicAddr, protoName, &portRange) != 0 ) errx (1, "redirect_port: invalid public port range"); } else { publicAddr.s_addr = INADDR_ANY; if (StrToPortRange (ptr, protoName, &portRange) != 0) errx (1, "redirect_port: invalid public port range"); } publicPort = GETLOPORT(portRange); numPublicPorts = GETNUMPORTS(portRange); /* * Extract remote address and optionally port. */ ptr = strtok (NULL, " \t"); if (ptr) { separator = strchr (ptr, ':'); if (separator) { if (StrToAddrAndPortRange (ptr, &remoteAddr, protoName, &portRange) != 0) errx (1, "redirect_port: invalid remote port range"); } else { SETLOPORT(portRange, 0); SETNUMPORTS(portRange, 1); StrToAddr (ptr, &remoteAddr); } } else { SETLOPORT(portRange, 0); SETNUMPORTS(portRange, 1); remoteAddr.s_addr = INADDR_ANY; } remotePort = GETLOPORT(portRange); numRemotePorts = GETNUMPORTS(portRange); /* * Make sure port ranges match up, then add the redirect ports. */ if (numLocalPorts != numPublicPorts) errx (1, "redirect_port: port ranges must be equal in size"); /* Remote port range is allowed to be '0' which means all ports. */ if (numRemotePorts != numLocalPorts && (numRemotePorts != 1 || remotePort != 0)) errx (1, "redirect_port: remote port must be 0 or equal to local port range in size"); for (i = 0 ; i < numPublicPorts ; ++i) { /* If remotePort is all ports, set it to 0. */ u_short remotePortCopy = remotePort + i; if (numRemotePorts == 1 && remotePort == 0) remotePortCopy = 0; aliaslink = LibAliasRedirectPort (mla, localAddr, htons(localPort + i), remoteAddr, htons(remotePortCopy), publicAddr, htons(publicPort + i), proto); } /* * Setup LSNAT server pool. */ if (serverPool != NULL && aliaslink != NULL) { ptr = strtok(serverPool, ","); while (ptr != NULL) { if (StrToAddrAndPortRange(ptr, &localAddr, protoName, &portRange) != 0) errx(1, "redirect_port: invalid local port range"); localPort = GETLOPORT(portRange); if (GETNUMPORTS(portRange) != 1) errx(1, "redirect_port: local port must be single in this context"); LibAliasAddServer(mla, aliaslink, localAddr, htons(localPort)); ptr = strtok(NULL, ","); } } free (buf); } void SetupProtoRedirect(const char* parms) { char *buf; char* ptr; struct in_addr localAddr; struct in_addr publicAddr; struct in_addr remoteAddr; int proto; char* protoName; struct protoent *protoent; buf = strdup (parms); if (!buf) errx (1, "redirect_port: strdup() failed"); /* * Extract protocol. */ protoName = strtok(buf, " \t"); if (!protoName) errx(1, "redirect_proto: missing protocol"); protoent = getprotobyname(protoName); if (protoent == NULL) errx(1, "redirect_proto: unknown protocol %s", protoName); else proto = protoent->p_proto; /* * Extract local address. */ ptr = strtok(NULL, " \t"); if (!ptr) errx(1, "redirect_proto: missing local address"); else StrToAddr(ptr, &localAddr); /* * Extract optional public address. */ ptr = strtok(NULL, " \t"); if (ptr) StrToAddr(ptr, &publicAddr); else publicAddr.s_addr = INADDR_ANY; /* * Extract optional remote address. */ ptr = strtok(NULL, " \t"); if (ptr) StrToAddr(ptr, &remoteAddr); else remoteAddr.s_addr = INADDR_ANY; /* * Create aliasing link. */ (void)LibAliasRedirectProto(mla, localAddr, remoteAddr, publicAddr, proto); free (buf); } void SetupAddressRedirect (const char* parms) { char *buf; char* ptr; char* separator; struct in_addr localAddr; struct in_addr publicAddr; char* serverPool; struct alias_link *aliaslink; buf = strdup (parms); if (!buf) errx (1, "redirect_port: strdup() failed"); /* * Extract local address. */ ptr = strtok (buf, " \t"); if (!ptr) errx (1, "redirect_address: missing local address"); separator = strchr(ptr, ','); if (separator) { /* LSNAT redirection syntax. */ localAddr.s_addr = INADDR_NONE; serverPool = ptr; } else { StrToAddr (ptr, &localAddr); serverPool = NULL; } /* * Extract public address. */ ptr = strtok (NULL, " \t"); if (!ptr) errx (1, "redirect_address: missing public address"); StrToAddr (ptr, &publicAddr); aliaslink = LibAliasRedirectAddr(mla, localAddr, publicAddr); /* * Setup LSNAT server pool. */ if (serverPool != NULL && aliaslink != NULL) { ptr = strtok(serverPool, ","); while (ptr != NULL) { StrToAddr(ptr, &localAddr); LibAliasAddServer(mla, aliaslink, localAddr, htons(~0)); ptr = strtok(NULL, ","); } } free (buf); } void StrToAddr (const char* str, struct in_addr* addr) { struct hostent* hp; if (inet_aton (str, addr)) return; hp = gethostbyname (str); if (!hp) errx (1, "unknown host %s", str); memcpy (addr, hp->h_addr, sizeof (struct in_addr)); } u_short StrToPort (const char* str, const char* proto) { u_short port; struct servent* sp; char* end; port = strtol (str, &end, 10); if (end != str) return htons (port); sp = getservbyname (str, proto); if (!sp) errx (1, "%s/%s: unknown service", str, proto); return sp->s_port; } int StrToPortRange (const char* str, const char* proto, port_range *portRange) { const char* sep; struct servent* sp; char* end; u_short loPort; u_short hiPort; /* First see if this is a service, return corresponding port if so. */ sp = getservbyname (str,proto); if (sp) { SETLOPORT(*portRange, ntohs(sp->s_port)); SETNUMPORTS(*portRange, 1); return 0; } /* Not a service, see if it's a single port or port range. */ sep = strchr (str, '-'); if (sep == NULL) { SETLOPORT(*portRange, strtol(str, &end, 10)); if (end != str) { /* Single port. */ SETNUMPORTS(*portRange, 1); return 0; } /* Error in port range field. */ errx (1, "%s/%s: unknown service", str, proto); } /* Port range, get the values and sanity check. */ sscanf (str, "%hu-%hu", &loPort, &hiPort); SETLOPORT(*portRange, loPort); SETNUMPORTS(*portRange, 0); /* Error by default */ if (loPort <= hiPort) SETNUMPORTS(*portRange, hiPort - loPort + 1); if (GETNUMPORTS(*portRange) == 0) errx (1, "invalid port range %s", str); return 0; } static int StrToProto (const char* str) { if (!strcmp (str, "tcp")) return IPPROTO_TCP; if (!strcmp (str, "udp")) return IPPROTO_UDP; errx (1, "unknown protocol %s. Expected tcp or udp", str); } static int StrToAddrAndPortRange (char* str, struct in_addr* addr, char* proto, port_range *portRange) { char* ptr; ptr = strchr (str, ':'); if (!ptr) errx (1, "%s is missing port number", str); *ptr = '\0'; ++ptr; StrToAddr (str, addr); return StrToPortRange (ptr, proto, portRange); } static void SetupPunchFW(const char *strValue) { unsigned int base, num; if (sscanf(strValue, "%u:%u", &base, &num) != 2) errx(1, "punch_fw: basenumber:count parameter required"); if (CheckIpfwRulenum(base + num - 1) == -1) errx(1, "punch_fw: basenumber:count parameter should fit " "the maximum allowed rule numbers"); LibAliasSetFWBase(mla, base, num); (void)LibAliasSetMode(mla, PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW); } static void SetupSkinnyPort(const char *strValue) { unsigned int port; if (sscanf(strValue, "%u", &port) != 1) errx(1, "skinny_port: port parameter required"); LibAliasSetSkinnyPort(mla, port); } static void NewInstance(const char *name) { struct instance *ip; LIST_FOREACH(ip, &root, list) { if (!strcmp(ip->name, name)) { mla = ip->la; mip = ip; return; } } ninstance++; ip = calloc(sizeof *ip, 1); ip->name = strdup(name); ip->la = LibAliasInit (ip->la); ip->assignAliasAddr = 0; ip->ifName = NULL; ip->logDropped = 0; ip->inPort = 0; ip->outPort = 0; ip->inOutPort = 0; ip->aliasAddr.s_addr = INADDR_NONE; ip->ifMTU = -1; ip->aliasOverhead = 12; LIST_INSERT_HEAD(&root, ip, list); mla = ip->la; mip = ip; } static int CheckIpfwRulenum(unsigned int rnum) { unsigned int default_rule; size_t len = sizeof(default_rule); if (sysctlbyname("net.inet.ip.fw.default_rule", &default_rule, &len, NULL, 0) == -1) { warn("Failed to get the default ipfw rule number, using " "default historical value 65535. The reason was"); default_rule = 65535; } if (rnum >= default_rule) { return -1; } return 0; } Index: head/sbin/newfs_nandfs/newfs_nandfs.c =================================================================== --- head/sbin/newfs_nandfs/newfs_nandfs.c (revision 289676) +++ head/sbin/newfs_nandfs/newfs_nandfs.c (revision 289677) @@ -1,1179 +1,1179 @@ /*- * Copyright (c) 2010-2012 Semihalf. * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEBUG #undef DEBUG #ifdef DEBUG #define debug(fmt, args...) do { \ printf("nandfs:" fmt "\n", ##args); } while (0) #else #define debug(fmt, args...) #endif #define NANDFS_FIRST_BLOCK nandfs_first_block() #define NANDFS_FIRST_CNO 1 #define NANDFS_BLOCK_BAD 1 #define NANDFS_BLOCK_GOOD 0 struct file_info { uint64_t ino; const char *name; uint32_t mode; uint64_t size; uint8_t nblocks; uint32_t *blocks; struct nandfs_inode *inode; }; static struct file_info user_files[] = { { NANDFS_ROOT_INO, NULL, S_IFDIR | 0755, 0, 1, NULL, NULL }, }; static struct file_info ifile = { NANDFS_IFILE_INO, NULL, 0, 0, -1, NULL, NULL }; static struct file_info sufile = { NANDFS_SUFILE_INO, NULL, 0, 0, -1, NULL, NULL }; static struct file_info cpfile = { NANDFS_CPFILE_INO, NULL, 0, 0, -1, NULL, NULL }; static struct file_info datfile = { NANDFS_DAT_INO, NULL, 0, 0, -1, NULL, NULL }; struct nandfs_block { LIST_ENTRY(nandfs_block) block_link; uint32_t number; uint64_t offset; void *data; }; static LIST_HEAD(, nandfs_block) block_head = LIST_HEAD_INITIALIZER(&block_head); /* Storage geometry */ static off_t mediasize; static ssize_t sectorsize; static uint64_t nsegments; static uint64_t erasesize; static uint64_t segsize; static struct nandfs_fsdata fsdata; static struct nandfs_super_block super_block; static int is_nand; /* Nandfs parameters */ static size_t blocksize = NANDFS_DEF_BLOCKSIZE; static long blocks_per_segment; static long rsv_segment_percent = 5; static time_t nandfs_time; static uint32_t bad_segments_count = 0; static uint32_t *bad_segments = NULL; static uint8_t fsdata_blocks_state[NANDFS_NFSAREAS]; static u_char *volumelabel = NULL; static struct nandfs_super_root *sr; static uint32_t nuserfiles; static uint32_t seg_nblocks; static uint32_t seg_endblock; #define SIZE_TO_BLOCK(size) (((size) + (blocksize - 1)) / blocksize) static uint32_t nandfs_first_block(void) { uint32_t i, first_free, start_bad_segments = 0; for (i = 0; i < bad_segments_count; i++) { if (i == bad_segments[i]) start_bad_segments++; else break; } first_free = SIZE_TO_BLOCK(NANDFS_DATA_OFFSET_BYTES(erasesize) + (start_bad_segments * segsize)); if (first_free < (uint32_t)blocks_per_segment) return (blocks_per_segment); else return (first_free); } static void usage(void) { fprintf(stderr, "usage: newfs_nandfs [ -options ] device\n" "where the options are:\n" "\t-b block-size\n" "\t-B blocks-per-segment\n" "\t-L volume label\n" "\t-m reserved-segments-percentage\n"); exit(1); } static int nandfs_log2(unsigned n) { unsigned count; /* * N.B. this function will return 0 if supplied 0. */ for (count = 0; n/2; count++) n /= 2; return count; } /* from NetBSD's src/sys/net/if_ethersubr.c */ static uint32_t crc32_le(uint32_t crc, const uint8_t *buf, size_t len) { static const uint32_t crctab[] = { 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; size_t i; crc = crc ^ ~0U; for (i = 0; i < len; i++) { crc ^= buf[i]; crc = (crc >> 4) ^ crctab[crc & 0xf]; crc = (crc >> 4) ^ crctab[crc & 0xf]; } return (crc ^ ~0U); } static void * get_block(uint32_t block_nr, uint64_t offset) { struct nandfs_block *block, *new_block; LIST_FOREACH(block, &block_head, block_link) { if (block->number == block_nr) return block->data; } debug("allocating block %x\n", block_nr); new_block = malloc(sizeof(*block)); if (!new_block) err(1, "cannot allocate block"); new_block->number = block_nr; new_block->offset = offset; new_block->data = malloc(blocksize); if (!new_block->data) err(1, "cannot allocate block data"); memset(new_block->data, 0, blocksize); LIST_INSERT_HEAD(&block_head, new_block, block_link); return (new_block->data); } static int nandfs_seg_usage_blk_offset(uint64_t seg, uint64_t *blk, uint64_t *offset) { uint64_t off; uint16_t seg_size; seg_size = sizeof(struct nandfs_segment_usage); off = roundup(sizeof(struct nandfs_sufile_header), seg_size); off += (seg * seg_size); *blk = off / blocksize; *offset = (off % blocksize) / seg_size; return (0); } static uint32_t segment_size(void) { u_int size; size = sizeof(struct nandfs_segment_summary ); size += seg_nblocks * sizeof(struct nandfs_binfo_v); if (size > blocksize) err(1, "segsum info bigger that blocksize"); return (size); } static void prepare_blockgrouped_file(uint32_t block) { struct nandfs_block_group_desc *desc; uint32_t i, entries; desc = (struct nandfs_block_group_desc *)get_block(block, 0); entries = blocksize / sizeof(struct nandfs_block_group_desc); for (i = 0; i < entries; i++) desc[i].bg_nfrees = blocksize * 8; } static void alloc_blockgrouped_file(uint32_t block, uint32_t entry) { struct nandfs_block_group_desc *desc; uint32_t desc_nr; uint32_t *bitmap; desc = (struct nandfs_block_group_desc *)get_block(block, 0); bitmap = (uint32_t *)get_block(block + 1, 1); bitmap += (entry >> 5); if (*bitmap & (1 << (entry % 32))) { printf("nandfs: blockgrouped entry %d already allocated\n", entry); } *bitmap |= (1 << (entry % 32)); desc_nr = entry / (blocksize * 8); desc[desc_nr].bg_nfrees--; } static uint64_t count_su_blocks(void) { uint64_t maxblk, blk, offset, i; maxblk = blk = 0; for (i = 0; i < bad_segments_count; i++) { nandfs_seg_usage_blk_offset(bad_segments[i], &blk, &offset); debug("bad segment at block:%jx off: %jx", blk, offset); if (blk > maxblk) maxblk = blk; } debug("bad segment needs %#jx", blk); if (blk >= NDADDR) { printf("nandfs: file too big (%jd > %d)\n", blk, NDADDR); exit(2); } sufile.size = (blk + 1) * blocksize; return (blk + 1); } static void count_seg_blocks(void) { uint32_t i; for (i = 0; i < nuserfiles; i++) if (user_files[i].nblocks) { seg_nblocks += user_files[i].nblocks; user_files[i].blocks = malloc(user_files[i].nblocks * sizeof(uint32_t)); } ifile.nblocks = 2 + SIZE_TO_BLOCK(sizeof(struct nandfs_inode) * (NANDFS_USER_INO + 1)); ifile.blocks = malloc(ifile.nblocks * sizeof(uint32_t)); seg_nblocks += ifile.nblocks; cpfile.nblocks = SIZE_TO_BLOCK((NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET + 1) * sizeof(struct nandfs_checkpoint)); cpfile.blocks = malloc(cpfile.nblocks * sizeof(uint32_t)); seg_nblocks += cpfile.nblocks; if (!bad_segments) { sufile.nblocks = SIZE_TO_BLOCK((NANDFS_SUFILE_FIRST_SEGMENT_USAGE_OFFSET + 1) * sizeof(struct nandfs_segment_usage)); } else { debug("bad blocks found: extra space for sufile"); sufile.nblocks = count_su_blocks(); } sufile.blocks = malloc(sufile.nblocks * sizeof(uint32_t)); seg_nblocks += sufile.nblocks; datfile.nblocks = 2 + SIZE_TO_BLOCK((seg_nblocks) * sizeof(struct nandfs_dat_entry)); datfile.blocks = malloc(datfile.nblocks * sizeof(uint32_t)); seg_nblocks += datfile.nblocks; } static void assign_file_blocks(uint64_t start_block) { uint32_t i, j; for (i = 0; i < nuserfiles; i++) for (j = 0; j < user_files[i].nblocks; j++) { debug("user file %d at block %d at %#jx", i, j, (uintmax_t)start_block); user_files[i].blocks[j] = start_block++; } for (j = 0; j < ifile.nblocks; j++) { debug("ifile block %d at %#jx", j, (uintmax_t)start_block); ifile.blocks[j] = start_block++; } for (j = 0; j < cpfile.nblocks; j++) { debug("cpfile block %d at %#jx", j, (uintmax_t)start_block); cpfile.blocks[j] = start_block++; } for (j = 0; j < sufile.nblocks; j++) { debug("sufile block %d at %#jx", j, (uintmax_t)start_block); sufile.blocks[j] = start_block++; } for (j = 0; j < datfile.nblocks; j++) { debug("datfile block %d at %#jx", j, (uintmax_t)start_block); datfile.blocks[j] = start_block++; } /* add one for superroot */ debug("sr at block %#jx", (uintmax_t)start_block); sr = (struct nandfs_super_root *)get_block(start_block++, 0); seg_endblock = start_block; } static void save_datfile(void) { prepare_blockgrouped_file(datfile.blocks[0]); } static uint64_t update_datfile(uint64_t block) { struct nandfs_dat_entry *dat; static uint64_t vblock = 0; uint64_t allocated, i, off; if (vblock == 0) { alloc_blockgrouped_file(datfile.blocks[0], vblock); vblock++; } allocated = vblock; i = vblock / (blocksize / sizeof(*dat)); off = vblock % (blocksize / sizeof(*dat)); vblock++; dat = (struct nandfs_dat_entry *)get_block(datfile.blocks[2 + i], 2 + i); alloc_blockgrouped_file(datfile.blocks[0], allocated); dat[off].de_blocknr = block; dat[off].de_start = NANDFS_FIRST_CNO; dat[off].de_end = UINTMAX_MAX; return (allocated); } static union nandfs_binfo * update_block_info(union nandfs_binfo *binfo, struct file_info *file) { nandfs_daddr_t vblock; uint32_t i; for (i = 0; i < file->nblocks; i++) { debug("%s: blk %x", __func__, i); if (file->ino != NANDFS_DAT_INO) { vblock = update_datfile(file->blocks[i]); binfo->bi_v.bi_vblocknr = vblock; binfo->bi_v.bi_blkoff = i; binfo->bi_v.bi_ino = file->ino; file->inode->i_db[i] = vblock; } else { binfo->bi_dat.bi_blkoff = i; binfo->bi_dat.bi_ino = file->ino; file->inode->i_db[i] = datfile.blocks[i]; } binfo++; } return (binfo); } static void save_segsum(struct nandfs_segment_summary *ss) { union nandfs_binfo *binfo; struct nandfs_block *block; uint32_t sum_bytes, i; uint8_t crc_data, crc_skip; sum_bytes = segment_size(); ss->ss_magic = NANDFS_SEGSUM_MAGIC; ss->ss_bytes = sizeof(struct nandfs_segment_summary); ss->ss_flags = NANDFS_SS_LOGBGN | NANDFS_SS_LOGEND | NANDFS_SS_SR; ss->ss_seq = 1; ss->ss_create = nandfs_time; ss->ss_next = nandfs_first_block() + blocks_per_segment; /* nblocks = segment blocks + segsum block + superroot */ ss->ss_nblocks = seg_nblocks + 2; ss->ss_nbinfos = seg_nblocks; ss->ss_sumbytes = sum_bytes; crc_skip = sizeof(ss->ss_datasum) + sizeof(ss->ss_sumsum); ss->ss_sumsum = crc32_le(0, (uint8_t *)ss + crc_skip, sum_bytes - crc_skip); crc_data = 0; binfo = (union nandfs_binfo *)(ss + 1); for (i = 0; i < nuserfiles; i++) { if (user_files[i].nblocks) binfo = update_block_info(binfo, &user_files[i]); } binfo = update_block_info(binfo, &ifile); binfo = update_block_info(binfo, &cpfile); binfo = update_block_info(binfo, &sufile); update_block_info(binfo, &datfile); /* save superroot crc */ crc_skip = sizeof(sr->sr_sum); sr->sr_sum = crc32_le(0, (uint8_t *)sr + crc_skip, NANDFS_SR_BYTES - crc_skip); /* segment checksup */ crc_skip = sizeof(ss->ss_datasum); LIST_FOREACH(block, &block_head, block_link) { if (block->number < NANDFS_FIRST_BLOCK) continue; if (block->number == NANDFS_FIRST_BLOCK) crc_data = crc32_le(0, (uint8_t *)block->data + crc_skip, blocksize - crc_skip); else crc_data = crc32_le(crc_data, (uint8_t *)block->data, blocksize); } ss->ss_datasum = crc_data; } static void create_fsdata(void) { memset(&fsdata, 0, sizeof(struct nandfs_fsdata)); fsdata.f_magic = NANDFS_FSDATA_MAGIC; fsdata.f_nsegments = nsegments; fsdata.f_erasesize = erasesize; fsdata.f_first_data_block = NANDFS_FIRST_BLOCK; fsdata.f_blocks_per_segment = blocks_per_segment; fsdata.f_r_segments_percentage = rsv_segment_percent; fsdata.f_rev_level = NANDFS_CURRENT_REV; fsdata.f_sbbytes = NANDFS_SB_BYTES; fsdata.f_bytes = NANDFS_FSDATA_CRC_BYTES; fsdata.f_ctime = nandfs_time; fsdata.f_log_block_size = nandfs_log2(blocksize) - 10; fsdata.f_errors = 1; fsdata.f_inode_size = sizeof(struct nandfs_inode); fsdata.f_dat_entry_size = sizeof(struct nandfs_dat_entry); fsdata.f_checkpoint_size = sizeof(struct nandfs_checkpoint); fsdata.f_segment_usage_size = sizeof(struct nandfs_segment_usage); uuidgen(&fsdata.f_uuid, 1); if (volumelabel) memcpy(fsdata.f_volume_name, volumelabel, 16); fsdata.f_sum = crc32_le(0, (const uint8_t *)&fsdata, NANDFS_FSDATA_CRC_BYTES); } static void save_fsdata(void *data) { memcpy(data, &fsdata, sizeof(fsdata)); } static void create_super_block(void) { memset(&super_block, 0, sizeof(struct nandfs_super_block)); super_block.s_magic = NANDFS_SUPER_MAGIC; super_block.s_last_cno = NANDFS_FIRST_CNO; super_block.s_last_pseg = NANDFS_FIRST_BLOCK; super_block.s_last_seq = 1; super_block.s_free_blocks_count = (nsegments - bad_segments_count) * blocks_per_segment; super_block.s_mtime = 0; super_block.s_wtime = nandfs_time; super_block.s_state = NANDFS_VALID_FS; super_block.s_sum = crc32_le(0, (const uint8_t *)&super_block, NANDFS_SB_BYTES); } static void save_super_block(void *data) { memcpy(data, &super_block, sizeof(super_block)); } static void save_super_root(void) { sr->sr_bytes = NANDFS_SR_BYTES; sr->sr_flags = 0; sr->sr_nongc_ctime = nandfs_time; datfile.inode = &sr->sr_dat; cpfile.inode = &sr->sr_cpfile; sufile.inode = &sr->sr_sufile; } static struct nandfs_dir_entry * add_de(void *block, struct nandfs_dir_entry *de, uint64_t ino, const char *name, uint8_t type) { uint16_t reclen; /* modify last de */ de->rec_len = NANDFS_DIR_REC_LEN(de->name_len); de = (void *)((uint8_t *)de + de->rec_len); reclen = blocksize - ((uintptr_t)de - (uintptr_t)block); if (reclen < NANDFS_DIR_REC_LEN(strlen(name))) { printf("nandfs: too many dir entries for one block\n"); return (NULL); } de->inode = ino; de->rec_len = reclen; de->name_len = strlen(name); de->file_type = type; memset(de->name, 0, (strlen(name) + NANDFS_DIR_PAD - 1) & ~NANDFS_DIR_ROUND); memcpy(de->name, name, strlen(name)); return (de); } static struct nandfs_dir_entry * make_dir(void *block, uint64_t ino, uint64_t parent_ino) { struct nandfs_dir_entry *de = (struct nandfs_dir_entry *)block; /* create '..' entry */ de->inode = parent_ino; de->rec_len = NANDFS_DIR_REC_LEN(2); de->name_len = 2; de->file_type = DT_DIR; memset(de->name, 0, NANDFS_DIR_NAME_LEN(2)); memcpy(de->name, "..", 2); /* create '.' entry */ de = (void *)((uint8_t *)block + NANDFS_DIR_REC_LEN(2)); de->inode = ino; de->rec_len = blocksize - NANDFS_DIR_REC_LEN(2); de->name_len = 1; de->file_type = DT_DIR; memset(de->name, 0, NANDFS_DIR_NAME_LEN(1)); memcpy(de->name, ".", 1); return (de); } static void save_root_dir(void) { struct file_info *root = &user_files[0]; struct nandfs_dir_entry *de; uint32_t i; void *block; block = get_block(root->blocks[0], 0); de = make_dir(block, root->ino, root->ino); for (i = 1; i < nuserfiles; i++) de = add_de(block, de, user_files[i].ino, user_files[i].name, IFTODT(user_files[i].mode)); root->size = ((uintptr_t)de - (uintptr_t)block) + NANDFS_DIR_REC_LEN(de->name_len); } static void save_sufile(void) { struct nandfs_sufile_header *header; struct nandfs_segment_usage *su; uint64_t blk, i, off; void *block; int start; /* * At the beginning just zero-out everything */ for (i = 0; i < sufile.nblocks; i++) get_block(sufile.blocks[i], 0); start = 0; block = get_block(sufile.blocks[start], 0); header = (struct nandfs_sufile_header *)block; header->sh_ncleansegs = nsegments - bad_segments_count - 1; header->sh_ndirtysegs = 1; header->sh_last_alloc = 1; su = (struct nandfs_segment_usage *)header; off = NANDFS_SUFILE_FIRST_SEGMENT_USAGE_OFFSET; /* Allocate data segment */ su[off].su_lastmod = nandfs_time; /* nblocks = segment blocks + segsum block + superroot */ su[off].su_nblocks = seg_nblocks + 2; su[off].su_flags = NANDFS_SEGMENT_USAGE_DIRTY; off++; /* Allocate next segment */ su[off].su_lastmod = nandfs_time; su[off].su_nblocks = 0; su[off].su_flags = NANDFS_SEGMENT_USAGE_DIRTY; for (i = 0; i < bad_segments_count; i++) { nandfs_seg_usage_blk_offset(bad_segments[i], &blk, &off); debug("storing bad_segments[%jd]=%x at %jx off %jx\n", i, bad_segments[i], blk, off); block = get_block(sufile.blocks[blk], off * sizeof(struct nandfs_segment_usage *)); su = (struct nandfs_segment_usage *)block; su[off].su_lastmod = nandfs_time; su[off].su_nblocks = 0; su[off].su_flags = NANDFS_SEGMENT_USAGE_ERROR; } } static void save_cpfile(void) { struct nandfs_cpfile_header *header; struct nandfs_checkpoint *cp, *initial_cp; int i, entries = blocksize / sizeof(struct nandfs_checkpoint); uint64_t cno; header = (struct nandfs_cpfile_header *)get_block(cpfile.blocks[0], 0); header->ch_ncheckpoints = 1; header->ch_nsnapshots = 0; cp = (struct nandfs_checkpoint *)header; /* fill first checkpoint data*/ initial_cp = &cp[NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET]; initial_cp->cp_flags = 0; initial_cp->cp_checkpoints_count = 0; initial_cp->cp_cno = NANDFS_FIRST_CNO; initial_cp->cp_create = nandfs_time; initial_cp->cp_nblk_inc = seg_endblock - 1; initial_cp->cp_blocks_count = seg_nblocks; memset(&initial_cp->cp_snapshot_list, 0, sizeof(struct nandfs_snapshot_list)); ifile.inode = &initial_cp->cp_ifile_inode; /* mark rest of cp as invalid */ cno = NANDFS_FIRST_CNO + 1; i = NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET + 1; for (; i < entries; i++) { cp[i].cp_cno = cno++; cp[i].cp_flags = NANDFS_CHECKPOINT_INVALID; } } static void init_inode(struct nandfs_inode *inode, struct file_info *file) { inode->i_blocks = file->nblocks; inode->i_ctime = nandfs_time; inode->i_mtime = nandfs_time; inode->i_mode = file->mode & 0xffff; inode->i_links_count = 1; if (file->size > 0) inode->i_size = file->size; else inode->i_size = 0; if (file->ino == NANDFS_USER_INO) inode->i_flags = SF_NOUNLINK|UF_NOUNLINK; else inode->i_flags = 0; } static void save_ifile(void) { struct nandfs_inode *inode; struct file_info *file; uint64_t ino, blk, off; uint32_t i; prepare_blockgrouped_file(ifile.blocks[0]); for (i = 0; i <= NANDFS_USER_INO; i++) alloc_blockgrouped_file(ifile.blocks[0], i); for (i = 0; i < nuserfiles; i++) { file = &user_files[i]; ino = file->ino; blk = ino / (blocksize / sizeof(*inode)); off = ino % (blocksize / sizeof(*inode)); inode = (struct nandfs_inode *)get_block(ifile.blocks[2 + blk], 2 + blk); file->inode = &inode[off]; init_inode(file->inode, file); } init_inode(ifile.inode, &ifile); init_inode(cpfile.inode, &cpfile); init_inode(sufile.inode, &sufile); init_inode(datfile.inode, &datfile); } static int create_fs(void) { uint64_t start_block; uint32_t segsum_size; char *data; int i; nuserfiles = (sizeof(user_files) / sizeof(user_files[0])); /* Count and assign blocks */ count_seg_blocks(); segsum_size = segment_size(); start_block = NANDFS_FIRST_BLOCK + SIZE_TO_BLOCK(segsum_size); assign_file_blocks(start_block); /* Create super root structure */ save_super_root(); /* Create root directory */ save_root_dir(); /* Fill in file contents */ save_sufile(); save_cpfile(); save_ifile(); save_datfile(); /* Save fsdata and superblocks */ create_fsdata(); create_super_block(); for (i = 0; i < NANDFS_NFSAREAS; i++) { if (fsdata_blocks_state[i] != NANDFS_BLOCK_GOOD) continue; data = get_block((i * erasesize)/blocksize, 0); save_fsdata(data); data = get_block((i * erasesize + NANDFS_SBLOCK_OFFSET_BYTES) / blocksize, 0); if (blocksize > NANDFS_SBLOCK_OFFSET_BYTES) data += NANDFS_SBLOCK_OFFSET_BYTES; save_super_block(data); memset(data + sizeof(struct nandfs_super_block), 0xff, (blocksize - sizeof(struct nandfs_super_block) - NANDFS_SBLOCK_OFFSET_BYTES)); } /* Save segment summary and CRCs */ save_segsum(get_block(NANDFS_FIRST_BLOCK, 0)); return (0); } static void write_fs(int fda) { struct nandfs_block *block; char *data; u_int ret; /* Overwrite next block with ff if not nand device */ if (!is_nand) { data = get_block(seg_endblock, 0); memset(data, 0xff, blocksize); } LIST_FOREACH(block, &block_head, block_link) { lseek(fda, block->number * blocksize, SEEK_SET); ret = write(fda, block->data, blocksize); if (ret != blocksize) err(1, "cannot write filesystem data"); } } static void check_parameters(void) { int i; /* check blocksize */ if ((blocksize < NANDFS_MIN_BLOCKSIZE) || (blocksize > MAXBSIZE) || ((blocksize - 1) & blocksize)) { errx(1, "Bad blocksize (%zu). Must be in range [%u-%u] " "and a power of two.", blocksize, NANDFS_MIN_BLOCKSIZE, MAXBSIZE); } /* check blocks per segments */ if ((blocks_per_segment < NANDFS_SEG_MIN_BLOCKS) || ((blocksize - 1) & blocksize)) errx(1, "Bad blocks per segment (%lu). Must be greater than " "%u and a power of two.", blocks_per_segment, NANDFS_SEG_MIN_BLOCKS); /* check reserved segment percentage */ if ((rsv_segment_percent < 1) || (rsv_segment_percent > 99)) errx(1, "Bad reserved segment percentage. " "Must in range 1..99."); /* check volume label */ i = 0; if (volumelabel) { while (isalnum(volumelabel[++i])) ; if (volumelabel[i] != '\0') { errx(1, "bad volume label. " "Valid characters are alphanumerics."); } if (strlen(volumelabel) >= 16) errx(1, "Bad volume label. Length is longer than %d.", 16); } nandfs_time = time(NULL); } static void print_parameters(void) { printf("filesystem parameters:\n"); printf("blocksize: %#zx sectorsize: %#zx\n", blocksize, sectorsize); printf("erasesize: %#jx mediasize: %#jx\n", erasesize, mediasize); printf("segment size: %#jx blocks per segment: %#x\n", segsize, (uint32_t)blocks_per_segment); } /* * Exit with error if file system is mounted. */ static void check_mounted(const char *fname, mode_t mode) { struct statfs *mp; const char *s1, *s2; size_t len; int n, r; if (!(n = getmntinfo(&mp, MNT_NOWAIT))) err(1, "getmntinfo"); len = strlen(_PATH_DEV); s1 = fname; if (!strncmp(s1, _PATH_DEV, len)) s1 += len; r = S_ISCHR(mode) && s1 != fname && *s1 == 'r'; for (; n--; mp++) { s2 = mp->f_mntfromname; if (!strncmp(s2, _PATH_DEV, len)) s2 += len; if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) || !strcmp(s1, s2)) errx(1, "%s is mounted on %s", fname, mp->f_mntonname); } } static void calculate_geometry(int fd) { struct chip_param_io chip_params; char ident[DISK_IDENT_SIZE]; char medianame[MAXPATHLEN]; /* Check storage type */ g_get_ident(fd, ident, DISK_IDENT_SIZE); g_get_name(ident, medianame, MAXPATHLEN); debug("device name: %s", medianame); is_nand = (strstr(medianame, "gnand") != NULL); debug("is_nand = %d", is_nand); sectorsize = g_sectorsize(fd); debug("sectorsize: %#zx", sectorsize); /* Get storage size */ mediasize = g_mediasize(fd); debug("mediasize: %#jx", mediasize); /* Get storage erase unit size */ if (!is_nand) erasesize = NANDFS_DEF_ERASESIZE; else if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) != -1) erasesize = chip_params.page_size * chip_params.pages_per_block; else errx(1, "Cannot ioctl(NAND_IO_GET_CHIP_PARAM)"); debug("erasesize: %#jx", (uintmax_t)erasesize); if (blocks_per_segment == 0) { if (erasesize >= NANDFS_MIN_SEGSIZE) blocks_per_segment = erasesize / blocksize; else blocks_per_segment = NANDFS_MIN_SEGSIZE / blocksize; } /* Calculate number of segments */ segsize = blocksize * blocks_per_segment; nsegments = ((mediasize - NANDFS_NFSAREAS * erasesize) / segsize) - 2; debug("segsize: %#jx", segsize); debug("nsegments: %#jx", nsegments); } static void erase_device(int fd) { int rest, failed; uint64_t i, nblocks; off_t offset; failed = 0; for (i = 0; i < NANDFS_NFSAREAS; i++) { debug("Deleting %jx\n", i * erasesize); if (g_delete(fd, i * erasesize, erasesize)) { printf("cannot delete %jx\n", i * erasesize); fsdata_blocks_state[i] = NANDFS_BLOCK_BAD; failed++; } else fsdata_blocks_state[i] = NANDFS_BLOCK_GOOD; } if (failed == NANDFS_NFSAREAS) { printf("%d first blocks not usable. Unable to create " "filesystem.\n", failed); exit(1); } for (i = 0; i < nsegments; i++) { offset = NANDFS_NFSAREAS * erasesize + i * segsize; if (g_delete(fd, offset, segsize)) { printf("cannot delete segment %jx (offset %jd)\n", i, offset); bad_segments_count++; bad_segments = realloc(bad_segments, bad_segments_count * sizeof(uint32_t)); bad_segments[bad_segments_count - 1] = i; } } if (bad_segments_count == nsegments) { printf("no valid segments\n"); exit(1); } /* Delete remaining blocks at the end of device */ rest = mediasize % segsize; nblocks = rest / erasesize; for (i = 0; i < nblocks; i++) { offset = (segsize * nsegments) + (i * erasesize); if (g_delete(fd, offset, erasesize)) { printf("cannot delete space after last segment " "- probably a bad block\n"); } } } static void erase_initial(int fd) { char buf[512]; u_int i; memset(buf, 0xff, sizeof(buf)); lseek(fd, 0, SEEK_SET); for (i = 0; i < NANDFS_NFSAREAS * erasesize; i += sizeof(buf)) write(fd, buf, sizeof(buf)); } static void create_nandfs(int fd) { create_fs(); write_fs(fd); } static void print_summary(void) { - printf("filesystem created succesfully\n"); + printf("filesystem was created successfully\n"); printf("total segments: %#jx valid segments: %#jx\n", nsegments, nsegments - bad_segments_count); printf("total space: %ju MB free: %ju MB\n", (nsegments * blocks_per_segment * blocksize) / (1024 * 1024), ((nsegments - bad_segments_count) * blocks_per_segment * blocksize) / (1024 * 1024)); } int main(int argc, char *argv[]) { struct stat sb; char buf[MAXPATHLEN]; const char opts[] = "b:B:L:m:"; const char *fname; int ch, fd; while ((ch = getopt(argc, argv, opts)) != -1) { switch (ch) { case 'b': blocksize = strtol(optarg, (char **)NULL, 10); if (blocksize == 0) usage(); break; case 'B': blocks_per_segment = strtol(optarg, (char **)NULL, 10); if (blocks_per_segment == 0) usage(); break; case 'L': volumelabel = optarg; break; case 'm': rsv_segment_percent = strtol(optarg, (char **)NULL, 10); if (rsv_segment_percent == 0) usage(); break; default: usage(); } } argc -= optind; argv += optind; if (argc < 1 || argc > 2) usage(); /* construct proper device path */ fname = *argv++; if (!strchr(fname, '/')) { snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname); if (!(fname = strdup(buf))) err(1, NULL); } fd = g_open(fname, 1); if (fd == -1) err(1, "Cannot open %s", fname); if (fstat(fd, &sb) == -1) err(1, "Cannot stat %s", fname); if (!S_ISCHR(sb.st_mode)) warnx("%s is not a character device", fname); check_mounted(fname, sb.st_mode); calculate_geometry(fd); check_parameters(); print_parameters(); if (is_nand) erase_device(fd); else erase_initial(fd); create_nandfs(fd); print_summary(); g_close(fd); return (0); } Index: head/sbin/rcorder/rcorder.c =================================================================== --- head/sbin/rcorder/rcorder.c (revision 289676) +++ head/sbin/rcorder/rcorder.c (revision 289677) @@ -1,806 +1,806 @@ # if 0 /* $NetBSD: rcorder.c,v 1.7 2000/08/04 07:33:55 enami Exp $ */ #endif /* * Copyright (c) 1998, 1999 Matthew R. Green * All rights reserved. * Copyright (c) 1998 * Perry E. Metzger. 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 for the NetBSD Project * by Perry E. Metzger. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 "ealloc.h" #include "sprite.h" #include "hash.h" #ifdef DEBUG static int debug = 0; # define DPRINTF(args) if (debug) { fflush(stdout); fprintf args; } #else # define DPRINTF(args) #endif #define REQUIRE_STR "# REQUIRE:" #define REQUIRE_LEN (sizeof(REQUIRE_STR) - 1) #define REQUIRES_STR "# REQUIRES:" #define REQUIRES_LEN (sizeof(REQUIRES_STR) - 1) #define PROVIDE_STR "# PROVIDE:" #define PROVIDE_LEN (sizeof(PROVIDE_STR) - 1) #define PROVIDES_STR "# PROVIDES:" #define PROVIDES_LEN (sizeof(PROVIDES_STR) - 1) #define BEFORE_STR "# BEFORE:" #define BEFORE_LEN (sizeof(BEFORE_STR) - 1) #define KEYWORD_STR "# KEYWORD:" #define KEYWORD_LEN (sizeof(KEYWORD_STR) - 1) #define KEYWORDS_STR "# KEYWORDS:" #define KEYWORDS_LEN (sizeof(KEYWORDS_STR) - 1) static int exit_code; static int file_count; static char **file_list; typedef int bool; #define TRUE 1 #define FALSE 0 typedef bool flag; #define SET TRUE #define RESET FALSE static Hash_Table provide_hash_s, *provide_hash; typedef struct provnode provnode; typedef struct filenode filenode; typedef struct f_provnode f_provnode; typedef struct f_reqnode f_reqnode; typedef struct strnodelist strnodelist; struct provnode { flag head; flag in_progress; filenode *fnode; provnode *next, *last; }; struct f_provnode { provnode *pnode; f_provnode *next; }; struct f_reqnode { Hash_Entry *entry; f_reqnode *next; }; struct strnodelist { filenode *node; strnodelist *next; char s[1]; }; struct filenode { char *filename; flag in_progress; filenode *next, *last; f_reqnode *req_list; f_provnode *prov_list; strnodelist *keyword_list; }; static filenode fn_head_s, *fn_head; static strnodelist *bl_list; static strnodelist *keep_list; static strnodelist *skip_list; static void do_file(filenode *fnode); static void strnode_add(strnodelist **, char *, filenode *); static int skip_ok(filenode *fnode); static int keep_ok(filenode *fnode); static void satisfy_req(f_reqnode *rnode, char *filename); static void crunch_file(char *); static void parse_require(filenode *, char *); static void parse_provide(filenode *, char *); static void parse_before(filenode *, char *); static void parse_keywords(filenode *, char *); static filenode *filenode_new(char *); static void add_require(filenode *, char *); static void add_provide(filenode *, char *); static void add_before(filenode *, char *); static void add_keyword(filenode *, char *); static void insert_before(void); static Hash_Entry *make_fake_provision(filenode *); static void crunch_all_files(void); static void initialize(void); static void generate_ordering(void); int main(int argc, char *argv[]) { int ch; while ((ch = getopt(argc, argv, "dk:s:")) != -1) switch (ch) { case 'd': #ifdef DEBUG debug = 1; #else warnx("debugging not compiled in, -d ignored"); #endif break; case 'k': strnode_add(&keep_list, optarg, 0); break; case 's': strnode_add(&skip_list, optarg, 0); break; default: /* XXX should crunch it? */ break; } argc -= optind; argv += optind; file_count = argc; file_list = argv; DPRINTF((stderr, "parse_args\n")); initialize(); DPRINTF((stderr, "initialize\n")); crunch_all_files(); DPRINTF((stderr, "crunch_all_files\n")); generate_ordering(); DPRINTF((stderr, "generate_ordering\n")); exit(exit_code); } /* * initialise various variables. */ static void initialize(void) { fn_head = &fn_head_s; provide_hash = &provide_hash_s; Hash_InitTable(provide_hash, file_count); } /* generic function to insert a new strnodelist element */ static void strnode_add(strnodelist **listp, char *s, filenode *fnode) { strnodelist *ent; ent = emalloc(sizeof *ent + strlen(s)); ent->node = fnode; strcpy(ent->s, s); ent->next = *listp; *listp = ent; } /* * below are the functions that deal with creating the lists - * from the filename's given and the dependancies and provisions + * from the filename's given dependencies and provisions * in each of these files. no ordering or checking is done here. */ /* * we have a new filename, create a new filenode structure. * fill in the bits, and put it in the filenode linked list */ static filenode * filenode_new(char *filename) { filenode *temp; temp = emalloc(sizeof(*temp)); memset(temp, 0, sizeof(*temp)); temp->filename = estrdup(filename); temp->req_list = NULL; temp->prov_list = NULL; temp->keyword_list = NULL; temp->in_progress = RESET; /* * link the filenode into the list of filenodes. * note that the double linking means we can delete a * filenode without searching for where it belongs. */ temp->next = fn_head->next; if (temp->next != NULL) temp->next->last = temp; temp->last = fn_head; fn_head->next = temp; return (temp); } /* * add a requirement to a filenode. */ static void add_require(filenode *fnode, char *s) { Hash_Entry *entry; f_reqnode *rnode; int new; entry = Hash_CreateEntry(provide_hash, s, &new); if (new) Hash_SetValue(entry, NULL); rnode = emalloc(sizeof(*rnode)); rnode->entry = entry; rnode->next = fnode->req_list; fnode->req_list = rnode; } /* * add a provision to a filenode. if this provision doesn't * have a head node, create one here. */ static void add_provide(filenode *fnode, char *s) { Hash_Entry *entry; f_provnode *f_pnode; provnode *pnode, *head; int new; entry = Hash_CreateEntry(provide_hash, s, &new); head = Hash_GetValue(entry); /* create a head node if necessary. */ if (head == NULL) { head = emalloc(sizeof(*head)); head->head = SET; head->in_progress = RESET; head->fnode = NULL; head->last = head->next = NULL; Hash_SetValue(entry, head); } #if 0 /* * Don't warn about this. We want to be able to support * scripts that do two complex things: * * - Two independent scripts which both provide the * same thing. Both scripts must be executed in * any order to meet the barrier. An example: * * Script 1: * * PROVIDE: mail * REQUIRE: LOGIN * * Script 2: * * PROVIDE: mail * REQUIRE: LOGIN * * - Two interdependent scripts which both provide the * same thing. Both scripts must be executed in * graph order to meet the barrier. An example: * * Script 1: * * PROVIDE: nameservice dnscache * REQUIRE: SERVERS * * Script 2: * * PROVIDE: nameservice nscd * REQUIRE: dnscache */ else if (new == 0) { warnx("file `%s' provides `%s'.", fnode->filename, s); warnx("\tpreviously seen in `%s'.", head->next->fnode->filename); } #endif pnode = emalloc(sizeof(*pnode)); pnode->head = RESET; pnode->in_progress = RESET; pnode->fnode = fnode; pnode->next = head->next; pnode->last = head; head->next = pnode; if (pnode->next != NULL) pnode->next->last = pnode; f_pnode = emalloc(sizeof(*f_pnode)); f_pnode->pnode = pnode; f_pnode->next = fnode->prov_list; fnode->prov_list = f_pnode; } /* * put the BEFORE: lines to a list and handle them later. */ static void add_before(filenode *fnode, char *s) { strnodelist *bf_ent; bf_ent = emalloc(sizeof *bf_ent + strlen(s)); bf_ent->node = fnode; strcpy(bf_ent->s, s); bf_ent->next = bl_list; bl_list = bf_ent; } /* * add a key to a filenode. */ static void add_keyword(filenode *fnode, char *s) { strnode_add(&fnode->keyword_list, s, fnode); } /* * loop over the rest of a REQUIRE line, giving each word to * add_require() to do the real work. */ static void parse_require(filenode *node, char *buffer) { char *s; while ((s = strsep(&buffer, " \t\n")) != NULL) if (*s != '\0') add_require(node, s); } /* * loop over the rest of a PROVIDE line, giving each word to * add_provide() to do the real work. */ static void parse_provide(filenode *node, char *buffer) { char *s; while ((s = strsep(&buffer, " \t\n")) != NULL) if (*s != '\0') add_provide(node, s); } /* * loop over the rest of a BEFORE line, giving each word to * add_before() to do the real work. */ static void parse_before(filenode *node, char *buffer) { char *s; while ((s = strsep(&buffer, " \t\n")) != NULL) if (*s != '\0') add_before(node, s); } /* * loop over the rest of a KEYWORD line, giving each word to * add_keyword() to do the real work. */ static void parse_keywords(filenode *node, char *buffer) { char *s; while ((s = strsep(&buffer, " \t\n")) != NULL) if (*s != '\0') add_keyword(node, s); } /* * given a file name, create a filenode for it, read in lines looking * for provision and requirement lines, building the graphs as needed. */ static void crunch_file(char *filename) { FILE *fp; char *buf; int require_flag, provide_flag, before_flag, keywords_flag; enum { BEFORE_PARSING, PARSING, PARSING_DONE } state; filenode *node; char delims[3] = { '\\', '\\', '\0' }; struct stat st; if ((fp = fopen(filename, "r")) == NULL) { warn("could not open %s", filename); return; } if (fstat(fileno(fp), &st) == -1) { warn("could not stat %s", filename); fclose(fp); return; } if (!S_ISREG(st.st_mode)) { #if 0 warnx("%s is not a file", filename); #endif fclose(fp); return; } node = filenode_new(filename); /* * we don't care about length, line number, don't want # for comments, * and have no flags. */ for (state = BEFORE_PARSING; state != PARSING_DONE && (buf = fparseln(fp, NULL, NULL, delims, 0)) != NULL; free(buf)) { require_flag = provide_flag = before_flag = keywords_flag = 0; if (strncmp(REQUIRE_STR, buf, REQUIRE_LEN) == 0) require_flag = REQUIRE_LEN; else if (strncmp(REQUIRES_STR, buf, REQUIRES_LEN) == 0) require_flag = REQUIRES_LEN; else if (strncmp(PROVIDE_STR, buf, PROVIDE_LEN) == 0) provide_flag = PROVIDE_LEN; else if (strncmp(PROVIDES_STR, buf, PROVIDES_LEN) == 0) provide_flag = PROVIDES_LEN; else if (strncmp(BEFORE_STR, buf, BEFORE_LEN) == 0) before_flag = BEFORE_LEN; else if (strncmp(KEYWORD_STR, buf, KEYWORD_LEN) == 0) keywords_flag = KEYWORD_LEN; else if (strncmp(KEYWORDS_STR, buf, KEYWORDS_LEN) == 0) keywords_flag = KEYWORDS_LEN; else { if (state == PARSING) state = PARSING_DONE; continue; } state = PARSING; if (require_flag) parse_require(node, buf + require_flag); else if (provide_flag) parse_provide(node, buf + provide_flag); else if (before_flag) parse_before(node, buf + before_flag); else if (keywords_flag) parse_keywords(node, buf + keywords_flag); } fclose(fp); } static Hash_Entry * make_fake_provision(filenode *node) { Hash_Entry *entry; f_provnode *f_pnode; provnode *head, *pnode; static int i = 0; int new; char buffer[30]; do { snprintf(buffer, sizeof buffer, "fake_prov_%08d", i++); entry = Hash_CreateEntry(provide_hash, buffer, &new); } while (new == 0); head = emalloc(sizeof(*head)); head->head = SET; head->in_progress = RESET; head->fnode = NULL; head->last = head->next = NULL; Hash_SetValue(entry, head); pnode = emalloc(sizeof(*pnode)); pnode->head = RESET; pnode->in_progress = RESET; pnode->fnode = node; pnode->next = head->next; pnode->last = head; head->next = pnode; if (pnode->next != NULL) pnode->next->last = pnode; f_pnode = emalloc(sizeof(*f_pnode)); f_pnode->pnode = pnode; f_pnode->next = node->prov_list; node->prov_list = f_pnode; return (entry); } /* * go through the BEFORE list, inserting requirements into the graph(s) * as required. in the before list, for each entry B, we have a file F * and a string S. we create a "fake" provision (P) that F provides. * for each entry in the provision list for S, add a requirement to * that provisions filenode for P. */ static void insert_before(void) { Hash_Entry *entry, *fake_prov_entry; provnode *pnode; f_reqnode *rnode; strnodelist *bl; int new; while (bl_list != NULL) { bl = bl_list->next; fake_prov_entry = make_fake_provision(bl_list->node); entry = Hash_CreateEntry(provide_hash, bl_list->s, &new); if (new == 1) warnx("file `%s' is before unknown provision `%s'", bl_list->node->filename, bl_list->s); for (pnode = Hash_GetValue(entry); pnode; pnode = pnode->next) { if (pnode->head) continue; rnode = emalloc(sizeof(*rnode)); rnode->entry = fake_prov_entry; rnode->next = pnode->fnode->req_list; pnode->fnode->req_list = rnode; } free(bl_list); bl_list = bl; } } /* * loop over all the files calling crunch_file() on them to do the * real work. after we have built all the nodes, insert the BEFORE: * lines into graph(s). */ static void crunch_all_files(void) { int i; for (i = 0; i < file_count; i++) crunch_file(file_list[i]); insert_before(); } /* * below are the functions that traverse the graphs we have built * finding out the desired ordering, printing each file in turn. * if missing requirements, or cyclic graphs are detected, a * warning will be issued, and we will continue on.. */ /* * given a requirement node (in a filename) we attempt to satisfy it. * we do some sanity checking first, to ensure that we have providers, * aren't already satisfied and aren't already being satisfied (ie, * cyclic). if we pass all this, we loop over the provision list * calling do_file() (enter recursion) for each filenode in this * provision. */ static void satisfy_req(f_reqnode *rnode, char *filename) { Hash_Entry *entry; provnode *head; entry = rnode->entry; head = Hash_GetValue(entry); if (head == NULL) { warnx("requirement `%s' in file `%s' has no providers.", Hash_GetKey(entry), filename); exit_code = 1; return; } /* return if the requirement is already satisfied. */ if (head->next == NULL) return; /* * if list is marked as in progress, * print that there is a circular dependency on it and abort */ if (head->in_progress == SET) { warnx("Circular dependency on provision `%s' in file `%s'.", Hash_GetKey(entry), filename); exit_code = 1; return; } head->in_progress = SET; /* * while provision_list is not empty * do_file(first_member_of(provision_list)); */ while (head->next != NULL) do_file(head->next->fnode); } static int skip_ok(filenode *fnode) { strnodelist *s; strnodelist *k; for (s = skip_list; s; s = s->next) for (k = fnode->keyword_list; k; k = k->next) if (strcmp(k->s, s->s) == 0) return (0); return (1); } static int keep_ok(filenode *fnode) { strnodelist *s; strnodelist *k; for (s = keep_list; s; s = s->next) for (k = fnode->keyword_list; k; k = k->next) if (strcmp(k->s, s->s) == 0) return (1); /* an empty keep_list means every one */ return (!keep_list); } /* * given a filenode, we ensure we are not a cyclic graph. if this * is ok, we loop over the filenodes requirements, calling satisfy_req() * for each of them.. once we have done this, remove this filenode * from each provision table, as we are now done. * * NOTE: do_file() is called recursively from several places and cannot * safely free() anything related to items that may be recursed on. - * Circular dependancies will cause problems if we do. + * Circular dependencies will cause problems if we do. */ static void do_file(filenode *fnode) { f_reqnode *r, *r_tmp; f_provnode *p, *p_tmp; provnode *pnode; int was_set; DPRINTF((stderr, "do_file on %s.\n", fnode->filename)); /* * if fnode is marked as in progress, * print that fnode; is circularly depended upon and abort. */ if (fnode->in_progress == SET) { warnx("Circular dependency on file `%s'.", fnode->filename); was_set = exit_code = 1; } else was_set = 0; /* mark fnode */ fnode->in_progress = SET; /* * for each requirement of fnode -> r * satisfy_req(r, filename) */ r = fnode->req_list; while (r != NULL) { r_tmp = r; satisfy_req(r, fnode->filename); r = r->next; #if 0 if (was_set == 0) free(r_tmp); #endif } fnode->req_list = NULL; /* * for each provision of fnode -> p * remove fnode from provision list for p in hash table */ p = fnode->prov_list; while (p != NULL) { p_tmp = p; pnode = p->pnode; if (pnode->next != NULL) { pnode->next->last = pnode->last; } if (pnode->last != NULL) { pnode->last->next = pnode->next; } free(pnode); p = p->next; free(p_tmp); } fnode->prov_list = NULL; /* do_it(fnode) */ DPRINTF((stderr, "next do: ")); /* if we were already in progress, don't print again */ if (was_set == 0 && skip_ok(fnode) && keep_ok(fnode)) printf("%s\n", fnode->filename); if (fnode->next != NULL) { fnode->next->last = fnode->last; } if (fnode->last != NULL) { fnode->last->next = fnode->next; } DPRINTF((stderr, "nuking %s\n", fnode->filename)); #if 0 if (was_set == 0) { free(fnode->filename); free(fnode); } #endif } static void generate_ordering(void) { /* * while there remain undone files{f}, * pick an arbitrary f, and do_file(f) * Note that the first file in the file list is perfectly * arbitrary, and easy to find, so we use that. */ /* * N.B.: the file nodes "self delete" after they execute, so * after each iteration of the loop, the head will be pointing * to something totally different. The loop ends up being * executed only once for every strongly connected set of * nodes. */ while (fn_head->next != NULL) { DPRINTF((stderr, "generate on %s\n", fn_head->next->filename)); do_file(fn_head->next); } } Index: head/usr.bin/calendar/parsedata.c =================================================================== --- head/usr.bin/calendar/parsedata.c (revision 289676) +++ head/usr.bin/calendar/parsedata.c (revision 289677) @@ -1,1116 +1,1116 @@ /*- * Copyright (c) 1992-2009 Edwin Groothuis . * 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 "calendar.h" static char *showflags(int flags); static int isonlydigits(char *s, int nostar); static const char *getmonthname(int i); static int checkmonth(char *s, size_t *len, size_t *offset, const char **month); static const char *getdayofweekname(int i); static int checkdayofweek(char *s, size_t *len, size_t *offset, const char **dow); static int indextooffset(char *s); static int parseoffset(char *s); static char *floattoday(int year, double f); static char *floattotime(double f); static int wdayom (int day, int offset, int month, int year); /* * Expected styles: * * Date ::= Month . ' ' . DayOfMonth | * Month . ' ' . DayOfWeek . ModifierIndex | * Month . '/' . DayOfMonth | * Month . '/' . DayOfWeek . ModifierIndex | * DayOfMonth . ' ' . Month | * DayOfMonth . '/' . Month | * DayOfWeek . ModifierIndex . ' ' .Month | * DayOfWeek . ModifierIndex . '/' .Month | * DayOfWeek . ModifierIndex | * SpecialDay . ModifierOffset * * Month ::= MonthName | MonthNumber | '*' * MonthNumber ::= '0' ... '9' | '00' ... '09' | '10' ... '12' * MonthName ::= MonthNameShort | MonthNameLong * MonthNameLong ::= 'January' ... 'December' * MonthNameShort ::= 'Jan' ... 'Dec' | 'Jan.' ... 'Dec.' * * DayOfWeek ::= DayOfWeekShort | DayOfWeekLong * DayOfWeekShort ::= 'Mon' .. 'Sun' * DayOfWeekLong ::= 'Monday' .. 'Sunday' * DayOfMonth ::= '0' ... '9' | '00' ... '09' | '10' ... '29' | * '30' ... '31' | '*' * * ModifierOffset ::= '' | '+' . ModifierNumber | '-' . ModifierNumber * ModifierNumber ::= '0' ... '9' | '00' ... '99' | '000' ... '299' | * '300' ... '359' | '360' ... '365' * ModifierIndex ::= 'Second' | 'Third' | 'Fourth' | 'Fifth' | * 'First' | 'Last' * * SpecialDay ::= 'Easter' | 'Paskha' | 'ChineseNewYear' * */ static int determinestyle(char *date, int *flags, char *month, int *imonth, char *dayofmonth, int *idayofmonth, char *dayofweek, int *idayofweek, char *modifieroffset, char *modifierindex, char *specialday, char *year, int *iyear) { char *p, *p1, *p2, *py; const char *dow, *pmonth; char pold; size_t len, offset; *flags = F_NONE; *month = '\0'; *imonth = 0; *year = '\0'; *iyear = 0; *dayofmonth = '\0'; *idayofmonth = 0; *dayofweek = '\0'; *idayofweek = 0; *modifieroffset = '\0'; *modifierindex = '\0'; *specialday = '\0'; #define CHECKSPECIAL(s1, s2, lens2, type) \ if (s2 != NULL && strncmp(s1, s2, lens2) == 0) { \ *flags |= F_SPECIALDAY; \ *flags |= type; \ *flags |= F_VARIABLE; \ if (strlen(s1) == lens2) { \ strcpy(specialday, s1); \ return (1); \ } \ strncpy(specialday, s1, lens2); \ specialday[lens2] = '\0'; \ strcpy(modifieroffset, s1 + lens2); \ *flags |= F_MODIFIEROFFSET; \ return (1); \ } if ((p = strchr(date, ' ')) == NULL) { if ((p = strchr(date, '/')) == NULL) { CHECKSPECIAL(date, STRING_CNY, strlen(STRING_CNY), F_CNY); CHECKSPECIAL(date, ncny.name, ncny.len, F_CNY); CHECKSPECIAL(date, STRING_NEWMOON, strlen(STRING_NEWMOON), F_NEWMOON); CHECKSPECIAL(date, nnewmoon.name, nnewmoon.len, F_NEWMOON); CHECKSPECIAL(date, STRING_FULLMOON, strlen(STRING_FULLMOON), F_FULLMOON); CHECKSPECIAL(date, nfullmoon.name, nfullmoon.len, F_FULLMOON); CHECKSPECIAL(date, STRING_PASKHA, strlen(STRING_PASKHA), F_PASKHA); CHECKSPECIAL(date, npaskha.name, npaskha.len, F_PASKHA); CHECKSPECIAL(date, STRING_EASTER, strlen(STRING_EASTER), F_EASTER); CHECKSPECIAL(date, neaster.name, neaster.len, F_EASTER); CHECKSPECIAL(date, STRING_MAREQUINOX, strlen(STRING_MAREQUINOX), F_MAREQUINOX); CHECKSPECIAL(date, nmarequinox.name, nmarequinox.len, F_SEPEQUINOX); CHECKSPECIAL(date, STRING_SEPEQUINOX, strlen(STRING_SEPEQUINOX), F_SEPEQUINOX); CHECKSPECIAL(date, nsepequinox.name, nsepequinox.len, F_SEPEQUINOX); CHECKSPECIAL(date, STRING_JUNSOLSTICE, strlen(STRING_JUNSOLSTICE), F_JUNSOLSTICE); CHECKSPECIAL(date, njunsolstice.name, njunsolstice.len, F_JUNSOLSTICE); CHECKSPECIAL(date, STRING_DECSOLSTICE, strlen(STRING_DECSOLSTICE), F_DECSOLSTICE); CHECKSPECIAL(date, ndecsolstice.name, ndecsolstice.len, F_DECSOLSTICE); if (checkdayofweek(date, &len, &offset, &dow) != 0) { *flags |= F_DAYOFWEEK; *flags |= F_VARIABLE; *idayofweek = offset; if (strlen(date) == len) { strcpy(dayofweek, date); return (1); } strncpy(dayofweek, date, len); dayofweek[len] = '\0'; strcpy(modifierindex, date + len); *flags |= F_MODIFIERINDEX; return (1); } if (isonlydigits(date, 1)) { /* Assume month number only */ *flags |= F_MONTH; *imonth = (int)strtol(date, (char **)NULL, 10); strcpy(month, getmonthname(*imonth)); return(1); } return (0); } } /* * After this, leave by goto-ing to "allfine" or "fail" to restore the * original data in `date'. */ pold = *p; *p = 0; p1 = date; p2 = p + 1; /* Now p2 points to the next field and p1 to the first field */ if ((py = strchr(p2, '/')) != NULL) { /* We have a year in the string. Now this is getting tricky */ strcpy(year, p1); *iyear = (int)strtol(year, NULL, 10); p1 = p2; p2 = py + 1; *py = 0; *flags |= F_YEAR; } /* Check if there is a month-string in the date */ if ((checkmonth(p1, &len, &offset, &pmonth) != 0) || (checkmonth(p2, &len, &offset, &pmonth) != 0 && (p2 = p1))) { /* p2 is the non-month part */ *flags |= F_MONTH; *imonth = offset; strcpy(month, getmonthname(offset)); if (isonlydigits(p2, 1)) { strcpy(dayofmonth, p2); *idayofmonth = (int)strtol(p2, (char **)NULL, 10); *flags |= F_DAYOFMONTH; goto allfine; } if (strcmp(p2, "*") == 0) { *flags |= F_ALLDAY; goto allfine; } if (checkdayofweek(p2, &len, &offset, &dow) != 0) { *flags |= F_DAYOFWEEK; *flags |= F_VARIABLE; *idayofweek = offset; strcpy(dayofweek, getdayofweekname(offset)); if (strlen(p2) == len) goto allfine; strcpy(modifierindex, p2 + len); *flags |= F_MODIFIERINDEX; goto allfine; } goto fail; } /* Check if there is an every-day or every-month in the string */ if ((strcmp(p1, "*") == 0 && isonlydigits(p2, 1)) || (strcmp(p2, "*") == 0 && isonlydigits(p1, 1) && (p2 = p1))) { int d; *flags |= F_ALLMONTH; *flags |= F_DAYOFMONTH; d = (int)strtol(p2, (char **)NULL, 10); *idayofmonth = d; sprintf(dayofmonth, "%d", d); goto allfine; } /* Month as a number, then a weekday */ if (isonlydigits(p1, 1) && checkdayofweek(p2, &len, &offset, &dow) != 0) { int d; *flags |= F_MONTH; *flags |= F_DAYOFWEEK; *flags |= F_VARIABLE; *idayofweek = offset; d = (int)strtol(p1, (char **)NULL, 10); *imonth = d; strcpy(month, getmonthname(d)); strcpy(dayofweek, getdayofweekname(offset)); if (strlen(p2) == len) goto allfine; strcpy(modifierindex, p2 + len); *flags |= F_MODIFIERINDEX; goto allfine; } /* If both the month and date are specified as numbers */ if (isonlydigits(p1, 1) && isonlydigits(p2, 0)) { - /* Now who wants to be this ambigious? :-( */ + /* Now who wants to be this ambiguous? :-( */ int m, d; if (strchr(p2, '*') != NULL) *flags |= F_VARIABLE; m = (int)strtol(p1, (char **)NULL, 10); d = (int)strtol(p2, (char **)NULL, 10); *flags |= F_MONTH; *flags |= F_DAYOFMONTH; if (m > 12) { *imonth = d; *idayofmonth = m; strcpy(month, getmonthname(d)); sprintf(dayofmonth, "%d", m); } else { *imonth = m; *idayofmonth = d; strcpy(month, getmonthname(m)); sprintf(dayofmonth, "%d", d); } goto allfine; } /* FALLTHROUGH */ fail: *p = pold; return (0); allfine: *p = pold; return (1); } void remember(int *rememberindex, int *y, int *m, int *d, char **ed, int yy, int mm, int dd, char *extra); void remember(int *rememberindex, int *y, int *m, int *d, char **ed, int yy, int mm, int dd, char *extra) { static int warned = 0; if (*rememberindex >= MAXCOUNT - 1) { if (warned == 0) warnx("Index > %d, ignored", MAXCOUNT); warned++; return; } y[*rememberindex] = yy; m[*rememberindex] = mm; d[*rememberindex] = dd; if (extra != NULL) strcpy(ed[*rememberindex], extra); else ed[*rememberindex][0] = '\0'; *rememberindex += 1; } static void debug_determinestyle(int dateonly, char *date, int flags, char *month, int imonth, char *dayofmonth, int idayofmonth, char *dayofweek, int idayofweek, char *modifieroffset, char *modifierindex, char *specialday, char *year, int iyear) { if (dateonly != 0) { printf("-------\ndate: |%s|\n", date); if (dateonly == 1) return; } printf("flags: %x - %s\n", flags, showflags(flags)); if (modifieroffset[0] != '\0') printf("modifieroffset: |%s|\n", modifieroffset); if (modifierindex[0] != '\0') printf("modifierindex: |%s|\n", modifierindex); if (year[0] != '\0') printf("year: |%s| (%d)\n", year, iyear); if (month[0] != '\0') printf("month: |%s| (%d)\n", month, imonth); if (dayofmonth[0] != '\0') printf("dayofmonth: |%s| (%d)\n", dayofmonth, idayofmonth); if (dayofweek[0] != '\0') printf("dayofweek: |%s| (%d)\n", dayofweek, idayofweek); if (specialday[0] != '\0') printf("specialday: |%s|\n", specialday); } static struct yearinfo { int year; int ieaster, ipaskha, firstcnyday; double ffullmoon[MAXMOONS], fnewmoon[MAXMOONS]; double ffullmooncny[MAXMOONS], fnewmooncny[MAXMOONS]; int ichinesemonths[MAXMOONS]; double equinoxdays[2], solsticedays[2]; int *monthdays; struct yearinfo *next; } *years, *yearinfo; /* * Calculate dates with offset from weekdays, like Thurs-3, Wed+2, etc. * day is the day of the week, * offset the ordinal number of the weekday in the month. */ static int wdayom (int day, int offset, int month, int year) { /* Weekday of first day in month */ int wday1; /* first day of month */ /* Weekday of last day in month */ int wdayn; int d; wday1 = first_dayofweek_of_month(year, month); if (wday1 < 0) /* not set */ return (wday1); /* * Date of zeroth or first of our weekday in month, depending on the * relationship with the first of the month. The range is -6:6. */ d = (day - wday1 + 1) % 7; /* * Which way are we counting? Offset 0 is invalid, abs (offset) > 5 is * meaningless, but that's OK. Offset 5 may or may not be meaningless, * so there's no point in complaining for complaining's sake. */ if (offset < 0) { /* back from end of month */ /* FIXME */ wdayn = d; while (wdayn <= yearinfo->monthdays[month]) wdayn += 7; d = offset * 7 + wdayn; } else if (offset > 0){ if (d > 0) d += offset * 7 - 7; else d += offset * 7; } else warnx ("Invalid offset 0"); return (d); } /* * Possible date formats include any combination of: * 3-charmonth (January, Jan, Jan) * 3-charweekday (Friday, Monday, mon.) * numeric month or day (1, 2, 04) * * Any character may separate them, or they may not be separated. Any line, * following a line that is matched, that starts with "whitespace", is shown * along with the matched line. */ int parsedaymonth(char *date, int *yearp, int *monthp, int *dayp, int *flags, char **edp) { char month[100], dayofmonth[100], dayofweek[100], modifieroffset[100]; char syear[100]; char modifierindex[100], specialday[100]; int idayofweek = -1, imonth = -1, idayofmonth = -1, iyear = -1; int year, remindex; int d, m, dow, rm, rd, offset; char *ed; int retvalsign = 1; /* * CONVENTION * * Month: 1-12 * Monthname: Jan .. Dec * Day: 1-31 * Weekday: Mon .. Sun * */ *flags = 0; if (debug) debug_determinestyle(1, date, *flags, month, imonth, dayofmonth, idayofmonth, dayofweek, idayofweek, modifieroffset, modifierindex, specialday, syear, iyear); if (determinestyle(date, flags, month, &imonth, dayofmonth, &idayofmonth, dayofweek, &idayofweek, modifieroffset, modifierindex, specialday, syear, &iyear) == 0) { if (debug) printf("Failed!\n"); return (0); } if (debug) debug_determinestyle(0, date, *flags, month, imonth, dayofmonth, idayofmonth, dayofweek, idayofweek, modifieroffset, modifierindex, specialday, syear, iyear); remindex = 0; for (year = year1; year <= year2; year++) { int lflags = *flags; /* If the year is specified, only do it if it is this year! */ if ((lflags & F_YEAR) != 0) if (iyear != year) continue; lflags &= ~F_YEAR; /* Get important dates for this year */ yearinfo = years; while (yearinfo != NULL) { if (yearinfo->year == year) break; yearinfo = yearinfo -> next; } if (yearinfo == NULL) { yearinfo = (struct yearinfo *)calloc(1, sizeof(struct yearinfo)); if (yearinfo == NULL) errx(1, "Unable to allocate more years"); yearinfo->year = year; yearinfo->next = years; years = yearinfo; yearinfo->monthdays = monthdaytab[isleap(year)]; yearinfo->ieaster = easter(year); yearinfo->ipaskha = paskha(year); fpom(year, UTCOffset, yearinfo->ffullmoon, yearinfo->fnewmoon); fpom(year, UTCOFFSET_CNY, yearinfo->ffullmooncny, yearinfo->fnewmooncny); fequinoxsolstice(year, UTCOffset, yearinfo->equinoxdays, yearinfo->solsticedays); /* * CNY: Match day with sun longitude at 330` with new * moon */ yearinfo->firstcnyday = calculatesunlongitude30(year, UTCOFFSET_CNY, yearinfo->ichinesemonths); for (m = 0; yearinfo->fnewmooncny[m] >= 0; m++) { if (yearinfo->fnewmooncny[m] > yearinfo->firstcnyday) { yearinfo->firstcnyday = floor(yearinfo->fnewmooncny[m - 1]); break; } } } /* Same day every year */ if (lflags == (F_MONTH | F_DAYOFMONTH)) { if (!remember_ymd(year, imonth, idayofmonth)) continue; remember(&remindex, yearp, monthp, dayp, edp, year, imonth, idayofmonth, NULL); continue; } /* XXX Same day every year, but variable */ if (lflags == (F_MONTH | F_DAYOFMONTH | F_VARIABLE)) { if (!remember_ymd(year, imonth, idayofmonth)) continue; remember(&remindex, yearp, monthp, dayp, edp, year, imonth, idayofmonth, NULL); continue; } /* Same day every month */ if (lflags == (F_ALLMONTH | F_DAYOFMONTH)) { for (m = 1; m <= 12; m++) { if (!remember_ymd(year, m, idayofmonth)) continue; remember(&remindex, yearp, monthp, dayp, edp, year, m, idayofmonth, NULL); } continue; } /* Every day of a month */ if (lflags == (F_ALLDAY | F_MONTH)) { for (d = 1; d <= yearinfo->monthdays[imonth]; d++) { if (!remember_ymd(year, imonth, d)) continue; remember(&remindex, yearp, monthp, dayp, edp, year, imonth, d, NULL); } continue; } /* One day of every month */ if (lflags == (F_ALLMONTH | F_DAYOFWEEK)) { for (m = 1; m <= 12; m++) { if (!remember_ymd(year, m, idayofmonth)) continue; remember(&remindex, yearp, monthp, dayp, edp, year, m, idayofmonth, NULL); } continue; } /* Every dayofweek of the year */ if (lflags == (F_DAYOFWEEK | F_VARIABLE)) { dow = first_dayofweek_of_year(year); d = (idayofweek - dow + 8) % 7; while (d <= 366) { if (remember_yd(year, d, &rm, &rd)) remember(&remindex, yearp, monthp, dayp, edp, year, rm, rd, NULL); d += 7; } continue; } /* * Every so-manied dayofweek of every month of the year: * Thu-3 */ if (lflags == (F_DAYOFWEEK | F_MODIFIERINDEX | F_VARIABLE)) { offset = indextooffset(modifierindex); for (m = 0; m <= 12; m++) { d = wdayom (idayofweek, offset, m, year); if (remember_ymd(year, m, d)) { remember(&remindex, yearp, monthp, dayp, edp, year, m, d, NULL); continue; } } continue; } /* * A certain dayofweek of a month * Jan/Thu-3 */ if (lflags == (F_MONTH | F_DAYOFWEEK | F_MODIFIERINDEX | F_VARIABLE)) { offset = indextooffset(modifierindex); dow = first_dayofweek_of_month(year, imonth); d = (idayofweek - dow + 8) % 7; if (offset > 0) { while (d <= yearinfo->monthdays[imonth]) { if (--offset == 0 && remember_ymd(year, imonth, d)) { remember(&remindex, yearp, monthp, dayp, edp, year, imonth, d, NULL); continue; } d += 7; } continue; } if (offset < 0) { while (d <= yearinfo->monthdays[imonth]) d += 7; while (offset != 0) { offset++; d -= 7; } if (remember_ymd(year, imonth, d)) remember(&remindex, yearp, monthp, dayp, edp, year, imonth, d, NULL); continue; } continue; } /* Every dayofweek of the month */ if (lflags == (F_DAYOFWEEK | F_MONTH | F_VARIABLE)) { dow = first_dayofweek_of_month(year, imonth); d = (idayofweek - dow + 8) % 7; while (d <= yearinfo->monthdays[imonth]) { if (remember_ymd(year, imonth, d)) remember(&remindex, yearp, monthp, dayp, edp, year, imonth, d, NULL); d += 7; } continue; } /* Easter */ if ((lflags & ~F_MODIFIEROFFSET) == (F_SPECIALDAY | F_VARIABLE | F_EASTER)) { offset = 0; if ((lflags & F_MODIFIEROFFSET) != 0) offset = parseoffset(modifieroffset); if (remember_yd(year, yearinfo->ieaster + offset, &rm, &rd)) remember(&remindex, yearp, monthp, dayp, edp, year, rm, rd, NULL); continue; } /* Paskha */ if ((lflags & ~F_MODIFIEROFFSET) == (F_SPECIALDAY | F_VARIABLE | F_PASKHA)) { offset = 0; if ((lflags & F_MODIFIEROFFSET) != 0) offset = parseoffset(modifieroffset); if (remember_yd(year, yearinfo->ipaskha + offset, &rm, &rd)) remember(&remindex, yearp, monthp, dayp, edp, year, rm, rd, NULL); continue; } /* Chinese New Year */ if ((lflags & ~F_MODIFIEROFFSET) == (F_SPECIALDAY | F_VARIABLE | F_CNY)) { offset = 0; if ((lflags & F_MODIFIEROFFSET) != 0) offset = parseoffset(modifieroffset); if (remember_yd(year, yearinfo->firstcnyday + offset, &rm, &rd)) remember(&remindex, yearp, monthp, dayp, edp, year, rm, rd, NULL); continue; } /* FullMoon */ if ((lflags & ~F_MODIFIEROFFSET) == (F_SPECIALDAY | F_VARIABLE | F_FULLMOON)) { int i; offset = 0; if ((lflags & F_MODIFIEROFFSET) != 0) offset = parseoffset(modifieroffset); for (i = 0; yearinfo->ffullmoon[i] > 0; i++) { if (remember_yd(year, floor(yearinfo->ffullmoon[i]) + offset, &rm, &rd)) { ed = floattotime( yearinfo->ffullmoon[i]); remember(&remindex, yearp, monthp, dayp, edp, year, rm, rd, ed); } } continue; } /* NewMoon */ if ((lflags & ~F_MODIFIEROFFSET) == (F_SPECIALDAY | F_VARIABLE | F_NEWMOON)) { int i; offset = 0; if ((lflags & F_MODIFIEROFFSET) != 0) offset = parseoffset(modifieroffset); for (i = 0; yearinfo->ffullmoon[i] > 0; i++) { if (remember_yd(year, floor(yearinfo->fnewmoon[i]) + offset, &rm, &rd)) { ed = floattotime(yearinfo->fnewmoon[i]); remember(&remindex, yearp, monthp, dayp, edp, year, rm, rd, ed); } } continue; } /* (Mar|Sep)Equinox */ if ((lflags & ~F_MODIFIEROFFSET) == (F_SPECIALDAY | F_VARIABLE | F_MAREQUINOX)) { offset = 0; if ((lflags & F_MODIFIEROFFSET) != 0) offset = parseoffset(modifieroffset); if (remember_yd(year, yearinfo->equinoxdays[0] + offset, &rm, &rd)) { ed = floattotime(yearinfo->equinoxdays[0]); remember(&remindex, yearp, monthp, dayp, edp, year, rm, rd, ed); } continue; } if ((lflags & ~F_MODIFIEROFFSET) == (F_SPECIALDAY | F_VARIABLE | F_SEPEQUINOX)) { offset = 0; if ((lflags & F_MODIFIEROFFSET) != 0) offset = parseoffset(modifieroffset); if (remember_yd(year, yearinfo->equinoxdays[1] + offset, &rm, &rd)) { ed = floattotime(yearinfo->equinoxdays[1]); remember(&remindex, yearp, monthp, dayp, edp, year, rm, rd, ed); } continue; } /* (Jun|Dec)Solstice */ if ((lflags & ~F_MODIFIEROFFSET) == (F_SPECIALDAY | F_VARIABLE | F_JUNSOLSTICE)) { offset = 0; if ((lflags & F_MODIFIEROFFSET) != 0) offset = parseoffset(modifieroffset); if (remember_yd(year, yearinfo->solsticedays[0] + offset, &rm, &rd)) { ed = floattotime(yearinfo->solsticedays[0]); remember(&remindex, yearp, monthp, dayp, edp, year, rm, rd, ed); } continue; } if ((lflags & ~F_MODIFIEROFFSET) == (F_SPECIALDAY | F_VARIABLE | F_DECSOLSTICE)) { offset = 0; if ((lflags & F_MODIFIEROFFSET) != 0) offset = parseoffset(modifieroffset); if (remember_yd(year, yearinfo->solsticedays[1] + offset, &rm, &rd)) { ed = floattotime(yearinfo->solsticedays[1]); remember(&remindex, yearp, monthp, dayp, edp, year, rm, rd, ed); } continue; } if (debug) { printf("Unprocessed:\n"); debug_determinestyle(2, date, lflags, month, imonth, dayofmonth, idayofmonth, dayofweek, idayofweek, modifieroffset, modifierindex, specialday, syear, iyear); } retvalsign = -1; } if (retvalsign == -1) return (-remindex - 1); else return (remindex); } static char * showflags(int flags) { static char s[1000]; s[0] = '\0'; if ((flags & F_YEAR) != 0) strcat(s, "year "); if ((flags & F_MONTH) != 0) strcat(s, "month "); if ((flags & F_DAYOFWEEK) != 0) strcat(s, "dayofweek "); if ((flags & F_DAYOFMONTH) != 0) strcat(s, "dayofmonth "); if ((flags & F_MODIFIERINDEX) != 0) strcat(s, "modifierindex "); if ((flags & F_MODIFIEROFFSET) != 0) strcat(s, "modifieroffset "); if ((flags & F_SPECIALDAY) != 0) strcat(s, "specialday "); if ((flags & F_ALLMONTH) != 0) strcat(s, "allmonth "); if ((flags & F_ALLDAY) != 0) strcat(s, "allday "); if ((flags & F_VARIABLE) != 0) strcat(s, "variable "); if ((flags & F_CNY) != 0) strcat(s, "chinesenewyear "); if ((flags & F_PASKHA) != 0) strcat(s, "paskha "); if ((flags & F_EASTER) != 0) strcat(s, "easter "); if ((flags & F_FULLMOON) != 0) strcat(s, "fullmoon "); if ((flags & F_NEWMOON) != 0) strcat(s, "newmoon "); if ((flags & F_MAREQUINOX) != 0) strcat(s, "marequinox "); if ((flags & F_SEPEQUINOX) != 0) strcat(s, "sepequinox "); if ((flags & F_JUNSOLSTICE) != 0) strcat(s, "junsolstice "); if ((flags & F_DECSOLSTICE) != 0) strcat(s, "decsolstice "); return s; } static const char * getmonthname(int i) { if (i <= 0 || i > 12) return (""); if (nmonths[i - 1].len != 0 && nmonths[i - 1].name != NULL) return (nmonths[i - 1].name); return (months[i - 1]); } static int checkmonth(char *s, size_t *len, size_t *offset, const char **month) { struct fixs *n; int i; for (i = 0; fnmonths[i].name != NULL; i++) { n = fnmonths + i; if (strncasecmp(s, n->name, n->len) == 0) { *len = n->len; *month = n->name; *offset = i + 1; return (1); } } for (i = 0; nmonths[i].name != NULL; i++) { n = nmonths + i; if (strncasecmp(s, n->name, n->len) == 0) { *len = n->len; *month = n->name; *offset = i + 1; return (1); } } for (i = 0; fmonths[i] != NULL; i++) { *len = strlen(fmonths[i]); if (strncasecmp(s, fmonths[i], *len) == 0) { *month = fmonths[i]; *offset = i + 1; return (1); } } for (i = 0; months[i] != NULL; i++) { if (strncasecmp(s, months[i], 3) == 0) { *len = 3; *month = months[i]; *offset = i + 1; return (1); } } return (0); } static const char * getdayofweekname(int i) { if (ndays[i].len != 0 && ndays[i].name != NULL) return (ndays[i].name); return (days[i]); } static int checkdayofweek(char *s, size_t *len, size_t *offset, const char **dow) { struct fixs *n; int i; for (i = 0; fndays[i].name != NULL; i++) { n = fndays + i; if (strncasecmp(s, n->name, n->len) == 0) { *len = n->len; *dow = n->name; *offset = i; return (1); } } for (i = 0; ndays[i].name != NULL; i++) { n = ndays + i; if (strncasecmp(s, n->name, n->len) == 0) { *len = n->len; *dow = n->name; *offset = i; return (1); } } for (i = 0; fdays[i] != NULL; i++) { *len = strlen(fdays[i]); if (strncasecmp(s, fdays[i], *len) == 0) { *dow = fdays[i]; *offset = i; return (1); } } for (i = 0; days[i] != NULL; i++) { if (strncasecmp(s, days[i], 3) == 0) { *len = 3; *dow = days[i]; *offset = i; return (1); } } return (0); } static int isonlydigits(char *s, int nostar) { int i; for (i = 0; s[i] != '\0'; i++) { if (nostar == 0 && s[i] == '*' && s[i + 1] == '\0') return 1; if (!isdigit((unsigned char)s[i])) return (0); } return (1); } static int indextooffset(char *s) { int i; struct fixs *n; char *es; if (s[0] == '+' || s[0] == '-') { i = strtol (s, &es, 10); if (*es != '\0') /* trailing junk */ errx (1, "Invalid specifier format: %s\n", s); return (i); } for (i = 0; i < 6; i++) { if (strcasecmp(s, sequences[i]) == 0) { if (i == 5) return (-1); return (i + 1); } } for (i = 0; i < 6; i++) { n = nsequences + i; if (n->len == 0) continue; if (strncasecmp(s, n->name, n->len) == 0) { if (i == 5) return (-1); return (i + 1); } } return (0); } static int parseoffset(char *s) { return strtol(s, NULL, 10); } static char * floattotime(double f) { static char buf[100]; int hh, mm, ss, i; f -= floor(f); i = f * SECSPERDAY; hh = i / SECSPERHOUR; i %= SECSPERHOUR; mm = i / SECSPERMINUTE; i %= SECSPERMINUTE; ss = i; sprintf(buf, "%02d:%02d:%02d", hh, mm, ss); return (buf); } static char * floattoday(int year, double f) { static char buf[100]; int i, m, d, hh, mm, ss; int *cumdays = cumdaytab[isleap(year)]; for (i = 0; 1 + cumdays[i] < f; i++) ; m = --i; d = floor(f - 1 - cumdays[i]); f -= floor(f); i = f * SECSPERDAY; hh = i / SECSPERHOUR; i %= SECSPERHOUR; mm = i / SECSPERMINUTE; i %= SECSPERMINUTE; ss = i; sprintf(buf, "%02d-%02d %02d:%02d:%02d", m, d, hh, mm, ss); return (buf); } void dodebug(char *what) { int year; printf("UTCOffset: %g\n", UTCOffset); printf("eastlongitude: %d\n", EastLongitude); if (strcmp(what, "moon") == 0) { double ffullmoon[MAXMOONS], fnewmoon[MAXMOONS]; int i; for (year = year1; year <= year2; year++) { fpom(year, UTCOffset, ffullmoon, fnewmoon); printf("Full moon %d:\t", year); for (i = 0; ffullmoon[i] >= 0; i++) { printf("%g (%s) ", ffullmoon[i], floattoday(year, ffullmoon[i])); } printf("\nNew moon %d:\t", year); for (i = 0; fnewmoon[i] >= 0; i++) { printf("%g (%s) ", fnewmoon[i], floattoday(year, fnewmoon[i])); } printf("\n"); } return; } if (strcmp(what, "sun") == 0) { double equinoxdays[2], solsticedays[2]; for (year = year1; year <= year2; year++) { printf("Sun in %d:\n", year); fequinoxsolstice(year, UTCOffset, equinoxdays, solsticedays); printf("e[0] - %g (%s)\n", equinoxdays[0], floattoday(year, equinoxdays[0])); printf("e[1] - %g (%s)\n", equinoxdays[1], floattoday(year, equinoxdays[1])); printf("s[0] - %g (%s)\n", solsticedays[0], floattoday(year, solsticedays[0])); printf("s[1] - %g (%s)\n", solsticedays[1], floattoday(year, solsticedays[1])); } return; } } Index: head/usr.bin/colldef/parse.y =================================================================== --- head/usr.bin/colldef/parse.y (revision 289676) +++ head/usr.bin/colldef/parse.y (revision 289677) @@ -1,384 +1,384 @@ %{ /*- * Copyright (c) 1995 Alex Tatmanjants * at Electronni Visti IA, Kiev, Ukraine. * 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 ``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 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 "collate.h" #include "common.h" extern FILE *yyin; void yyerror(const char *fmt, ...) __printflike(1, 2); int yyparse(void); int yylex(void); static void usage(void); static void collate_print_tables(void); char map_name[FILENAME_MAX] = "."; char curr_chain[STR_LEN]; char __collate_version[STR_LEN]; u_char charmap_table[UCHAR_MAX + 1][CHARMAP_SYMBOL_LEN]; #undef __collate_substitute_table u_char __collate_substitute_table[UCHAR_MAX + 1][STR_LEN]; #undef __collate_char_pri_table struct __collate_st_char_pri __collate_char_pri_table[UCHAR_MAX + 1]; struct __collate_st_chain_pri *__collate_chain_pri_table; int chain_index = 0; int prim_pri = 1, sec_pri = 1; #ifdef COLLATE_DEBUG int debug; #endif const char *out_file = "LC_COLLATE"; %} %union { u_char ch; u_char str[BUFSIZE]; } %token SUBSTITUTE WITH ORDER RANGE %token STRING %token DEFN %token CHAR %% collate : statment_list ; statment_list : statment | statment_list '\n' statment ; statment : | charmap | substitute | order ; charmap : DEFN CHAR { if (strlen($1) + 1 > CHARMAP_SYMBOL_LEN) yyerror("Charmap symbol name '%s' is too long", $1); strcpy(charmap_table[$2], $1); } ; substitute : SUBSTITUTE CHAR WITH STRING { if ($2 == '\0') yyerror("NUL character can't be substituted"); if (strchr($4, $2) != NULL) yyerror("Char 0x%02x substitution is recursive", $2); if (strlen($4) + 1 > STR_LEN) yyerror("Char 0x%02x substitution is too long", $2); strcpy(__collate_substitute_table[$2], $4); } ; order : ORDER order_list { FILE *fp; int ch, substed, ordered; uint32_t u32; for (ch = 0; ch < UCHAR_MAX + 1; ch++) { substed = (__collate_substitute_table[ch][0] != ch); ordered = !!__collate_char_pri_table[ch].prim; if (!ordered && !substed) yyerror("Char 0x%02x not found", ch); if (substed && ordered) yyerror("Char 0x%02x can't be ordered since substituted", ch); } if ((__collate_chain_pri_table = realloc(__collate_chain_pri_table, sizeof(*__collate_chain_pri_table) * (chain_index + 1))) == NULL) yyerror("can't grow chain table"); (void)memset(&__collate_chain_pri_table[chain_index], 0, sizeof(__collate_chain_pri_table[0])); chain_index++; #ifdef COLLATE_DEBUG if (debug) collate_print_tables(); #endif if ((fp = fopen(out_file, "w")) == NULL) err(EX_UNAVAILABLE, "can't open destination file %s", out_file); strcpy(__collate_version, COLLATE_VERSION1_2); if (fwrite(__collate_version, sizeof(__collate_version), 1, fp) != 1) err(EX_IOERR, - "IO error writting collate version to destination file %s", + "I/O error writing collate version to destination file %s", out_file); u32 = htonl(chain_index); if (fwrite(&u32, sizeof(u32), 1, fp) != 1) err(EX_IOERR, - "IO error writting chains number to destination file %s", + "I/O error writing chains number to destination file %s", out_file); if (fwrite(__collate_substitute_table, sizeof(__collate_substitute_table), 1, fp) != 1) err(EX_IOERR, - "IO error writting substitute table to destination file %s", + "I/O error writing substitution table to destination file %s", out_file); for (ch = 0; ch < UCHAR_MAX + 1; ch++) { __collate_char_pri_table[ch].prim = htonl(__collate_char_pri_table[ch].prim); __collate_char_pri_table[ch].sec = htonl(__collate_char_pri_table[ch].sec); } if (fwrite(__collate_char_pri_table, sizeof(__collate_char_pri_table), 1, fp) != 1) err(EX_IOERR, - "IO error writting char table to destination file %s", + "I/O error writing char table to destination file %s", out_file); for (ch = 0; ch < chain_index; ch++) { __collate_chain_pri_table[ch].prim = htonl(__collate_chain_pri_table[ch].prim); __collate_chain_pri_table[ch].sec = htonl(__collate_chain_pri_table[ch].sec); } if (fwrite(__collate_chain_pri_table, sizeof(*__collate_chain_pri_table), chain_index, fp) != (size_t)chain_index) err(EX_IOERR, - "IO error writting chain table to destination file %s", + "I/O error writing chain table to destination file %s", out_file); if (fclose(fp) != 0) - err(EX_IOERR, "IO error closing destination file %s", + err(EX_IOERR, "I/O error closing destination file %s", out_file); exit(EX_OK); } ; order_list : item | order_list ';' item ; chain : CHAR CHAR { curr_chain[0] = $1; curr_chain[1] = $2; if (curr_chain[0] == '\0' || curr_chain[1] == '\0') yyerror("\\0 can't be chained"); curr_chain[2] = '\0'; } | chain CHAR { static char tb[2]; tb[0] = $2; if (tb[0] == '\0') yyerror("\\0 can't be chained"); if (strlen(curr_chain) + 2 > STR_LEN) yyerror("Chain '%s' grows too long", curr_chain); (void)strcat(curr_chain, tb); } ; item : CHAR { if (__collate_char_pri_table[$1].prim) yyerror("Char 0x%02x duplicated", $1); __collate_char_pri_table[$1].prim = prim_pri++; } | chain { if ((__collate_chain_pri_table = realloc(__collate_chain_pri_table, sizeof(*__collate_chain_pri_table) * (chain_index + 1))) == NULL) yyerror("can't grow chain table"); (void)memset(&__collate_chain_pri_table[chain_index], 0, sizeof(__collate_chain_pri_table[0])); (void)strcpy(__collate_chain_pri_table[chain_index].str, curr_chain); __collate_chain_pri_table[chain_index].prim = prim_pri++; chain_index++; } | CHAR RANGE CHAR { u_int i; if ($3 <= $1) yyerror("Illegal range 0x%02x -- 0x%02x", $1, $3); for (i = $1; i <= $3; i++) { if (__collate_char_pri_table[(u_char)i].prim) yyerror("Char 0x%02x duplicated", (u_char)i); __collate_char_pri_table[(u_char)i].prim = prim_pri++; } } | '{' prim_order_list '}' { prim_pri++; } | '(' sec_order_list ')' { prim_pri++; sec_pri = 1; } ; prim_order_list : prim_sub_item | prim_order_list ',' prim_sub_item ; sec_order_list : sec_sub_item | sec_order_list ',' sec_sub_item ; prim_sub_item : CHAR { if (__collate_char_pri_table[$1].prim) yyerror("Char 0x%02x duplicated", $1); __collate_char_pri_table[$1].prim = prim_pri; } | CHAR RANGE CHAR { u_int i; if ($3 <= $1) yyerror("Illegal range 0x%02x -- 0x%02x", $1, $3); for (i = $1; i <= $3; i++) { if (__collate_char_pri_table[(u_char)i].prim) yyerror("Char 0x%02x duplicated", (u_char)i); __collate_char_pri_table[(u_char)i].prim = prim_pri; } } | chain { if ((__collate_chain_pri_table = realloc(__collate_chain_pri_table, sizeof(*__collate_chain_pri_table) * (chain_index + 1))) == NULL) yyerror("can't grow chain table"); (void)memset(&__collate_chain_pri_table[chain_index], 0, sizeof(__collate_chain_pri_table[0])); (void)strcpy(__collate_chain_pri_table[chain_index].str, curr_chain); __collate_chain_pri_table[chain_index].prim = prim_pri; chain_index++; } ; sec_sub_item : CHAR { if (__collate_char_pri_table[$1].prim) yyerror("Char 0x%02x duplicated", $1); __collate_char_pri_table[$1].prim = prim_pri; __collate_char_pri_table[$1].sec = sec_pri++; } | CHAR RANGE CHAR { u_int i; if ($3 <= $1) yyerror("Illegal range 0x%02x -- 0x%02x", $1, $3); for (i = $1; i <= $3; i++) { if (__collate_char_pri_table[(u_char)i].prim) yyerror("Char 0x%02x duplicated", (u_char)i); __collate_char_pri_table[(u_char)i].prim = prim_pri; __collate_char_pri_table[(u_char)i].sec = sec_pri++; } } | chain { if ((__collate_chain_pri_table = realloc(__collate_chain_pri_table, sizeof(*__collate_chain_pri_table) * (chain_index + 1))) == NULL) yyerror("can't grow chain table"); (void)memset(&__collate_chain_pri_table[chain_index], 0, sizeof(__collate_chain_pri_table[0])); (void)strcpy(__collate_chain_pri_table[chain_index].str, curr_chain); __collate_chain_pri_table[chain_index].prim = prim_pri; __collate_chain_pri_table[chain_index].sec = sec_pri++; chain_index++; } ; %% int main(int ac, char **av) { int ch; #ifdef COLLATE_DEBUG while((ch = getopt(ac, av, ":do:I:")) != -1) { #else while((ch = getopt(ac, av, ":o:I:")) != -1) { #endif switch (ch) { #ifdef COLLATE_DEBUG case 'd': debug++; break; #endif case 'o': out_file = optarg; break; case 'I': strlcpy(map_name, optarg, sizeof(map_name)); break; default: usage(); } } ac -= optind; av += optind; if (ac > 0) { if ((yyin = fopen(*av, "r")) == NULL) err(EX_UNAVAILABLE, "can't open source file %s", *av); } for (ch = 0; ch <= UCHAR_MAX; ch++) __collate_substitute_table[ch][0] = ch; yyparse(); return 0; } static void usage(void) { fprintf(stderr, "usage: colldef [-I map_dir] [-o out_file] [filename]\n"); exit(EX_USAGE); } void yyerror(const char *fmt, ...) { va_list ap; char msg[128]; va_start(ap, fmt); vsnprintf(msg, sizeof(msg), fmt, ap); va_end(ap); errx(EX_UNAVAILABLE, "%s near line %d", msg, line_no); } #ifdef COLLATE_DEBUG static void collate_print_tables(void) { int i; printf("Substitute table:\n"); for (i = 0; i < UCHAR_MAX + 1; i++) if (i != *__collate_substitute_table[i]) printf("\t'%c' --> \"%s\"\n", i, __collate_substitute_table[i]); printf("Chain priority table:\n"); for (i = 0; i < chain_index - 1; i++) printf("\t\"%s\" : %d %d\n", __collate_chain_pri_table[i].str, __collate_chain_pri_table[i].prim, __collate_chain_pri_table[i].sec); printf("Char priority table:\n"); for (i = 0; i < UCHAR_MAX + 1; i++) printf("\t'%c' : %d %d\n", i, __collate_char_pri_table[i].prim, __collate_char_pri_table[i].sec); } #endif Index: head/usr.bin/dtc/dtb.hh =================================================================== --- head/usr.bin/dtc/dtb.hh (revision 289676) +++ head/usr.bin/dtc/dtb.hh (revision 289677) @@ -1,365 +1,365 @@ /*- * Copyright (c) 2013 David Chisnall * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * 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. * * $FreeBSD$ */ #ifndef _DTB_HH_ #define _DTB_HH_ #include #include "string.hh" #include namespace dtc { /** * The dtb namespace contains code related to the generation of device tree * blobs, the binary representation of flattened device trees. The abstract * tree representation calls into this code to generate the output. */ namespace dtb { /** The token types in the DTB, as defined by §7.4.1 of the ePAPR * specification. All of these values are written in big-endian format in the * output. */ enum token_type { /** * Marker indicating the start of a node in the tree. This is followed * by the nul-terminated name. If a unit address is specified, then * the name also contains the address, with an @ symbol between the end * of the name and the start of the address. * * The name is then padded such that the next token begins on a 4-byte * boundary. The node may contain properties, other nodes, both, or be * empty. */ FDT_BEGIN_NODE = 0x00000001, /** * Marker indicating the end of a node. */ FDT_END_NODE = 0x00000002, /** * The start of a property. This is followed by two 32-bit big-endian * values. The first indicates the length of the property value, the * second its index in the strings table. It is then followed by the * property value, if the value is of non-zero length. */ FDT_PROP = 0x00000003, /** * Ignored token. May be used for padding inside DTB nodes. */ FDT_NOP = 0x00000004, /** * Marker indicating the end of the tree. */ FDT_END = 0x00000009 }; /** * Returns the token as a string. This is used for debugging and for printing * human-friendly error messages about malformed DTB input. */ inline const char *token_type_name(token_type t) { switch(t) { case FDT_BEGIN_NODE: return "FDT_BEGIN_NODE"; case FDT_END_NODE: return "FDT_END_NODE"; case FDT_PROP: return "FDT_PROP"; case FDT_NOP: return "FDT_NOP"; case FDT_END: return "FDT_END"; } assert(0); } /** * Abstract class for writing a section of the output. We create one * of these for each section that needs to be written. It is intended to build * a temporary buffer of the output in memory and then write it to a file * stream. The size can be returned after all of the data has been written * into the internal buffer, so the sizes of the three tables can be calculated * before storing them in the buffer. */ struct output_writer { /** * Writes a label into the output stream. This is only applicable for * assembly output, where the labels become symbols that can be * resolved at link time. */ virtual void write_label(string name) = 0; /** * Writes a comment into the output stream. Useful only when debugging * the output. */ virtual void write_comment(string name) = 0; /** * Writes a string. A nul terminator is implicitly added. */ virtual void write_string(string name) = 0; /** * Writes a single 8-bit value. */ virtual void write_data(uint8_t) = 0; /** * Writes a single 32-bit value. The value is written in big-endian * format, but should be passed in the host's native endian. */ virtual void write_data(uint32_t) = 0; /** * Writes a single 64-bit value. The value is written in big-endian * format, but should be passed in the host's native endian. */ virtual void write_data(uint64_t) = 0; /** * Writes the collected output to the specified file descriptor. */ virtual void write_to_file(int fd) = 0; /** * Returns the number of bytes. */ virtual uint32_t size() = 0; /** * Helper for writing tokens to the output stream. This writes a * comment above the token describing its value, for easier debugging * of the output. */ inline void write_token(token_type t) { write_comment(token_type_name(t)); write_data((uint32_t)t); } /** * Helper function that writes a byte buffer to the output, one byte at * a time. */ void write_data(byte_buffer b); }; /** * Binary file writer. This class is responsible for writing the DTB output * directly in blob format. */ class binary_writer : public output_writer { /** * The internal buffer used to store the blob while it is being * constructed. */ byte_buffer buffer; public: /** * The binary format does not support labels, so this method * does nothing. */ virtual void write_label(string name) {} /** * Comments are ignored by the binary writer. */ virtual void write_comment(string name) {} virtual void write_string(string name); virtual void write_data(uint8_t v); virtual void write_data(uint32_t v); virtual void write_data(uint64_t v); virtual void write_to_file(int fd); virtual uint32_t size(); }; /** * Assembly writer. This class is responsible for writing the output in an * assembly format that is suitable for linking into a kernel, loader, and so * on. */ class asm_writer : public output_writer { /** * The internal buffer for temporary values. Note that this actually * contains ASCII text, but it is a byte buffer so that we can just * copy strings across as-is. */ byte_buffer buffer; /** * The number of bytes written to the current line. This is used to * allow line wrapping, where we aim to write four .byte directives to * make the alignment clearer. */ int byte_count; /** * The current number of bytes written. This is the number in binary * format, not the number of bytes in the buffer. */ uint32_t bytes_written; /** - * Writes a C string directly to the ouput as-is. This is mainly used + * Writes a C string directly to the output as-is. This is mainly used * for writing directives. */ void write_string(const char *c); /** * Writes the string, starting on a new line. */ void write_line(const char *c); /** * Writes a byte in binary format. This will emit a single .byte * directive, with up to four per line. */ void write_byte(uint8_t b); public: asm_writer() : byte_count(0), bytes_written(0) {} virtual void write_label(string name); virtual void write_comment(string name); virtual void write_string(string name); virtual void write_data(uint8_t v); virtual void write_data(uint32_t v); virtual void write_data(uint64_t v); virtual void write_to_file(int fd); virtual uint32_t size(); }; /** * Class encapsulating the device tree blob header. This class stores all of * the values found in the header and is responsible for writing them to the * output. */ struct header { /** * Magic value, used to validate that this really is a device tree * blob. Should always be set to 0xd00dfeed. */ uint32_t magic; /** * The total size of the blob, including header, reservations, strings * table, and padding. */ uint32_t totalsize; /** * The offset from the start of the blob of the struct table (i.e. the * part of the blob containing the entire device tree). */ uint32_t off_dt_struct; /** * The offset from the start of the blob of the strings table. */ uint32_t off_dt_strings; /** * The offset of the reservation map from the start of the blob. */ uint32_t off_mem_rsvmap; /** * The version of the blob. This should always be 17. */ uint32_t version; /** * The earliest version of the DTB specification with which this blob * is backwards compatible. This should always be 16. */ uint32_t last_comp_version; /** * The ID of the CPU where this boots. */ uint32_t boot_cpuid_phys; /** * The size of the strings table. */ uint32_t size_dt_strings; /** * The size of the struct table. */ uint32_t size_dt_struct; /** * Writes the entire header to the specified output buffer. */ void write(output_writer &out); /** * Reads the header from bits binary representation in a blob. */ bool read_dtb(input_buffer &input); /** * Default constructor. Initialises the values that have sensible * defaults, leaves the others blank. */ header() : magic(0xd00dfeed), version(17), last_comp_version(16), boot_cpuid_phys(0) {} }; /** * Class encapsulating the string table. FDT strings are stored in a string * section. This maintains a map from strings to their offsets in the strings * section. * * Note: We don't currently do suffix matching, which may save a small amount * of space. */ class string_table { /** * Map from strings to their offset. */ std::map string_offsets; /** * The strings, in the order in which they should be written to the * output. The order must be stable - adding another string must not * change the offset of any that we have already referenced - and so we * simply write the strings in the order that they are passed. */ std::vector strings; /** * The current size of the strings section. */ uint32_t size; public: /** * Default constructor, creates an empty strings table. */ string_table() : size(0) {} /** * Adds a string to the table, returning the offset from the start * where it will be written. If the string is already present, this * will return its existing offset, otherwise it will return a new * offset. */ uint32_t add_string(string str); /** * Writes the strings table to the specified output. */ void write(dtb::output_writer &writer); }; } // namespace dtb } // namespace dtc #endif // !_DTB_HH_ Index: head/usr.bin/indent/indent.1 =================================================================== --- head/usr.bin/indent/indent.1 (revision 289676) +++ head/usr.bin/indent/indent.1 (revision 289677) @@ -1,557 +1,557 @@ .\" Copyright (c) 1980, 1990, 1993 .\" The Regents of the University of California. All rights reserved. .\" Copyright (c) 1976 Board of Trustees of the University of Illinois. .\" 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. .\" .\" @(#)indent.1 8.1 (Berkeley) 7/1/93 .\" $FreeBSD$ .\" .Dd March 3, 2012 .Dt INDENT 1 .Os .Sh NAME .Nm indent .Nd indent and format C program source .Sh SYNOPSIS .Nm .Op Ar input-file Op Ar output-file .Op Fl bacc | Fl nbacc .Op Fl bad | Fl nbad .Op Fl bap | Fl nbap .Bk -words .Op Fl ei | Fl ei .Op Fl eei | Fl eei .Op Fl bbb | Fl nbbb .Ek .Op Fl \&bc | Fl nbc .Op Fl \&bl .Op Fl \&br .Op Fl c Ns Ar n .Op Fl \&cd Ns Ar n .Bk -words .Op Fl cdb | Fl ncdb .Ek .Op Fl \&ce | Fl nce .Op Fl \&ci Ns Ar n .Op Fl cli Ns Ar n .Op Fl d Ns Ar n .Op Fl \&di Ns Ar n .Bk -words .Op Fl fbs | Fl nfbs .Op Fl fc1 | Fl nfc1 .Op Fl fcb | Fl nfcb .Ek .Op Fl i Ns Ar n .Op Fl \&ip | Fl nip .Op Fl l Ns Ar n .Op Fl \&lc Ns Ar n .Op Fl \&ldi Ns Ar n .Op Fl \&lp | Fl nlp .Op Fl npro .Op Fl pcs | Fl npcs .Op Fl psl | Fl npsl .Op Fl \&sc | Fl nsc .Bk -words .Op Fl sob | Fl nsob .Ek .Op Fl \&st .Op Fl \&ta .Op Fl troff .Op Fl ut | Fl nut .Op Fl v | Fl \&nv .Sh DESCRIPTION The .Nm utility is a .Em C program formatter. It reformats the .Em C program in the .Ar input-file according to the switches. The switches which can be specified are described below. They may appear before or after the file names. .Pp .Sy NOTE : If you only specify an .Ar input-file , the formatting is done `in-place', that is, the formatted file is written back into .Ar input-file and a backup copy of .Ar input-file is written in the current directory. If .Ar input-file is named .Sq Pa /blah/blah/file , the backup file is named .Sq Pa file.BAK . .Pp If .Ar output-file is specified, .Nm checks to make sure that it is different from .Ar input-file . .Pp The options listed below control the formatting style imposed by .Nm . .Bl -tag -width Op .It Fl bacc , nbacc If .Fl bacc is specified, a blank line is forced around every conditional compilation block. For example, in front of every #ifdef and after every #endif. Other blank lines surrounding such blocks will be swallowed. Default: .Fl nbacc . .It Fl bad , nbad If .Fl bad is specified, a blank line is forced after every block of declarations. Default: .Fl nbad . .It Fl bap , nbap If .Fl bap is specified, a blank line is forced after every procedure body. Default: .Fl nbap . .It Fl bbb , nbbb If .Fl bbb is specified, a blank line is forced before every block comment. Default: .Fl nbbb . .It Fl \&bc , nbc If .Fl \&bc is specified, then a newline is forced after each comma in a declaration. .Fl nbc turns off this option. Default: .Fl \&nbc . .It Fl \&br , \&bl Specifying .Fl \&bl lines-up compound statements like this: .Bd -literal -offset indent if (...) { code } .Ed .Pp Specifying .Fl \&br (the default) makes them look like this: .Bd -literal -offset indent if (...) { code } .Ed .It Fl c Ns Ar n The column in which comments on code start. The default is 33. .It Fl cd Ns Ar n The column in which comments on declarations start. The default is for these comments to start in the same column as those on code. .It Fl cdb , ncdb Enables (disables) the placement of comment delimiters on blank lines. With this option enabled, comments look like this: .Bd -literal -offset indent /* * this is a comment */ .Ed .Pp Rather than like this: .Bd -literal -offset indent /* this is a comment */ .Ed .Pp This only affects block comments, not comments to the right of code. The default is .Fl cdb . .It Fl ce , nce Enables (disables) forcing of `else's to cuddle up to the immediately preceding `}'. The default is .Fl \&ce . .It Fl \&ci Ns Ar n Sets the continuation indent to be .Ar n . Continuation lines will be indented that far from the beginning of the first line of the statement. Parenthesized expressions have extra indentation added to indicate the nesting, unless .Fl \&lp is in effect or the continuation indent is exactly half of the main indent. .Fl \&ci defaults to the same value as .Fl i . .It Fl cli Ns Ar n Causes case labels to be indented .Ar n tab stops to the right of the containing .Ic switch statement. .Fl cli0.5 causes case labels to be indented half a tab stop. The default is .Fl cli0 . .It Fl d Ns Ar n Controls the placement of comments which are not to the right of code. For example, .Fl \&d\&1 means that such comments are placed one indentation level to the left of code. Specifying the default .Fl \&d\&0 lines-up these comments with the code. See the section on comment indentation below. .It Fl \&di Ns Ar n Specifies the indentation, in character positions, of global variable names and all struct/union member names relative to the beginning of their type declaration. The default is .Fl di16 . .It Fl dj , ndj .Fl \&dj left justifies declarations. .Fl ndj indents declarations the same as code. The default is .Fl ndj . .It Fl \&ei , nei Enables (disables) special .Ic else-if processing. If it is enabled, an .Ic if following an .Ic else will have the same indentation as the preceding .Ic \&if statement. The default is .Fl ei . .It Fl eei , neei Enables (disables) extra indentation on continuation lines of the expression part of .Ic if and .Ic while statements. These continuation lines will be indented one extra level. The default is .Fl neei . .It Fl fbs , nfbs Enables (disables) splitting the function declaration and opening brace across two lines. The default is .Fl fbs . .It Fl fc1 , nfc1 Enables (disables) the formatting of comments that start in column 1. Often, comments whose leading `/' is in column 1 have been carefully hand formatted by the programmer. In such cases, .Fl nfc1 should be used. The default is .Fl fc1 . .It Fl fcb , nfcb Enables (disables) the formatting of block comments (ones that begin with `/*\\n'). Often, block comments have been not so carefully hand formatted by the programmer, but reformatting that would just change the line breaks is not wanted. In such cases, .Fl nfcb should be used. Block comments are then handled like box comments. The default is .Fl fcb . .It Fl i Ns Ar n The number of spaces for one indentation level. The default is 8. .It Fl \&ip , nip Enables (disables) the indentation of parameter declarations from the left margin. The default is .Fl \&ip . .It Fl l Ns Ar n Maximum length of an output line. The default is 78. .It Fl \&ldi Ns Ar n Specifies the indentation, in character positions, of local variable names relative to the beginning of their type declaration. The default is for local variable names to be indented by the same amount as global ones. .It Fl \&lp , nlp Lines-up code surrounded by parenthesis in continuation lines. If a line has a left paren which is not closed on that line, then continuation lines will be lined up to start at the character position just after the left paren. For example, here is how a piece of continued code looks with .Fl nlp in effect: .Bd -literal -offset indent p1 = first_procedure(second_procedure(p2, p3), \ \ third_procedure(p4, p5)); .Ed .Pp With .Fl lp in effect (the default) the code looks somewhat clearer: .Bd -literal -offset indent p1\ =\ first_procedure(second_procedure(p2,\ p3), \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ third_procedure(p4,\ p5)); .Ed .Pp Inserting two more newlines we get: .Bd -literal -offset indent p1\ =\ first_procedure(second_procedure(p2, \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ p3), \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ third_procedure(p4, \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ p5)); .Ed .It Fl npro Causes the profile files, .Sq Pa ./.indent.pro and .Sq Pa ~/.indent.pro , to be ignored. .It Fl pcs , npcs If true .Pq Fl pcs all procedure calls will have a space inserted between the name and the `('. The default is .Fl npcs . .It Fl psl , npsl If true .Pq Fl psl the names of procedures being defined are placed in column 1 \- their types, if any, will be left on the previous lines. The default is .Fl psl . .It Fl \&sc , nsc Enables (disables) the placement of asterisks (`*'s) at the left edge of all comments. The default is .Fl sc . .It Fl sob , nsob If .Fl sob is specified, indent will swallow optional blank lines. You can use this to get rid of blank lines after declarations. Default: .Fl nsob . .It Fl \&st Causes .Nm to take its input from stdin and put its output to stdout. .It Fl ta Automatically add all identifiers ending in "_t" to the list of type keywords. .It Fl T Ns Ar typename Adds .Ar typename to the list of type keywords. Names accumulate: .Fl T can be specified more than once. You need to specify all the typenames that appear in your program that are defined by .Ic typedef \- nothing will be harmed if you miss a few, but the program will not be formatted as nicely as it should. This sounds like a painful thing to have to do, but it is really a symptom of a problem in C: .Ic typedef causes a syntactic change in the language and .Nm cannot find all instances of .Ic typedef . .It Fl troff Causes .Nm to format the program for processing by .Xr troff 1 . It will produce a fancy listing in much the same spirit as .Xr vgrind 1 . If the output file is not specified, the default is standard output, rather than formatting in place. .It Fl ut , nut Enables (disables) the use of tab characters in the output. Tabs are assumed to be aligned on columns divisible by 8. The default is .Fl ut . .It Fl v , \&nv .Fl v turns on `verbose' mode; .Fl \&nv turns it off. When in verbose mode, .Nm reports when it splits one line of input into two or more lines of output, and gives some size statistics at completion. The default is .Fl \&nv . .El .Pp You may set up your own `profile' of defaults to .Nm by creating a file called .Pa .indent.pro in your login directory and/or the current directory and including whatever switches you like. A `.indent.pro' in the current directory takes precedence over the one in your login directory. If .Nm is run and a profile file exists, then it is read to set up the program's defaults. Switches on the command line, though, always override profile switches. The switches should be separated by spaces, tabs or newlines. .Pp .Ss Comments .Sq Em Box .Em comments . The .Nm utility assumes that any comment with a dash or star immediately after the start of comment (that is, `/*\-' or `/**') is a comment surrounded by a box of stars. Each line of such a comment is left unchanged, except that its indentation may be adjusted to account for the change in indentation of the first line of the comment. .Pp .Em Straight text . All other comments are treated as straight text. The .Nm utility fits as many words (separated by blanks, tabs, or newlines) on a line as possible. Blank lines break paragraphs. .Ss Comment indentation If a comment is on a line with code it is started in the `comment column', which is set by the .Fl c Ns Ns Ar n command line parameter. Otherwise, the comment is started at .Ar n indentation levels less than where code is currently being placed, where .Ar n is specified by the .Fl d Ns Ns Ar n command line parameter. If the code on a line extends past the comment column, the comment starts further to the right, and the right margin may be automatically extended in extreme cases. .Ss Preprocessor lines In general, .Nm leaves preprocessor lines alone. The only reformatting that it will do is to straighten up trailing comments. It leaves embedded comments alone. Conditional compilation .Pq Ic #ifdef...#endif is recognized and .Nm attempts to correctly compensate for the syntactic peculiarities introduced. .Ss C syntax The .Nm utility understands a substantial amount about the syntax of C, but it has a `forgiving' parser. It attempts to cope with the usual sorts of -incomplete and misformed syntax. +incomplete and malformed syntax. In particular, the use of macros like: .Pp .Dl #define forever for(;;) .Pp is handled properly. .Sh ENVIRONMENT The .Nm utility uses the .Ev HOME environment variable. .Sh FILES .Bl -tag -width "./.indent.pro" -compact .It Pa ./.indent.pro profile file .It Pa ~/.indent.pro profile file .El .Sh HISTORY The .Nm command appeared in .Bx 4.2 . .Sh BUGS The .Nm utility has even more switches than .Xr ls 1 . .Pp A common mistake is to try to indent all the .Em C programs in a directory by typing: .Pp .Dl indent *.c .Pp This is probably a bug, not a feature. Index: head/usr.bin/locale/locale.c =================================================================== --- head/usr.bin/locale/locale.c (revision 289676) +++ head/usr.bin/locale/locale.c (revision 289677) @@ -1,688 +1,688 @@ /*- * Copyright (c) 2002, 2003 Alexey Zelkin * 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. * * $FreeBSD$ */ /* * XXX: implement missing era_* (LC_TIME) keywords (require libc & * nl_langinfo(3) extensions) * * XXX: correctly handle reserved 'charmap' keyword and '-m' option (require * localedef(1) implementation). Currently it's handled via * nl_langinfo(CODESET). */ #include #include #include #include #include #include #include #include #include #include #include "setlocale.h" /* Local prototypes */ void init_locales_list(void); void list_charmaps(void); void list_locales(void); const char *lookup_localecat(int); char *kwval_lconv(int); int kwval_lookup(char *, char **, int *, int *); void showdetails(char *); void showkeywordslist(char *substring); void showlocale(void); void usage(void); /* Global variables */ static StringList *locales = NULL; int all_locales = 0; int all_charmaps = 0; int prt_categories = 0; int prt_keywords = 0; int more_params = 0; struct _lcinfo { const char *name; int id; } lcinfo [] = { { "LC_CTYPE", LC_CTYPE }, { "LC_COLLATE", LC_COLLATE }, { "LC_TIME", LC_TIME }, { "LC_NUMERIC", LC_NUMERIC }, { "LC_MONETARY", LC_MONETARY }, { "LC_MESSAGES", LC_MESSAGES } }; #define NLCINFO (sizeof(lcinfo)/sizeof(lcinfo[0])) /* ids for values not referenced by nl_langinfo() */ #define KW_ZERO 10000 #define KW_GROUPING (KW_ZERO+1) #define KW_INT_CURR_SYMBOL (KW_ZERO+2) #define KW_CURRENCY_SYMBOL (KW_ZERO+3) #define KW_MON_DECIMAL_POINT (KW_ZERO+4) #define KW_MON_THOUSANDS_SEP (KW_ZERO+5) #define KW_MON_GROUPING (KW_ZERO+6) #define KW_POSITIVE_SIGN (KW_ZERO+7) #define KW_NEGATIVE_SIGN (KW_ZERO+8) #define KW_INT_FRAC_DIGITS (KW_ZERO+9) #define KW_FRAC_DIGITS (KW_ZERO+10) #define KW_P_CS_PRECEDES (KW_ZERO+11) #define KW_P_SEP_BY_SPACE (KW_ZERO+12) #define KW_N_CS_PRECEDES (KW_ZERO+13) #define KW_N_SEP_BY_SPACE (KW_ZERO+14) #define KW_P_SIGN_POSN (KW_ZERO+15) #define KW_N_SIGN_POSN (KW_ZERO+16) #define KW_INT_P_CS_PRECEDES (KW_ZERO+17) #define KW_INT_P_SEP_BY_SPACE (KW_ZERO+18) #define KW_INT_N_CS_PRECEDES (KW_ZERO+19) #define KW_INT_N_SEP_BY_SPACE (KW_ZERO+20) #define KW_INT_P_SIGN_POSN (KW_ZERO+21) #define KW_INT_N_SIGN_POSN (KW_ZERO+22) struct _kwinfo { const char *name; int isstr; /* true - string, false - number */ int catid; /* LC_* */ int value_ref; const char *comment; } kwinfo [] = { { "charmap", 1, LC_CTYPE, CODESET, "" }, /* hack */ { "decimal_point", 1, LC_NUMERIC, RADIXCHAR, "" }, { "thousands_sep", 1, LC_NUMERIC, THOUSEP, "" }, { "grouping", 1, LC_NUMERIC, KW_GROUPING, "" }, { "radixchar", 1, LC_NUMERIC, RADIXCHAR, "Same as decimal_point (FreeBSD only)" }, /* compat */ { "thousep", 1, LC_NUMERIC, THOUSEP, "Same as thousands_sep (FreeBSD only)" }, /* compat */ { "int_curr_symbol", 1, LC_MONETARY, KW_INT_CURR_SYMBOL, "" }, { "currency_symbol", 1, LC_MONETARY, KW_CURRENCY_SYMBOL, "" }, { "mon_decimal_point", 1, LC_MONETARY, KW_MON_DECIMAL_POINT, "" }, { "mon_thousands_sep", 1, LC_MONETARY, KW_MON_THOUSANDS_SEP, "" }, { "mon_grouping", 1, LC_MONETARY, KW_MON_GROUPING, "" }, { "positive_sign", 1, LC_MONETARY, KW_POSITIVE_SIGN, "" }, { "negative_sign", 1, LC_MONETARY, KW_NEGATIVE_SIGN, "" }, { "int_frac_digits", 0, LC_MONETARY, KW_INT_FRAC_DIGITS, "" }, { "frac_digits", 0, LC_MONETARY, KW_FRAC_DIGITS, "" }, { "p_cs_precedes", 0, LC_MONETARY, KW_P_CS_PRECEDES, "" }, { "p_sep_by_space", 0, LC_MONETARY, KW_P_SEP_BY_SPACE, "" }, { "n_cs_precedes", 0, LC_MONETARY, KW_N_CS_PRECEDES, "" }, { "n_sep_by_space", 0, LC_MONETARY, KW_N_SEP_BY_SPACE, "" }, { "p_sign_posn", 0, LC_MONETARY, KW_P_SIGN_POSN, "" }, { "n_sign_posn", 0, LC_MONETARY, KW_N_SIGN_POSN, "" }, { "int_p_cs_precedes", 0, LC_MONETARY, KW_INT_P_CS_PRECEDES, "" }, { "int_p_sep_by_space", 0, LC_MONETARY, KW_INT_P_SEP_BY_SPACE, "" }, { "int_n_cs_precedes", 0, LC_MONETARY, KW_INT_N_CS_PRECEDES, "" }, { "int_n_sep_by_space", 0, LC_MONETARY, KW_INT_N_SEP_BY_SPACE, "" }, { "int_p_sign_posn", 0, LC_MONETARY, KW_INT_P_SIGN_POSN, "" }, { "int_n_sign_posn", 0, LC_MONETARY, KW_INT_N_SIGN_POSN, "" }, { "d_t_fmt", 1, LC_TIME, D_T_FMT, "" }, { "d_fmt", 1, LC_TIME, D_FMT, "" }, { "t_fmt", 1, LC_TIME, T_FMT, "" }, { "am_str", 1, LC_TIME, AM_STR, "" }, { "pm_str", 1, LC_TIME, PM_STR, "" }, { "t_fmt_ampm", 1, LC_TIME, T_FMT_AMPM, "" }, { "day_1", 1, LC_TIME, DAY_1, "" }, { "day_2", 1, LC_TIME, DAY_2, "" }, { "day_3", 1, LC_TIME, DAY_3, "" }, { "day_4", 1, LC_TIME, DAY_4, "" }, { "day_5", 1, LC_TIME, DAY_5, "" }, { "day_6", 1, LC_TIME, DAY_6, "" }, { "day_7", 1, LC_TIME, DAY_7, "" }, { "abday_1", 1, LC_TIME, ABDAY_1, "" }, { "abday_2", 1, LC_TIME, ABDAY_2, "" }, { "abday_3", 1, LC_TIME, ABDAY_3, "" }, { "abday_4", 1, LC_TIME, ABDAY_4, "" }, { "abday_5", 1, LC_TIME, ABDAY_5, "" }, { "abday_6", 1, LC_TIME, ABDAY_6, "" }, { "abday_7", 1, LC_TIME, ABDAY_7, "" }, { "mon_1", 1, LC_TIME, MON_1, "" }, { "mon_2", 1, LC_TIME, MON_2, "" }, { "mon_3", 1, LC_TIME, MON_3, "" }, { "mon_4", 1, LC_TIME, MON_4, "" }, { "mon_5", 1, LC_TIME, MON_5, "" }, { "mon_6", 1, LC_TIME, MON_6, "" }, { "mon_7", 1, LC_TIME, MON_7, "" }, { "mon_8", 1, LC_TIME, MON_8, "" }, { "mon_9", 1, LC_TIME, MON_9, "" }, { "mon_10", 1, LC_TIME, MON_10, "" }, { "mon_11", 1, LC_TIME, MON_11, "" }, { "mon_12", 1, LC_TIME, MON_12, "" }, { "abmon_1", 1, LC_TIME, ABMON_1, "" }, { "abmon_2", 1, LC_TIME, ABMON_2, "" }, { "abmon_3", 1, LC_TIME, ABMON_3, "" }, { "abmon_4", 1, LC_TIME, ABMON_4, "" }, { "abmon_5", 1, LC_TIME, ABMON_5, "" }, { "abmon_6", 1, LC_TIME, ABMON_6, "" }, { "abmon_7", 1, LC_TIME, ABMON_7, "" }, { "abmon_8", 1, LC_TIME, ABMON_8, "" }, { "abmon_9", 1, LC_TIME, ABMON_9, "" }, { "abmon_10", 1, LC_TIME, ABMON_10, "" }, { "abmon_11", 1, LC_TIME, ABMON_11, "" }, { "abmon_12", 1, LC_TIME, ABMON_12, "" }, { "altmon_1", 1, LC_TIME, ALTMON_1, "(FreeBSD only)" }, { "altmon_2", 1, LC_TIME, ALTMON_2, "(FreeBSD only)" }, { "altmon_3", 1, LC_TIME, ALTMON_3, "(FreeBSD only)" }, { "altmon_4", 1, LC_TIME, ALTMON_4, "(FreeBSD only)" }, { "altmon_5", 1, LC_TIME, ALTMON_5, "(FreeBSD only)" }, { "altmon_6", 1, LC_TIME, ALTMON_6, "(FreeBSD only)" }, { "altmon_7", 1, LC_TIME, ALTMON_7, "(FreeBSD only)" }, { "altmon_8", 1, LC_TIME, ALTMON_8, "(FreeBSD only)" }, { "altmon_9", 1, LC_TIME, ALTMON_9, "(FreeBSD only)" }, { "altmon_10", 1, LC_TIME, ALTMON_10, "(FreeBSD only)" }, { "altmon_11", 1, LC_TIME, ALTMON_11, "(FreeBSD only)" }, { "altmon_12", 1, LC_TIME, ALTMON_12, "(FreeBSD only)" }, { "era", 1, LC_TIME, ERA, "(unavailable)" }, { "era_d_fmt", 1, LC_TIME, ERA_D_FMT, "(unavailable)" }, { "era_d_t_fmt", 1, LC_TIME, ERA_D_T_FMT, "(unavailable)" }, { "era_t_fmt", 1, LC_TIME, ERA_T_FMT, "(unavailable)" }, { "alt_digits", 1, LC_TIME, ALT_DIGITS, "" }, { "d_md_order", 1, LC_TIME, D_MD_ORDER, "(FreeBSD only)" }, /* local */ { "yesexpr", 1, LC_MESSAGES, YESEXPR, "" }, { "noexpr", 1, LC_MESSAGES, NOEXPR, "" }, { "yesstr", 1, LC_MESSAGES, YESSTR, "(POSIX legacy)" }, /* compat */ { "nostr", 1, LC_MESSAGES, NOSTR, "(POSIX legacy)" } /* compat */ }; #define NKWINFO (sizeof(kwinfo)/sizeof(kwinfo[0])) const char *boguslocales[] = { "UTF-8" }; #define NBOGUS (sizeof(boguslocales)/sizeof(boguslocales[0])) int main(int argc, char *argv[]) { int ch; int tmp; while ((ch = getopt(argc, argv, "ackms:")) != -1) { switch (ch) { case 'a': all_locales = 1; break; case 'c': prt_categories = 1; break; case 'k': prt_keywords = 1; break; case 'm': all_charmaps = 1; break; default: usage(); } } argc -= optind; argv += optind; /* validate arguments */ if (all_locales && all_charmaps) usage(); if ((all_locales || all_charmaps) && argc > 0) usage(); if ((all_locales || all_charmaps) && (prt_categories || prt_keywords)) usage(); /* process '-a' */ if (all_locales) { list_locales(); exit(0); } /* process '-m' */ if (all_charmaps) { list_charmaps(); exit(0); } /* check for special case '-k list' */ tmp = 0; if (prt_keywords && argc > 0) while (tmp < argc) if (strcasecmp(argv[tmp++], "list") == 0) { showkeywordslist(argv[tmp]); exit(0); } /* process '-c', '-k', or command line arguments. */ if (prt_categories || prt_keywords || argc > 0) { if (argc > 0) { setlocale(LC_ALL, ""); while (argc > 0) { showdetails(*argv); argv++; argc--; } } else { uint i; for (i = 0; i < sizeof (kwinfo) / sizeof (struct _kwinfo); i++) showdetails ((char *)kwinfo [i].name); } exit(0); } /* no arguments, show current locale state */ showlocale(); return (0); } void usage(void) { printf("Usage: locale [ -a | -m ]\n" " locale -k list [prefix]\n" " locale [ -ck ] [keyword ...]\n"); exit(1); } /* * Output information about all available locales * * XXX actually output of this function does not guarantee that locale * is really available to application, since it can be broken or * inconsistent thus setlocale() will fail. Maybe add '-V' function to * also validate these locales? */ void list_locales(void) { size_t i; init_locales_list(); for (i = 0; i < locales->sl_cur; i++) { printf("%s\n", locales->sl_str[i]); } } /* * qsort() helper function */ static int scmp(const void *s1, const void *s2) { return strcmp(*(const char **)s1, *(const char **)s2); } /* * Output information about all available charmaps * * XXX this function is doing a task in hackish way, i.e. by scaning * list of locales, spliting their codeset part and building list of * them. */ void list_charmaps(void) { size_t i; char *s, *cs; StringList *charmaps; /* initialize StringList */ charmaps = sl_init(); if (charmaps == NULL) err(1, "could not allocate memory"); /* fetch locales list */ init_locales_list(); /* split codesets and build their list */ for (i = 0; i < locales->sl_cur; i++) { s = locales->sl_str[i]; if ((cs = strchr(s, '.')) != NULL) { cs++; if (sl_find(charmaps, cs) == NULL) sl_add(charmaps, cs); } } /* add US-ASCII, if not yet added */ if (sl_find(charmaps, "US-ASCII") == NULL) sl_add(charmaps, "US-ASCII"); /* sort the list */ qsort(charmaps->sl_str, charmaps->sl_cur, sizeof(char *), scmp); /* print results */ for (i = 0; i < charmaps->sl_cur; i++) { printf("%s\n", charmaps->sl_str[i]); } } /* * Retrieve sorted list of system locales (or user locales, if PATH_LOCALE * environment variable is set) */ void init_locales_list(void) { DIR *dirp; struct dirent *dp; size_t i; int bogus; /* why call this function twice ? */ if (locales != NULL) return; /* initialize StringList */ locales = sl_init(); if (locales == NULL) err(1, "could not allocate memory"); /* get actual locales directory name */ if (__detect_path_locale() != 0) err(1, "unable to find locales storage"); /* open locales directory */ dirp = opendir(_PathLocale); if (dirp == NULL) err(1, "could not open directory '%s'", _PathLocale); /* scan directory and store its contents except "." and ".." */ while ((dp = readdir(dirp)) != NULL) { if (*(dp->d_name) == '.') continue; /* exclude "." and ".." */ for (bogus = i = 0; i < NBOGUS; i++) if (strncmp(dp->d_name, boguslocales[i], strlen(boguslocales[i])) == 0) bogus = 1; if (!bogus) sl_add(locales, strdup(dp->d_name)); } closedir(dirp); /* make sure that 'POSIX' and 'C' locales are present in the list. * POSIX 1003.1-2001 requires presence of 'POSIX' name only here, but * we also list 'C' for constistency */ if (sl_find(locales, "POSIX") == NULL) sl_add(locales, "POSIX"); if (sl_find(locales, "C") == NULL) sl_add(locales, "C"); /* make output nicer, sort the list */ qsort(locales->sl_str, locales->sl_cur, sizeof(char *), scmp); } /* * Show current locale status, depending on environment variables */ void showlocale(void) { size_t i; const char *lang, *vval, *eval; setlocale(LC_ALL, ""); lang = getenv("LANG"); if (lang == NULL) { lang = ""; } printf("LANG=%s\n", lang); /* XXX: if LANG is null, then set it to "C" to get implied values? */ for (i = 0; i < NLCINFO; i++) { vval = setlocale(lcinfo[i].id, NULL); eval = getenv(lcinfo[i].name); if (eval != NULL && !strcmp(eval, vval) && strcmp(lang, vval)) { /* * Appropriate environment variable set, its value - * is valid and not overriden by LC_ALL + * is valid and not overridden by LC_ALL * * XXX: possible side effect: if both LANG and - * overriden environment variable are set into same + * overridden environment variable are set into same * value, then it'll be assumed as 'implied' */ printf("%s=%s\n", lcinfo[i].name, vval); } else { printf("%s=\"%s\"\n", lcinfo[i].name, vval); } } vval = getenv("LC_ALL"); if (vval == NULL) { vval = ""; } printf("LC_ALL=%s\n", vval); } /* * keyword value lookup helper (via localeconv()) */ char * kwval_lconv(int id) { struct lconv *lc; char *rval; rval = NULL; lc = localeconv(); switch (id) { case KW_GROUPING: rval = lc->grouping; break; case KW_INT_CURR_SYMBOL: rval = lc->int_curr_symbol; break; case KW_CURRENCY_SYMBOL: rval = lc->currency_symbol; break; case KW_MON_DECIMAL_POINT: rval = lc->mon_decimal_point; break; case KW_MON_THOUSANDS_SEP: rval = lc->mon_thousands_sep; break; case KW_MON_GROUPING: rval = lc->mon_grouping; break; case KW_POSITIVE_SIGN: rval = lc->positive_sign; break; case KW_NEGATIVE_SIGN: rval = lc->negative_sign; break; case KW_INT_FRAC_DIGITS: rval = &(lc->int_frac_digits); break; case KW_FRAC_DIGITS: rval = &(lc->frac_digits); break; case KW_P_CS_PRECEDES: rval = &(lc->p_cs_precedes); break; case KW_P_SEP_BY_SPACE: rval = &(lc->p_sep_by_space); break; case KW_N_CS_PRECEDES: rval = &(lc->n_cs_precedes); break; case KW_N_SEP_BY_SPACE: rval = &(lc->n_sep_by_space); break; case KW_P_SIGN_POSN: rval = &(lc->p_sign_posn); break; case KW_N_SIGN_POSN: rval = &(lc->n_sign_posn); break; case KW_INT_P_CS_PRECEDES: rval = &(lc->int_p_cs_precedes); break; case KW_INT_P_SEP_BY_SPACE: rval = &(lc->int_p_sep_by_space); break; case KW_INT_N_CS_PRECEDES: rval = &(lc->int_n_cs_precedes); break; case KW_INT_N_SEP_BY_SPACE: rval = &(lc->int_n_sep_by_space); break; case KW_INT_P_SIGN_POSN: rval = &(lc->int_p_sign_posn); break; case KW_INT_N_SIGN_POSN: rval = &(lc->int_n_sign_posn); break; default: break; } return (rval); } /* * keyword value and properties lookup */ int kwval_lookup(char *kwname, char **kwval, int *cat, int *isstr) { int rval; size_t i; rval = 0; for (i = 0; i < NKWINFO; i++) { if (strcasecmp(kwname, kwinfo[i].name) == 0) { rval = 1; *cat = kwinfo[i].catid; *isstr = kwinfo[i].isstr; if (kwinfo[i].value_ref < KW_ZERO) { *kwval = nl_langinfo(kwinfo[i].value_ref); } else { *kwval = kwval_lconv(kwinfo[i].value_ref); } break; } } return (rval); } /* * Show details about requested keyword according to '-k' and/or '-c' * command line options specified. */ void showdetails(char *kw) { int isstr, cat, tmpval; char *kwval; if (kwval_lookup(kw, &kwval, &cat, &isstr) == 0) { /* * invalid keyword specified. * XXX: any actions? */ fprintf(stderr, "Unknown keyword: `%s'\n", kw); return; } if (prt_categories) { if (prt_keywords) printf("%-20s ", lookup_localecat(cat)); else printf("%-20s\t%s\n", kw, lookup_localecat(cat)); } if (prt_keywords) { if (isstr) { printf("%s=\"%s\"\n", kw, kwval); } else { tmpval = (char) *kwval; printf("%s=%d\n", kw, tmpval); } } if (!prt_categories && !prt_keywords) { if (isstr) { printf("%s\n", kwval); } else { tmpval = (char) *kwval; printf("%d\n", tmpval); } } } /* * Convert locale category id into string */ const char * lookup_localecat(int cat) { size_t i; for (i = 0; i < NLCINFO; i++) if (lcinfo[i].id == cat) { return (lcinfo[i].name); } return ("UNKNOWN"); } /* * Show list of keywords */ void showkeywordslist(char *substring) { size_t i; #define FMT "%-20s %-12s %-7s %-20s\n" if (substring == NULL) printf("List of available keywords\n\n"); else printf("List of available keywords starting with '%s'\n\n", substring); printf(FMT, "Keyword", "Category", "Type", "Comment"); printf("-------------------- ------------ ------- --------------------\n"); for (i = 0; i < NKWINFO; i++) { if (substring != NULL) { if (strncmp(kwinfo[i].name, substring, strlen(substring)) != 0) continue; } printf(FMT, kwinfo[i].name, lookup_localecat(kwinfo[i].catid), (kwinfo[i].isstr == 0) ? "number" : "string", kwinfo[i].comment); } } Index: head/usr.bin/look/look.1 =================================================================== --- head/usr.bin/look/look.1 (revision 289676) +++ head/usr.bin/look/look.1 (revision 289677) @@ -1,126 +1,126 @@ .\" Copyright (c) 1990, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 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. .\" .\" @(#)look.1 8.1 (Berkeley) 6/14/93 .\" $FreeBSD$ .\" .Dd July 17, 2004 .Dt LOOK 1 .Os .Sh NAME .Nm look .Nd display lines beginning with a given string .Sh SYNOPSIS .Nm .Op Fl df .Op Fl t Ar termchar .Ar string .Op Ar .Sh DESCRIPTION The .Nm utility displays any lines in .Ar file which contain .Ar string as a prefix. As .Nm performs a binary search, the lines in .Ar file must be sorted. .Pp If .Ar file is not specified, the file .Pa /usr/share/dict/words is used, only alphanumeric characters are compared and the case of alphabetic characters is ignored. .Pp The following options are available: .Bl -tag -width indent .It Fl d , -alphanum Dictionary character set and order, i.e., only alphanumeric characters are compared. .It Fl f , -ignore-case Ignore the case of alphabetic characters. .It Fl t , -terminate Ar termchar Specify a string termination character, i.e., only the characters in .Ar string up to and including the first occurrence of .Ar termchar are compared. .El .Sh ENVIRONMENT The .Ev LANG , LC_ALL and .Ev LC_CTYPE environment variables affect the execution of the .Nm utility. Their effect is described in .Xr environ 7 . .Sh FILES .Bl -tag -width /usr/share/dict/words -compact .It Pa /usr/share/dict/words the dictionary .El .Sh EXIT STATUS The .Nm utility exits 0 if one or more lines were found and displayed, 1 if no lines were found, and >1 if an error occurred. .Sh COMPATIBILITY The original manual page stated that tabs and blank characters participated in comparisons when the .Fl d option was specified. This was incorrect and the current man page matches the historic implementation. .Pp The .Fl a and .Fl -alternative -flags are ignored for compability. +flags are ignored for compatibility. .Sh SEE ALSO .Xr grep 1 , .Xr sort 1 .Sh HISTORY A .Nm utility appeared in .At v7 . .Sh BUGS Lines are not compared according to the current locale's collating order. Input files must be sorted with .Ev LC_COLLATE set to .Ql C . Index: head/usr.bin/mkimg/mkimg.1 =================================================================== --- head/usr.bin/mkimg/mkimg.1 (revision 289676) +++ head/usr.bin/mkimg/mkimg.1 (revision 289677) @@ -1,331 +1,331 @@ .\" Copyright (c) 2013, 2014 Juniper Networks, Inc. .\" 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 ``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 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. .\" .\" $FreeBSD$ .\" .Dd August 7, 2015 .Dt MKIMG 1 .Os .Sh NAME .Nm mkimg .Nd "utility to make disk images" .Sh SYNOPSIS .Nm .Op Fl H Ar heads .Op Fl P Ar blksz .Op Fl S Ar secsz .Op Fl T Ar tracksz .Op Fl b Ar bootcode .Op Fl c Ar capacity .Op Fl f Ar format .Op Fl o Ar outfile .Op Fl v .Op Fl y .Op Fl s Ar scheme Op Fl p Ar partition ... .Nm .Ar --formats | --schemes | --version .Sh DESCRIPTION The .Nm utility creates a disk image from the raw partition contents specified with the .Ar partition argument(s) and using the partitioning scheme specified with the .Ar scheme argument. The disk image is written to .Ar stdout by default or the file specified with the .Ar outfile argument. The image file is a raw disk image by default, but the format of the image file can be specified with the .Ar format argument. .Pp The disk image can be made bootable by specifying the scheme-specific boot block contents with the .Ar bootcode argument and, depending on the scheme, with a boot partition. The contents of such a boot partition is provided like any other partition and the .Nm utility does not treat it any differently from other partitions. .Pp Some partitioning schemes need a disk geometry and for those the .Nm utility accepts the .Ar tracksz and .Ar heads arguments, specifying the number of sectors per track and the number of heads per cylinder (resp.) .Pp Both the logical and physical sector size can be specified and for that the .Nm utility accepts the .Ar secsz and .Ar blksz arguments. The .Ar secsz argument is used to specify the logical sector size. This is the sector size reported by a disk when queried for its capacity. Modern disks use a larger sector size internally, referred to as block size by the .Nm utility and this can be specified by the .Ar blksz argument. The .Nm utility will use the (physical) block size to determine the start of partitions and to round the size of the disk image. .Pp The .Fl c option can be used to specify a minimal capacity for the disk image. Use this option without the .Fl s and .Fl p options to create an empty disk image with the given (virtual) size. An empty partition table can be written to the disk when specifying a partitioning scheme with the .Fl s option, but without specifying any partitions. When the size required to for all the partitions is larger than the given capacity, then the disk image will be larger than the capacity given. .Pp The .Fl v option increases the level of output that the .Nm utility prints. .Pp The .Fl y option is used for testing purposes only and is not to be used in production. When present, the .Nm utility will generate predictable values for Universally Unique Identifiers (UUIDs) and time stamps so that consecutive runs of the .Nm utility will create images that are identical. .Pp A set of long options exist to query about the .Nm utility itself. Options in this set should be given by themselves because the .Nm utility exits immediately after providing the requested information. The version of the .Nm utility is printed when the .Ar --version option is given. The list of supported output formats is printed when the .Ar --formats option is given and the list of supported partitioning schemes is printed when the .Ar --schemes option is given. Both the format and scheme lists a space-separated lists for easy handling in scripts. .Pp For a more descriptive list of supported partitioning schemes or supported output format, or for a detailed description of how to specify partitions, run the .Nm utility without any arguments. This will print a usage message with all the necessary details. .Sh DISK FORMATS The .Nm utility supports a number of output file formats. A short description of these is given below. .Ss QCOW and QCOW2 QCOW stands for "QEMU Copy On Write". It's a sparse file format akin to VHD and VMDK and QCOW represents the first version. QCOW2 represents version 2 of the file format. Version 2 is not backward compatible with version 1 and adds support for snapshots among other things. The QCOW file formats are natively supported by QEMU and Xen. To write QCOW, specify .Fl f Ar qcow on the command line. To write version 2 QCOW, specify .Fl f Ar qcow2 on the command line. The preferred file extension is ".qcow" and ".qcow2" for QCOW and QCOW2 (resp.), but ".qcow" is sometimes used for version 2 files as well. .Ss RAW file format This file format is a sector by sector representation of an actual disk. There is no extra information that describes or relates to the format itself. The size of the file is the size of the (virtual) disk. This file format is suitable for being copyied onto a disk with utilities like .Nm dd . To write a raw disk file, either omit the .Fl f option, or specify .Fl f Ar raw on the command line. The preferred file extension is one of ".img" or ".raw", but there's no real convention for it. .Ss Dynamic VHD and Fixed VHD Microsoft's "Virtual Hard Disk" file formats. The dynamic format is a sparse format akin to QCOW and VMDK. The fixed format is effectively a raw format with a footer appended to the file and as such it's often indistinguishable from the raw format. The fixed file format has been added to support Microsoft's Azure platform and due to inconsistencies in interpretation of the footer is not compatible with utilities like .Nm qemu when it is specifically instructed to interpreted the file as a VHD file. By default .Nm qemu will treat the file as a raw disk file, which mostly works fine. To have .Nm create a dynamic VHD file, specify .Fl f Ar vhd on the command line. To create a fixed VHD file for use by Azure, specify .Fl f Ar vhdf on the command line. The preferred file extension is ".vhd". .Ss VMDK VMware's "Virtual Machine Disk" file format. It's a sparse file format akin to QCOW and VHD and supported by many virtualization solutions. To create a VMDK file, specify .Fl f Ar vmdk on the command line. The preferred file extension is ".vmdk". .Pp Not all virtualization solutions support all file formats, but often those virtualization environments have utilities to convert from one format to another. Note however that conversion may require that the virtual disk size is changed to match the constraints of the output format and this may invalidate the contents of the disk image. For example, the GUID Partition Table (GPT) scheme has a header in the last sector on the disk. When changing the disk size, the GPT must be changed so that the last header is moved accordingly. This is typically not part of the conversion process. If possible, use an output format specifically for the environment in which the file is intended to be used. .Sh ENVIRONMENT .Bl -tag -width "TMPDIR" -compact .It Ev TMPDIR Directory to put temporary files in; default is .Pa /tmp . .El .Sh EXAMPLES To create a bootable disk image that is partitioned using the GPT scheme and containing a root file system that was previously created using .Xr makefs and also containing a swap partition, run the .Nm utility as follows: .Dl % mkimg -s gpt -b /boot/pmbr -p freebsd-boot:=/boot/gptboot \ -p freebsd-ufs:=root-file-system.ufs -p freebsd-swap::1G \ -o gpt.img .Pp The command line given above results in a raw image file. This is because no output format was given. To create a VMDK image for example, add the .Fl f Ar vmdk argument to the .Nm utility and name the output file accordingly. .Pp A nested partitioning scheme is created by running the .Nm utility twice. The output of the first will be fed as the contents of a partition to the second. This can be done using a temporary file, like so: .Dl % mkimg -s bsd -b /boot/boot -p freebsd-ufs:=root-file-system.ufs \ -p freebsd-swap::1G -o /tmp/bsd.img .Dl % mkimg -s mbr -b /boot/mbr -p freebsd:=/tmp/bsd.img -o mbr-bsd.img .Pp Alternatively, the .Nm utility can be run in a cascaded fashion, whereby the output of the first is fed directly into the second. To do this, run the .Nm utility as follows: .Dl % mkimg -s mbr -b /boot/mbr -p freebsd:-'mkimg -s bsd -b /boot/boot \ -p freebsd-ufs:=root-file-system.ufs -p freebsd-swap::1G' -o mbr-bsd.img .Pp -To accomodate the need to have partitions named or numbered in a certain +To accommodate the need to have partitions named or numbered in a certain way, the .Nm utility allows for the specification of empty partitions. For example, to create an image that is compatible with partition layouts found in .Pa /etc/disktab , the 'd' partition often needs to be skipped. This is accomplished by inserting an unused partition after the first 2 partition specifications. It is worth noting at this time that the BSD scheme will automatically skip the 'c' partition by virtue of it referring to the entire disk. To create an image that is compatible with the qp120at disk, use the .Nm utility as follows: .Dl % mkimg -s bsd -b /boot/boot -p freebsd-ufs:=root-file-system.ufs \ -p freebsd-swap::20M -p- -p- -p- -p- -p freebsd-ufs:=usr-file-system.ufs \ -o bsd.img .Pp For partitioning schemes that feature partition labels, the .Nm utility supports assigning labels to the partitions specified. In the following example the file system partition is labeled as 'backup': .Dl % mkimg -s gpt -p freebsd-ufs/backup:=file-system.ufs -o gpt.img .Sh SEE ALSO .Xr dd 1 , .Xr gpart 8 , .Xr makefs 8 , .Xr mdconfig 8 , .Xr newfs 8 .Sh HISTORY The .Nm utility first appeared in .Fx 10.1 . .Sh AUTHORS The .Nm utility and manpage were written by .An Marcel Moolenaar Aq Mt marcelm@juniper.net . Index: head/usr.bin/mt/mt.c =================================================================== --- head/usr.bin/mt/mt.c (revision 289676) +++ head/usr.bin/mt/mt.c (revision 289677) @@ -1,1597 +1,1597 @@ /* * Copyright (c) 1980, 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. * 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. */ /*- * Copyright (c) 2013, 2014, 2015 Spectra Logic Corporation * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. * * Authors: Ken Merry (Spectra Logic Corporation) */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)mt.c 8.2 (Berkeley) 5/4/95"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); /* * mt -- * magnetic tape manipulation program */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* the appropriate sections of are also #ifdef'd for FreeBSD */ /* c_flags */ #define NEED_2ARGS 0x01 #define ZERO_ALLOWED 0x02 #define IS_DENSITY 0x04 #define DISABLE_THIS 0x08 #define IS_COMP 0x10 #define USE_GETOPT 0x20 #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #ifndef MAX #define MAX(a, b) (a > b) ? a : b #endif #define MT_PLURAL(a) (a == 1) ? "" : "s" typedef enum { MT_CMD_NONE = MTLOAD + 1, MT_CMD_PROTECT, MT_CMD_GETDENSITY } mt_commands; static const struct commands { const char *c_name; unsigned long c_code; int c_ronly; int c_flags; } com[] = { { "bsf", MTBSF, 1, 0 }, { "bsr", MTBSR, 1, 0 }, /* XXX FreeBSD considered "eof" dangerous, since it's being confused with "eom" (and is an alias for "weof" anyway) */ { "eof", MTWEOF, 0, DISABLE_THIS }, { "fsf", MTFSF, 1, 0 }, { "fsr", MTFSR, 1, 0 }, { "offline", MTOFFL, 1, 0 }, { "load", MTLOAD, 1, 0 }, { "rewind", MTREW, 1, 0 }, { "rewoffl", MTOFFL, 1, 0 }, { "ostatus", MTNOP, 1, 0 }, { "weof", MTWEOF, 0, ZERO_ALLOWED }, { "weofi", MTWEOFI, 0, ZERO_ALLOWED }, { "erase", MTERASE, 0, ZERO_ALLOWED}, { "blocksize", MTSETBSIZ, 0, NEED_2ARGS|ZERO_ALLOWED }, { "density", MTSETDNSTY, 0, NEED_2ARGS|ZERO_ALLOWED|IS_DENSITY }, { "eom", MTEOD, 1, 0 }, { "eod", MTEOD, 1, 0 }, { "smk", MTWSS, 0, 0 }, { "wss", MTWSS, 0, 0 }, { "fss", MTFSS, 1, 0 }, { "bss", MTBSS, 1, 0 }, { "comp", MTCOMP, 0, NEED_2ARGS|ZERO_ALLOWED|IS_COMP }, { "retension", MTRETENS, 1, 0 }, { "rdhpos", MTIOCRDHPOS, 0, 0 }, { "rdspos", MTIOCRDSPOS, 0, 0 }, { "sethpos", MTIOCHLOCATE, 0, NEED_2ARGS|ZERO_ALLOWED }, { "setspos", MTIOCSLOCATE, 0, NEED_2ARGS|ZERO_ALLOWED }, { "errstat", MTIOCERRSTAT, 0, 0 }, { "setmodel", MTIOCSETEOTMODEL, 0, NEED_2ARGS|ZERO_ALLOWED }, { "seteotmodel", MTIOCSETEOTMODEL, 0, NEED_2ARGS|ZERO_ALLOWED }, { "getmodel", MTIOCGETEOTMODEL, 0, 0 }, { "geteotmodel", MTIOCGETEOTMODEL, 0, 0 }, { "rblim", MTIOCRBLIM, 0, 0}, { "getdensity", MT_CMD_GETDENSITY, 0, USE_GETOPT}, { "status", MTIOCEXTGET, 0, USE_GETOPT }, { "locate", MTIOCEXTLOCATE, 0, USE_GETOPT }, { "param", MTIOCPARAMGET, 0, USE_GETOPT }, { "protect", MT_CMD_PROTECT, 0, USE_GETOPT }, { NULL, 0, 0, 0 } }; static const char *getblksiz(int); static void printreg(const char *, u_int, const char *); static void status(struct mtget *); static void usage(void); const char *get_driver_state_str(int dsreg); static void st_status (struct mtget *); static int mt_locate(int argc, char **argv, int mtfd, const char *tape); static int nstatus_print(int argc, char **argv, char *xml_str, struct mt_status_data *status_data); static int mt_xml_cmd(unsigned long cmd, int argc, char **argv, int mtfd, const char *tape); static int mt_print_density_entry(struct mt_status_entry *density_root, int indent); static int mt_print_density_report(struct mt_status_entry *report_root, int indent); static int mt_print_density(struct mt_status_entry *density_root, int indent); static int mt_getdensity(int argc, char **argv, char *xml_str, struct mt_status_data *status_data); static int mt_set_param(int mtfd, struct mt_status_data *status_data, char *param_name, char *param_value); static int mt_protect(int argc, char **argv, int mtfd, struct mt_status_data *status_data); static int mt_param(int argc, char **argv, int mtfd, char *xml_str, struct mt_status_data *status_data); static const char *denstostring (int d); static u_int32_t stringtocomp(const char *s); static const char *comptostring(u_int32_t comp); static void warn_eof(void); int main(int argc, char *argv[]) { const struct commands *comp; struct mtget mt_status; struct mtop mt_com; int ch, len, mtfd; const char *p, *tape; bzero(&mt_com, sizeof(mt_com)); if ((tape = getenv("TAPE")) == NULL) tape = DEFTAPE; while ((ch = getopt(argc, argv, "f:t:")) != -1) switch(ch) { case 'f': case 't': tape = optarg; break; case '?': usage(); break; default: break; } argc -= optind; argv += optind; if (argc < 1) usage(); len = strlen(p = *argv++); for (comp = com;; comp++) { if (comp->c_name == NULL) errx(1, "%s: unknown command", p); if (strncmp(p, comp->c_name, len) == 0) break; } if((comp->c_flags & NEED_2ARGS) && argc != 2) usage(); if(comp->c_flags & DISABLE_THIS) { warn_eof(); } if (comp->c_flags & USE_GETOPT) { argc--; optind = 0; } if ((mtfd = open(tape, comp->c_ronly ? O_RDONLY : O_RDWR)) < 0) err(1, "%s", tape); if (comp->c_code != MTNOP) { mt_com.mt_op = comp->c_code; if (*argv) { if (!isdigit(**argv) && (comp->c_flags & IS_DENSITY)) { const char *dcanon; mt_com.mt_count = mt_density_num(*argv); if (mt_com.mt_count == 0) errx(1, "%s: unknown density", *argv); dcanon = denstostring(mt_com.mt_count); if (strcmp(dcanon, *argv) != 0) printf( "Using \"%s\" as an alias for %s\n", *argv, dcanon); p = ""; } else if (!isdigit(**argv) && (comp->c_flags & IS_COMP)) { mt_com.mt_count = stringtocomp(*argv); if ((u_int32_t)mt_com.mt_count == 0xf0f0f0f0) errx(1, "%s: unknown compression", *argv); p = ""; } else if ((comp->c_flags & USE_GETOPT) == 0) { char *q; /* allow for hex numbers; useful for density */ mt_com.mt_count = strtol(*argv, &q, 0); p = q; } if (((comp->c_flags & USE_GETOPT) == 0) && (((mt_com.mt_count <= ((comp->c_flags & ZERO_ALLOWED)? -1: 0)) && ((comp->c_flags & IS_COMP) == 0)) || *p)) errx(1, "%s: illegal count", *argv); } else mt_com.mt_count = 1; switch (comp->c_code) { case MTIOCERRSTAT: { unsigned int i; union mterrstat umn; struct scsi_tape_errors *s = &umn.scsi_errstat; if (ioctl(mtfd, comp->c_code, (caddr_t)&umn) < 0) err(2, "%s", tape); (void)printf("Last I/O Residual: %u\n", s->io_resid); (void)printf(" Last I/O Command:"); for (i = 0; i < sizeof (s->io_cdb); i++) (void)printf(" %02X", s->io_cdb[i]); (void)printf("\n"); (void)printf(" Last I/O Sense:\n\n\t"); for (i = 0; i < sizeof (s->io_sense); i++) { (void)printf(" %02X", s->io_sense[i]); if (((i + 1) & 0xf) == 0) { (void)printf("\n\t"); } } (void)printf("\n"); (void)printf("Last Control Residual: %u\n", s->ctl_resid); (void)printf(" Last Control Command:"); for (i = 0; i < sizeof (s->ctl_cdb); i++) (void)printf(" %02X", s->ctl_cdb[i]); (void)printf("\n"); (void)printf(" Last Control Sense:\n\n\t"); for (i = 0; i < sizeof (s->ctl_sense); i++) { (void)printf(" %02X", s->ctl_sense[i]); if (((i + 1) & 0xf) == 0) { (void)printf("\n\t"); } } (void)printf("\n\n"); exit(0); /* NOTREACHED */ } case MTIOCRDHPOS: case MTIOCRDSPOS: { u_int32_t block; if (ioctl(mtfd, comp->c_code, (caddr_t)&block) < 0) err(2, "%s", tape); (void)printf("%s: %s block location %u\n", tape, (comp->c_code == MTIOCRDHPOS)? "hardware" : "logical", block); exit(0); /* NOTREACHED */ } case MTIOCSLOCATE: case MTIOCHLOCATE: { u_int32_t block = (u_int32_t)mt_com.mt_count; if (ioctl(mtfd, comp->c_code, (caddr_t)&block) < 0) err(2, "%s", tape); exit(0); /* NOTREACHED */ } case MTIOCGETEOTMODEL: { u_int32_t om; if (ioctl(mtfd, MTIOCGETEOTMODEL, (caddr_t)&om) < 0) err(2, "%s", tape); (void)printf("%s: the model is %u filemar%s at EOT\n", tape, om, (om > 1)? "ks" : "k"); exit(0); /* NOTREACHED */ } case MTIOCSETEOTMODEL: { u_int32_t om, nm = (u_int32_t)mt_com.mt_count; if (ioctl(mtfd, MTIOCGETEOTMODEL, (caddr_t)&om) < 0) err(2, "%s", tape); if (ioctl(mtfd, comp->c_code, (caddr_t)&nm) < 0) err(2, "%s", tape); (void)printf("%s: old model was %u filemar%s at EOT\n", tape, om, (om > 1)? "ks" : "k"); (void)printf("%s: new model is %u filemar%s at EOT\n", tape, nm, (nm > 1)? "ks" : "k"); exit(0); /* NOTREACHED */ } case MTIOCRBLIM: { struct mtrblim rblim; bzero(&rblim, sizeof(rblim)); if (ioctl(mtfd, MTIOCRBLIM, (caddr_t)&rblim) < 0) err(2, "%s", tape); (void)printf("%s:\n" " min blocksize %u byte%s\n" " max blocksize %u byte%s\n" " granularity %u byte%s\n", tape, rblim.min_block_length, MT_PLURAL(rblim.min_block_length), rblim.max_block_length, MT_PLURAL(rblim.max_block_length), (1 << rblim.granularity), MT_PLURAL((1 << rblim.granularity))); exit(0); /* NOTREACHED */ } case MTIOCPARAMGET: case MTIOCEXTGET: case MT_CMD_PROTECT: case MT_CMD_GETDENSITY: { int retval = 0; retval = mt_xml_cmd(comp->c_code, argc, argv, mtfd, tape); exit(retval); } case MTIOCEXTLOCATE: { int retval = 0; retval = mt_locate(argc, argv, mtfd, tape); exit(retval); } default: break; } if (ioctl(mtfd, MTIOCTOP, &mt_com) < 0) err(1, "%s: %s", tape, comp->c_name); } else { if (ioctl(mtfd, MTIOCGET, &mt_status) < 0) err(1, NULL); status(&mt_status); } exit(0); /* NOTREACHED */ } static const struct tape_desc { short t_type; /* type of magtape device */ const char *t_name; /* printing name */ const char *t_dsbits; /* "drive status" register */ const char *t_erbits; /* "error" register */ } tapes[] = { { MT_ISAR, "SCSI tape drive", 0, 0 }, { 0, NULL, 0, 0 } }; /* * Interpret the status buffer returned */ static void status(struct mtget *bp) { const struct tape_desc *mt; for (mt = tapes;; mt++) { if (mt->t_type == 0) { (void)printf("%d: unknown tape drive type\n", bp->mt_type); return; } if (mt->t_type == bp->mt_type) break; } if(mt->t_type == MT_ISAR) st_status(bp); else { (void)printf("%s tape drive, residual=%d\n", mt->t_name, bp->mt_resid); printreg("ds", (unsigned short)bp->mt_dsreg, mt->t_dsbits); printreg("\ner", (unsigned short)bp->mt_erreg, mt->t_erbits); (void)putchar('\n'); } } /* * Print a register a la the %b format of the kernel's printf. */ static void printreg(const char *s, u_int v, const char *bits) { int i, any = 0; char c; if (bits && *bits == 8) printf("%s=%o", s, v); else printf("%s=%x", s, v); if (!bits) return; bits++; if (v && bits) { putchar('<'); while ((i = *bits++)) { if (v & (1 << (i-1))) { if (any) putchar(','); any = 1; for (; (c = *bits) > 32; bits++) putchar(c); } else for (; *bits > 32; bits++) ; } putchar('>'); } } static void usage(void) { (void)fprintf(stderr, "usage: mt [-f device] command [count]\n"); exit(1); } static const struct compression_types { u_int32_t comp_number; const char *name; } comp_types[] = { { 0x00, "none" }, { 0x00, "off" }, { 0x10, "IDRC" }, { 0x20, "DCLZ" }, { 0xffffffff, "enable" }, { 0xffffffff, "on" }, { 0xf0f0f0f0, NULL} }; static const char * denstostring(int d) { static char buf[20]; const char *name = mt_density_name(d); if (name == NULL) sprintf(buf, "0x%02x", d); else sprintf(buf, "0x%02x:%s", d, name); return buf; } static const char * getblksiz(int bs) { static char buf[25]; if (bs == 0) return "variable"; else { sprintf(buf, "%d bytes", bs); return buf; } } static const char * comptostring(u_int32_t comp) { static char buf[20]; const struct compression_types *ct; if (comp == MT_COMP_DISABLED) return "disabled"; else if (comp == MT_COMP_UNSUPP) return "unsupported"; for (ct = comp_types; ct->name; ct++) if (ct->comp_number == comp) break; if (ct->comp_number == 0xf0f0f0f0) { sprintf(buf, "0x%x", comp); return(buf); } else return(ct->name); } static u_int32_t stringtocomp(const char *s) { const struct compression_types *ct; size_t l = strlen(s); for (ct = comp_types; ct->name; ct++) if (strncasecmp(ct->name, s, l) == 0) break; return(ct->comp_number); } static struct driver_state { int dsreg; const char *desc; } driver_states[] = { { MTIO_DSREG_REST, "at rest" }, { MTIO_DSREG_RBSY, "Communicating with drive" }, { MTIO_DSREG_WR, "Writing" }, { MTIO_DSREG_FMK, "Writing Filemarks" }, { MTIO_DSREG_ZER, "Erasing" }, { MTIO_DSREG_RD, "Reading" }, { MTIO_DSREG_FWD, "Spacing Forward" }, { MTIO_DSREG_REV, "Spacing Reverse" }, { MTIO_DSREG_POS, "Hardware Positioning (direction unknown)" }, { MTIO_DSREG_REW, "Rewinding" }, { MTIO_DSREG_TEN, "Retensioning" }, { MTIO_DSREG_UNL, "Unloading" }, { MTIO_DSREG_LD, "Loading" }, }; const char * get_driver_state_str(int dsreg) { unsigned int i; for (i = 0; i < (sizeof(driver_states)/sizeof(driver_states[0])); i++) { if (driver_states[i].dsreg == dsreg) return (driver_states[i].desc); } return (NULL); } static void st_status(struct mtget *bp) { printf("Mode Density Blocksize bpi " "Compression\n" "Current: %-17s %-12s %-7d %s\n" "---------available modes---------\n" "0: %-17s %-12s %-7d %s\n" "1: %-17s %-12s %-7d %s\n" "2: %-17s %-12s %-7d %s\n" "3: %-17s %-12s %-7d %s\n", denstostring(bp->mt_density), getblksiz(bp->mt_blksiz), mt_density_bp(bp->mt_density, TRUE), comptostring(bp->mt_comp), denstostring(bp->mt_density0), getblksiz(bp->mt_blksiz0), mt_density_bp(bp->mt_density0, TRUE), comptostring(bp->mt_comp0), denstostring(bp->mt_density1), getblksiz(bp->mt_blksiz1), mt_density_bp(bp->mt_density1, TRUE), comptostring(bp->mt_comp1), denstostring(bp->mt_density2), getblksiz(bp->mt_blksiz2), mt_density_bp(bp->mt_density2, TRUE), comptostring(bp->mt_comp2), denstostring(bp->mt_density3), getblksiz(bp->mt_blksiz3), mt_density_bp(bp->mt_density3, TRUE), comptostring(bp->mt_comp3)); if (bp->mt_dsreg != MTIO_DSREG_NIL) { const char sfmt[] = "Current Driver State: %s.\n"; printf("---------------------------------\n"); const char *state_str; state_str = get_driver_state_str(bp->mt_dsreg); if (state_str == NULL) { char foo[32]; (void) sprintf(foo, "Unknown state 0x%x", bp->mt_dsreg); printf(sfmt, foo); } else { printf(sfmt, state_str); } } if (bp->mt_resid == 0 && bp->mt_fileno == (daddr_t) -1 && bp->mt_blkno == (daddr_t) -1) return; printf("---------------------------------\n"); printf("File Number: %d\tRecord Number: %d\tResidual Count %d\n", bp->mt_fileno, bp->mt_blkno, bp->mt_resid); } static int mt_locate(int argc, char **argv, int mtfd, const char *tape) { struct mtlocate mtl; uint64_t logical_id = 0; mt_locate_dest_type dest_type = MT_LOCATE_DEST_FILE; int eod = 0, explicit = 0, immediate = 0; int64_t partition = 0; int block_addr_set = 0, partition_set = 0, file_set = 0, set_set = 0; int c, retval; retval = 0; bzero(&mtl, sizeof(mtl)); while ((c = getopt(argc, argv, "b:eEf:ip:s:")) != -1) { switch (c) { case 'b': /* Block address */ logical_id = strtoull(optarg, NULL, 0); dest_type = MT_LOCATE_DEST_OBJECT; block_addr_set = 1; break; case 'e': /* end of data */ eod = 1; dest_type = MT_LOCATE_DEST_EOD; break; case 'E': /* * XXX KDM explicit address mode. Should we even * allow this, since the driver doesn't operate in * explicit address mode? */ explicit = 1; break; case 'f': /* file number */ logical_id = strtoull(optarg, NULL, 0); dest_type = MT_LOCATE_DEST_FILE; file_set = 1; break; case 'i': /* * Immediate address mode. XXX KDM do we want to * implement this? The other commands in the * tape driver will need to be able to handle this. */ immediate = 1; break; case 'p': /* * Change partition to the given partition. */ partition = strtol(optarg, NULL, 0); partition_set = 1; break; case 's': /* Go to the given set mark */ logical_id = strtoull(optarg, NULL, 0); dest_type = MT_LOCATE_DEST_SET; set_set = 1; break; default: break; } } /* * These options are mutually exclusive. The user may only specify * one. */ if ((block_addr_set + file_set + eod + set_set) != 1) errx(1, "You must specify only one of -b, -f, -e, or -s"); mtl.dest_type = dest_type; switch (dest_type) { case MT_LOCATE_DEST_OBJECT: case MT_LOCATE_DEST_FILE: case MT_LOCATE_DEST_SET: mtl.logical_id = logical_id; break; case MT_LOCATE_DEST_EOD: break; } if (immediate != 0) mtl.flags |= MT_LOCATE_FLAG_IMMED; if (partition_set != 0) { mtl.flags |= MT_LOCATE_FLAG_CHANGE_PART; mtl.partition = partition; } if (explicit != 0) mtl.block_address_mode = MT_LOCATE_BAM_EXPLICIT; else mtl.block_address_mode = MT_LOCATE_BAM_IMPLICIT; if (ioctl(mtfd, MTIOCEXTLOCATE, &mtl) == -1) err(1, "MTIOCEXTLOCATE ioctl failed on %s", tape); return (retval); } typedef enum { MT_PERIPH_NAME = 0, MT_UNIT_NUMBER = 1, MT_VENDOR = 2, MT_PRODUCT = 3, MT_REVISION = 4, MT_COMPRESSION_SUPPORTED = 5, MT_COMPRESSION_ENABLED = 6, MT_COMPRESSION_ALGORITHM = 7, MT_MEDIA_DENSITY = 8, MT_MEDIA_BLOCKSIZE = 9, MT_CALCULATED_FILENO = 10, MT_CALCULATED_REL_BLKNO = 11, MT_REPORTED_FILENO = 12, MT_REPORTED_BLKNO = 13, MT_PARTITION = 14, MT_BOP = 15, MT_EOP = 16, MT_BPEW = 17, MT_DSREG = 18, MT_RESID = 19, MT_FIXED_MODE = 20, MT_SERIAL_NUM = 21, MT_MAXIO = 22, MT_CPI_MAXIO = 23, MT_MAX_BLK = 24, MT_MIN_BLK = 25, MT_BLK_GRAN = 26, MT_MAX_EFF_IOSIZE = 27 } status_item_index; static struct mt_status_items { const char *name; struct mt_status_entry *entry; } req_status_items[] = { { "periph_name", NULL }, { "unit_number", NULL }, { "vendor", NULL }, { "product", NULL }, { "revision", NULL }, { "compression_supported", NULL }, { "compression_enabled", NULL }, { "compression_algorithm", NULL }, { "media_density", NULL }, { "media_blocksize", NULL }, { "calculated_fileno", NULL }, { "calculated_rel_blkno", NULL }, { "reported_fileno", NULL }, { "reported_blkno", NULL }, { "partition", NULL }, { "bop", NULL }, { "eop", NULL }, { "bpew", NULL }, { "dsreg", NULL }, { "residual", NULL }, { "fixed_mode", NULL }, { "serial_num", NULL }, { "maxio", NULL }, { "cpi_maxio", NULL }, { "max_blk", NULL }, { "min_blk", NULL }, { "blk_gran", NULL }, { "max_effective_iosize", NULL } }; int nstatus_print(int argc, char **argv, char *xml_str, struct mt_status_data *status_data) { unsigned int i; int64_t calculated_fileno, calculated_rel_blkno; int64_t rep_fileno, rep_blkno, partition, resid; char block_str[32]; const char *dens_str; int dsreg, bop, eop, bpew; int xml_dump = 0; size_t dens_len; unsigned int field_width; int verbose = 0; int c; while ((c = getopt(argc, argv, "xv")) != -1) { switch (c) { case 'x': xml_dump = 1; break; case 'v': verbose = 1; break; default: break; } } if (xml_dump != 0) { printf("%s", xml_str); return (0); } for (i = 0; i < (sizeof(req_status_items)/sizeof(req_status_items[0])); i++) { char *name; name = __DECONST(char *, req_status_items[i].name); req_status_items[i].entry = mt_status_entry_find(status_data, name); if (req_status_items[i].entry == NULL) { errx(1, "Cannot find status entry %s", req_status_items[i].name); } } printf("Drive: %s%ju: <%s %s %s> Serial Number: %s\n", req_status_items[MT_PERIPH_NAME].entry->value, (uintmax_t)req_status_items[MT_UNIT_NUMBER].entry->value_unsigned, req_status_items[MT_VENDOR].entry->value, req_status_items[MT_PRODUCT].entry->value, req_status_items[MT_REVISION].entry->value, (req_status_items[MT_SERIAL_NUM].entry->value) ? req_status_items[MT_SERIAL_NUM].entry->value : "none"); printf("---------------------------------\n"); /* * We check to see whether we're in fixed mode or not, and don't * just believe the blocksize. If the SILI bit is turned on, the * blocksize will be set to 4, even though we're doing variable * length (well, multiples of 4) blocks. */ if (req_status_items[MT_FIXED_MODE].entry->value_signed == 0) snprintf(block_str, sizeof(block_str), "variable"); else snprintf(block_str, sizeof(block_str), "%s", getblksiz(req_status_items[ MT_MEDIA_BLOCKSIZE].entry->value_unsigned)); dens_str = denstostring(req_status_items[ MT_MEDIA_DENSITY].entry->value_unsigned); if (dens_str == NULL) dens_len = 0; else dens_len = strlen(dens_str); field_width = MAX(dens_len, 17); printf("Mode %-*s Blocksize bpi Compression\n" "Current: %-*s %-12s %-7d ", field_width, "Density", field_width, dens_str, block_str, mt_density_bp(req_status_items[ MT_MEDIA_DENSITY].entry->value_unsigned, TRUE)); if (req_status_items[MT_COMPRESSION_SUPPORTED].entry->value_signed == 0) printf("unsupported\n"); else if (req_status_items[ MT_COMPRESSION_ENABLED].entry->value_signed == 0) printf("disabled\n"); else { printf("enabled (%s)\n", comptostring(req_status_items[ MT_COMPRESSION_ALGORITHM].entry->value_unsigned)); } dsreg = req_status_items[MT_DSREG].entry->value_signed; if (dsreg != MTIO_DSREG_NIL) { const char sfmt[] = "Current Driver State: %s.\n"; printf("---------------------------------\n"); const char *state_str; state_str = get_driver_state_str(dsreg); if (state_str == NULL) { char foo[32]; (void) sprintf(foo, "Unknown state 0x%x", dsreg); printf(sfmt, foo); } else { printf(sfmt, state_str); } } resid = req_status_items[MT_RESID].entry->value_signed; calculated_fileno = req_status_items[ MT_CALCULATED_FILENO].entry->value_signed; calculated_rel_blkno = req_status_items[ MT_CALCULATED_REL_BLKNO].entry->value_signed; rep_fileno = req_status_items[ MT_REPORTED_FILENO].entry->value_signed; rep_blkno = req_status_items[ MT_REPORTED_BLKNO].entry->value_signed; bop = req_status_items[MT_BOP].entry->value_signed; eop = req_status_items[MT_EOP].entry->value_signed; bpew = req_status_items[MT_BPEW].entry->value_signed; partition = req_status_items[MT_PARTITION].entry->value_signed; printf("---------------------------------\n"); printf("Partition: %3jd Calc File Number: %3jd " " Calc Record Number: %jd\n" "Residual: %3jd Reported File Number: %3jd " "Reported Record Number: %jd\n", partition, calculated_fileno, calculated_rel_blkno, resid, rep_fileno, rep_blkno); printf("Flags: "); if (bop > 0 || eop > 0 || bpew > 0) { int need_comma = 0; if (bop > 0) { printf("BOP"); need_comma = 1; } if (eop > 0) { if (need_comma != 0) printf(","); printf("EOP"); need_comma = 1; } if (bpew > 0) { if (need_comma != 0) printf(","); printf("BPEW"); need_comma = 1; } } else { printf("None"); } printf("\n"); if (verbose != 0) { printf("---------------------------------\n"); printf("Tape I/O parameters:\n"); for (i = MT_MAXIO; i <= MT_MAX_EFF_IOSIZE; i++) { printf(" %s (%s): %ju bytes\n", req_status_items[i].entry->desc, req_status_items[i].name, req_status_items[i].entry->value_unsigned); } } return (0); } int mt_xml_cmd(unsigned long cmd, int argc, char **argv, int mtfd, const char *tape) { struct mt_status_data status_data; #if 0 struct mt_status_entry *entry; #endif char *xml_str; int retval; unsigned long ioctl_cmd; switch (cmd) { case MT_CMD_PROTECT: case MTIOCPARAMGET: ioctl_cmd = MTIOCPARAMGET; break; default: ioctl_cmd = MTIOCEXTGET; break; } retval = mt_get_xml_str(mtfd, ioctl_cmd, &xml_str); if (retval != 0) err(1, "Couldn't get mt XML string"); retval = mt_get_status(xml_str, &status_data); if (retval != XML_STATUS_OK) { warn("Couldn't get mt status for %s", tape); goto bailout; } /* * This gets set if there are memory allocation or other errors in * our parsing of the XML. */ if (status_data.error != 0) { warnx("%s", status_data.error_str); retval = 1; goto bailout; } #if 0 STAILQ_FOREACH(entry, &status_data.entries, links) mt_status_tree_print(entry, 0, NULL); #endif switch (cmd) { case MTIOCEXTGET: retval = nstatus_print(argc, argv, xml_str, &status_data); break; case MTIOCPARAMGET: retval = mt_param(argc, argv, mtfd, xml_str, &status_data); break; case MT_CMD_PROTECT: retval = mt_protect(argc, argv, mtfd, &status_data); break; case MT_CMD_GETDENSITY: retval = mt_getdensity(argc, argv, xml_str, &status_data); break; } bailout: if (xml_str != NULL) free(xml_str); mt_status_free(&status_data); return (retval); } static int mt_set_param(int mtfd, struct mt_status_data *status_data, char *param_name, char *param_value) { struct mt_status_entry *entry; struct mtparamset param_set; entry = mt_status_entry_find(status_data, __DECONST(char *, "mtparamget")); if (entry == NULL) errx(1, "Cannot find parameter root node"); bzero(¶m_set, sizeof(param_set)); entry = mt_entry_find(entry, param_name); if (entry == NULL) errx(1, "Unknown parameter name \"%s\"", param_name); strlcpy(param_set.value_name, param_name, sizeof(param_set.value_name)); switch (entry->var_type) { case MT_TYPE_INT: param_set.value.value_signed = strtoll(param_value, NULL, 0); param_set.value_type = MT_PARAM_SET_SIGNED; param_set.value_len = entry->size; break; case MT_TYPE_UINT: param_set.value.value_unsigned = strtoull(param_value, NULL, 0); param_set.value_type = MT_PARAM_SET_UNSIGNED; param_set.value_len = entry->size; break; case MT_TYPE_STRING: { size_t param_len; param_len = strlen(param_value) + 1; if (param_len > sizeof(param_set.value.value_fixed_str)) { param_set.value_type = MT_PARAM_SET_VAR_STR; param_set.value.value_var_str = param_value; } else { param_set.value_type = MT_PARAM_SET_FIXED_STR; strlcpy(param_set.value.value_fixed_str, param_value, sizeof(param_set.value.value_fixed_str)); } param_set.value_len = param_len; break; } default: errx(1, "Unknown parameter type %d for %s", entry->var_type, param_name); break; } if (ioctl(mtfd, MTIOCPARAMSET, ¶m_set) == -1) err(1, "MTIOCPARAMSET"); if (param_set.status != MT_PARAM_STATUS_OK) errx(1, "Failed to set %s: %s", param_name, param_set.error_str); return (0); } typedef enum { MT_PP_LBP_R, MT_PP_LBP_W, MT_PP_RBDP, MT_PP_PI_LENGTH, MT_PP_PROT_METHOD } mt_protect_param; static struct mt_protect_info { const char *name; struct mt_status_entry *entry; uint32_t value; } mt_protect_list[] = { { "lbp_r", NULL, 0 }, { "lbp_w", NULL, 0 }, { "rbdp", NULL, 0 }, { "pi_length", NULL, 0 }, { "prot_method", NULL, 0 } }; #define MT_NUM_PROTECT_PARAMS (sizeof(mt_protect_list)/sizeof(mt_protect_list[0])) #define MT_PROT_NAME "protection" static int mt_protect(int argc, char **argv, int mtfd, struct mt_status_data *status_data) { int retval = 0; int do_enable = 0, do_disable = 0, do_list = 0; int rbdp_set = 0, lbp_w_set = 0, lbp_r_set = 0; int prot_method_set = 0, pi_length_set = 0; int verbose = 0; uint32_t rbdp = 0, lbp_w = 0, lbp_r = 0; uint32_t prot_method = 0, pi_length = 0; struct mt_status_entry *prot_entry, *supported_entry; struct mt_status_entry *entry; struct mtparamset params[MT_NUM_PROTECT_PARAMS]; struct mtsetlist param_list; unsigned int i; int c; while ((c = getopt(argc, argv, "b:delL:m:r:vw:")) != -1) { switch (c) { case 'b': rbdp_set = 1; rbdp = strtoul(optarg, NULL, 0); if ((rbdp != 0) && (rbdp != 1)) errx(1, "valid values for -b are 0 and 1"); break; case 'd': do_disable = 1; break; case 'e': do_enable = 1; break; case 'l': do_list = 1; break; case 'L': pi_length_set = 1; pi_length = strtoul(optarg, NULL, 0); if (pi_length > SA_CTRL_DP_PI_LENGTH_MASK) errx(1, "PI length %u > maximum %u", pi_length, SA_CTRL_DP_PI_LENGTH_MASK); break; case 'm': prot_method_set = 1; prot_method = strtoul(optarg, NULL, 0); if (prot_method > SA_CTRL_DP_METHOD_MAX) errx(1, "Method %u > maximum %u", prot_method, SA_CTRL_DP_METHOD_MAX); break; case 'r': lbp_r_set = 1; lbp_r = strtoul(optarg, NULL, 0); if ((lbp_r != 0) && (lbp_r != 1)) errx(1, "valid values for -r are 0 and 1"); break; case 'v': verbose = 1; break; case 'w': lbp_w_set = 1; lbp_w = strtoul(optarg, NULL, 0); if ((lbp_w != 0) && (lbp_r != 1)) errx(1, "valid values for -r are 0 and 1"); break; default: break; } } if ((rbdp_set + do_disable + do_enable + do_list + pi_length_set + prot_method_set + lbp_r_set + lbp_w_set) == 0) errx(1, "Need an argument for protect"); if ((do_disable + do_enable + do_list) != 1) errx(1, "You must specify only one of -e, -d or -l"); if (do_list != 0) { retval = mt_protect_print(status_data, verbose); goto bailout; } if (do_enable != 0) { /* * Enable protection, but allow the user to override * settings if he doesn't want everything turned on. */ if (rbdp_set == 0) rbdp = 1; if (lbp_w_set == 0) lbp_w = 1; if (lbp_r_set == 0) lbp_r = 1; /* * If the user doesn't override it, we default to enabling * Reed-Solomon checkums. */ if (prot_method_set == 0) prot_method = SA_CTRL_DP_REED_SOLOMON; if (pi_length_set == 0) pi_length = SA_CTRL_DP_RS_LENGTH; } else if (do_disable != 0) { /* * If the user wants to disable protection, we ignore any * other parameters he has set. Everything gets set to 0. */ rbdp = lbp_w = lbp_r = 0; prot_method = pi_length = 0; } prot_entry = mt_status_entry_find(status_data, __DECONST(char *, MT_PROT_NAME)); if (prot_entry == NULL) errx(1, "Unable to find protection information status"); supported_entry = mt_entry_find(prot_entry, __DECONST(char *, "protection_supported")); if (supported_entry == NULL) errx(1, "Unable to find protection support information"); if (((supported_entry->var_type == MT_TYPE_INT) && (supported_entry->value_signed == 0)) || ((supported_entry->var_type == MT_TYPE_UINT) && (supported_entry->value_unsigned == 0))) errx(1, "This device does not support protection information"); mt_protect_list[MT_PP_LBP_R].value = lbp_r; mt_protect_list[MT_PP_LBP_W].value = lbp_w; mt_protect_list[MT_PP_RBDP].value = rbdp; mt_protect_list[MT_PP_PI_LENGTH].value = pi_length; mt_protect_list[MT_PP_PROT_METHOD].value = prot_method; bzero(¶ms, sizeof(params)); bzero(¶m_list, sizeof(param_list)); /* * Go through the list and make sure that we have this parameter, * and that it is still an unsigned integer. If not, we've got a * problem. */ for (i = 0; i < MT_NUM_PROTECT_PARAMS; i++) { entry = mt_entry_find(prot_entry, __DECONST(char *, mt_protect_list[i].name)); if (entry == NULL) { errx(1, "Unable to find parameter %s", mt_protect_list[i].name); } mt_protect_list[i].entry = entry; if (entry->var_type != MT_TYPE_UINT) errx(1, "Parameter %s is type %d, not unsigned, " "cannot proceed", mt_protect_list[i].name, entry->var_type); snprintf(params[i].value_name, sizeof(params[i].value_name), "%s.%s", MT_PROT_NAME, mt_protect_list[i].name); /* XXX KDM unify types here */ params[i].value_type = MT_PARAM_SET_UNSIGNED; params[i].value_len = sizeof(mt_protect_list[i].value); params[i].value.value_unsigned = mt_protect_list[i].value; } param_list.num_params = MT_NUM_PROTECT_PARAMS; param_list.param_len = sizeof(params); param_list.params = params; if (ioctl(mtfd, MTIOCSETLIST, ¶m_list) == -1) err(1, "error issuing MTIOCSETLIST ioctl"); for (i = 0; i < MT_NUM_PROTECT_PARAMS; i++) { if (params[i].status != MT_PARAM_STATUS_OK) { warnx("%s", params[i].error_str); retval = 1; } } bailout: return (retval); } static int mt_param(int argc, char **argv, int mtfd, char *xml_str, struct mt_status_data *status_data) { int list = 0, do_set = 0, xml_dump = 0; char *param_name = NULL, *param_value = NULL; int retval = 0, quiet = 0; int c; while ((c = getopt(argc, argv, "lp:qs:x")) != -1) { switch (c) { case 'l': list = 1; break; case 'p': if (param_name != NULL) { - warnx("Only one paramter name may be " + warnx("Only one parameter name may be " "specified"); retval = 1; goto bailout; } param_name = strdup(optarg); break; case 'q': quiet = 1; break; case 's': if (param_value != NULL) { - warnx("Only one paramter value may be " + warnx("Only one parameter value may be " "specified"); retval = 1; goto bailout; } param_value = strdup(optarg); do_set = 1; break; case 'x': xml_dump = 1; break; default: break; } } if ((list + do_set + xml_dump) != 1) { warnx("You must specify only one of -s, -l or -x"); retval = 1; goto bailout; } if (xml_dump != 0) { printf("%s", xml_str); retval = 0; goto bailout; } if (do_set != 0) { if (param_name == NULL) errx(1, "You must specify -p with -s"); retval = mt_set_param(mtfd, status_data, param_name, param_value); } else if (list != 0) retval = mt_param_list(status_data, param_name, quiet); bailout: free(param_name); free(param_value); return (retval); } int mt_print_density_entry(struct mt_status_entry *density_root, int indent) { struct mt_status_entry *entry; int retval = 0; STAILQ_FOREACH(entry, &density_root->child_entries, links) { if (entry->var_type == MT_TYPE_NODE) { retval = mt_print_density_entry(entry, indent + 2); if (retval != 0) break; else continue; } if ((strcmp(entry->entry_name, "primary_density_code") == 0) || (strcmp(entry->entry_name, "secondary_density_code") == 0) || (strcmp(entry->entry_name, "density_code") == 0)) { printf("%*s%s (%s): %s\n", indent, "", entry->desc ? entry->desc : "", entry->entry_name, denstostring(entry->value_unsigned)); } else if (strcmp(entry->entry_name, "density_flags") == 0) { printf("%*sMedium Access: ", indent, ""); if (entry->value_unsigned & MT_DENS_WRITE_OK) { printf("Read and Write\n"); } else { printf("Read Only\n"); } printf("%*sDefault Density: %s\n", indent, "", (entry->value_unsigned & MT_DENS_DEFLT) ? "Yes" : "No"); printf("%*sDuplicate Density: %s\n", indent, "", (entry->value_unsigned & MT_DENS_DUP) ? "Yes" : "No"); } else if (strcmp(entry->entry_name, "media_width") == 0) { printf("%*s%s (%s): %.1f mm\n", indent, "", entry->desc ? entry->desc : "", entry->entry_name, (double)((double)entry->value_unsigned / 10)); } else if (strcmp(entry->entry_name, "medium_length") == 0) { printf("%*s%s (%s): %ju m\n", indent, "", entry->desc ? entry->desc : "", entry->entry_name, (uintmax_t)entry->value_unsigned); } else if (strcmp(entry->entry_name, "capacity") == 0) { printf("%*s%s (%s): %ju MB\n", indent, "", entry->desc ? entry->desc : "", entry->entry_name, (uintmax_t)entry->value_unsigned); } else { printf("%*s%s (%s): %s\n", indent, "", entry->desc ? entry->desc : "", entry->entry_name, entry->value); } } return (retval); } int mt_print_density_report(struct mt_status_entry *report_root, int indent) { struct mt_status_entry *mt_report, *media_report; struct mt_status_entry *entry; int retval = 0; mt_report = mt_entry_find(report_root, __DECONST(char *, MT_MEDIUM_TYPE_REPORT_NAME)); if (mt_report == NULL) return (1); media_report = mt_entry_find(report_root, __DECONST(char *, MT_MEDIA_REPORT_NAME)); if (media_report == NULL) return (1); if ((mt_report->value_signed == 0) && (media_report->value_signed == 0)) { printf("%*sThis tape drive supports the following " "media densities:\n", indent, ""); } else if ((mt_report->value_signed == 0) && (media_report->value_signed != 0)) { printf("%*sThe tape currently in this drive supports " "the following media densities:\n", indent, ""); } else if ((mt_report->value_signed != 0) && (media_report->value_signed == 0)) { printf("%*sThis tape drive supports the following " "media types:\n", indent, ""); } else { printf("%*sThis tape currently in this drive supports " "the following media types:\n", indent, ""); } STAILQ_FOREACH(entry, &report_root->child_entries, links) { struct mt_status_nv *nv; if (strcmp(entry->entry_name, MT_DENSITY_ENTRY_NAME) != 0) continue; STAILQ_FOREACH(nv, &entry->nv_list, links) { if (strcmp(nv->name, "num") != 0) continue; break; } indent += 2; printf("%*sDensity Entry", indent, ""); if (nv != NULL) printf(" %s", nv->value); printf(":\n"); retval = mt_print_density_entry(entry, indent + 2); indent -= 2; } return (retval); } int mt_print_density(struct mt_status_entry *density_root, int indent) { struct mt_status_entry *entry; int retval = 0; /* * We should have this entry for every tape drive. This particular * value is reported via the mode page block header, not the * SCSI REPORT DENSITY SUPPORT command. */ entry = mt_entry_find(density_root, __DECONST(char *, MT_MEDIA_DENSITY_NAME)); if (entry == NULL) errx(1, "Unable to find node %s", MT_MEDIA_DENSITY_NAME); printf("%*sCurrent density: %s\n", indent, "", denstostring(entry->value_unsigned)); /* * It isn't an error if we don't have any density reports. Tape * drives that don't support the REPORT DENSITY SUPPORT command * won't have any; they will only have the current density entry * above. */ STAILQ_FOREACH(entry, &density_root->child_entries, links) { if (strcmp(entry->entry_name, MT_DENSITY_REPORT_NAME) != 0) continue; retval = mt_print_density_report(entry, indent); } return (retval); } int mt_getdensity(int argc, char **argv, char *xml_str, struct mt_status_data *status_data) { int retval = 0; int verbose = 0, xml_dump = 0; struct mt_status_entry *density_root = NULL; int c; while ((c = getopt(argc, argv, "vx")) != -1) { switch (c) { case 'v': verbose = 1; break; case 'x': xml_dump = 1; break; } } if (xml_dump != 0) { printf("%s", xml_str); return (0); } density_root = mt_status_entry_find(status_data, __DECONST(char *, MT_DENSITY_ROOT_NAME)); if (density_root == NULL) errx(1, "Cannot find density root node %s", MT_DENSITY_ROOT_NAME); retval = mt_print_density(density_root, 0); return (retval); } static void warn_eof(void) { fprintf(stderr, "The \"eof\" command has been disabled.\n" "Use \"weof\" if you really want to write end-of-file marks,\n" "or \"eom\" if you rather want to skip to the end of " "recorded medium.\n"); exit(1); } Index: head/usr.bin/patch/pch.c =================================================================== --- head/usr.bin/patch/pch.c (revision 289676) +++ head/usr.bin/patch/pch.c (revision 289677) @@ -1,1617 +1,1617 @@ /*- * Copyright 1986, Larry Wall * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following condition is met: * 1. Redistributions of source code must retain the above copyright notice, * this condition and the following disclaimer. * * 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. * * patch - a program to apply diffs to original files * * -C option added in 1998, original code by Marc Espie, based on FreeBSD * behaviour * * $OpenBSD: pch.c,v 1.43 2014/11/18 17:03:35 tobias Exp $ * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "util.h" #include "pch.h" #include "pathnames.h" /* Patch (diff listing) abstract type. */ static off_t p_filesize; /* size of the patch file */ static LINENUM p_first; /* 1st line number */ static LINENUM p_newfirst; /* 1st line number of replacement */ static LINENUM p_ptrn_lines; /* # lines in pattern */ static LINENUM p_repl_lines; /* # lines in replacement text */ static LINENUM p_end = -1; /* last line in hunk */ static LINENUM p_max; /* max allowed value of p_end */ static LINENUM p_context = 3; /* # of context lines */ static LINENUM p_input_line = 0; /* current line # from patch file */ static char **p_line = NULL;/* the text of the hunk */ static unsigned short *p_len = NULL; /* length of each line */ static char *p_char = NULL; /* +, -, and ! */ static int hunkmax = INITHUNKMAX; /* size of above arrays to begin with */ static int p_indent; /* indent to patch */ static off_t p_base; /* where to intuit this time */ static LINENUM p_bline; /* line # of p_base */ static off_t p_start; /* where intuit found a patch */ static LINENUM p_sline; /* and the line number for it */ static LINENUM p_hunk_beg; /* line number of current hunk */ static LINENUM p_efake = -1; /* end of faked up lines--don't free */ static LINENUM p_bfake = -1; /* beg of faked up lines */ static FILE *pfp = NULL; /* patch file pointer */ static char *bestguess = NULL; /* guess at correct filename */ static void grow_hunkmax(void); static int intuit_diff_type(void); static void next_intuit_at(off_t, LINENUM); static void skip_to(off_t, LINENUM); static size_t pgets(bool _do_indent); static char *best_name(const struct file_name *, bool); static char *posix_name(const struct file_name *, bool); static size_t num_components(const char *); static LINENUM strtolinenum(char *, char **); /* * Prepare to look for the next patch in the patch file. */ void re_patch(void) { p_first = 0; p_newfirst = 0; p_ptrn_lines = 0; p_repl_lines = 0; p_end = (LINENUM) - 1; p_max = 0; p_indent = 0; } /* * Open the patch file at the beginning of time. */ void open_patch_file(const char *filename) { struct stat filestat; int nr, nw; if (filename == NULL || *filename == '\0' || strEQ(filename, "-")) { pfp = fopen(TMPPATNAME, "w"); if (pfp == NULL) pfatal("can't create %s", TMPPATNAME); while ((nr = fread(buf, 1, buf_size, stdin)) > 0) { nw = fwrite(buf, 1, nr, pfp); if (nr != nw) pfatal("write error to %s", TMPPATNAME); } if (ferror(pfp) || fclose(pfp)) pfatal("can't write %s", TMPPATNAME); filename = TMPPATNAME; } pfp = fopen(filename, "r"); if (pfp == NULL) pfatal("patch file %s not found", filename); if (fstat(fileno(pfp), &filestat)) pfatal("can't stat %s", filename); p_filesize = filestat.st_size; next_intuit_at(0, 1L); /* start at the beginning */ set_hunkmax(); } /* * Make sure our dynamically realloced tables are malloced to begin with. */ void set_hunkmax(void) { if (p_line == NULL) p_line = malloc(hunkmax * sizeof(char *)); if (p_len == NULL) p_len = malloc(hunkmax * sizeof(unsigned short)); if (p_char == NULL) p_char = malloc(hunkmax * sizeof(char)); } /* * Enlarge the arrays containing the current hunk of patch. */ static void grow_hunkmax(void) { int new_hunkmax = hunkmax * 2; if (p_line == NULL || p_len == NULL || p_char == NULL) fatal("Internal memory allocation error\n"); p_line = reallocf(p_line, new_hunkmax * sizeof(char *)); p_len = reallocf(p_len, new_hunkmax * sizeof(unsigned short)); p_char = reallocf(p_char, new_hunkmax * sizeof(char)); if (p_line != NULL && p_len != NULL && p_char != NULL) { hunkmax = new_hunkmax; return; } if (!using_plan_a) fatal("out of memory\n"); out_of_mem = true; /* whatever is null will be allocated again */ /* from within plan_a(), of all places */ } /* True if the remainder of the patch file contains a diff of some sort. */ bool there_is_another_patch(void) { bool exists = false; if (p_base != 0 && p_base >= p_filesize) { if (verbose) say("done\n"); return false; } if (verbose) say("Hmm..."); diff_type = intuit_diff_type(); if (!diff_type) { if (p_base != 0) { if (verbose) say(" Ignoring the trailing garbage.\ndone\n"); } else say(" I can't seem to find a patch in there anywhere.\n"); return false; } if (verbose) say(" %sooks like %s to me...\n", (p_base == 0 ? "L" : "The next patch l"), diff_type == UNI_DIFF ? "a unified diff" : diff_type == CONTEXT_DIFF ? "a context diff" : diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" : diff_type == NORMAL_DIFF ? "a normal diff" : "an ed script"); if (p_indent && verbose) say("(Patch is indented %d space%s.)\n", p_indent, p_indent == 1 ? "" : "s"); skip_to(p_start, p_sline); while (filearg[0] == NULL) { if (force || batch) { say("No file to patch. Skipping...\n"); filearg[0] = xstrdup(bestguess); skip_rest_of_patch = true; return true; } ask("File to patch: "); if (*buf != '\n') { free(bestguess); bestguess = xstrdup(buf); filearg[0] = fetchname(buf, &exists, 0); } if (!exists) { ask("No file found--skip this patch? [n] "); if (*buf != 'y') continue; if (verbose) say("Skipping patch...\n"); free(filearg[0]); filearg[0] = fetchname(bestguess, &exists, 0); skip_rest_of_patch = true; return true; } } return true; } static void p4_fetchname(struct file_name *name, char *str) { char *t, *h; /* Skip leading whitespace. */ while (isspace((unsigned char)*str)) str++; /* Remove the file revision number. */ for (t = str, h = NULL; *t != '\0' && !isspace((unsigned char)*t); t++) if (*t == '#') h = t; if (h != NULL) *h = '\0'; name->path = fetchname(str, &name->exists, strippath); } /* Determine what kind of diff is in the remaining part of the patch file. */ static int intuit_diff_type(void) { off_t this_line = 0, previous_line; off_t first_command_line = -1; LINENUM fcl_line = -1; bool last_line_was_command = false, this_is_a_command = false; bool stars_last_line = false, stars_this_line = false; char *s, *t; int indent, retval; struct file_name names[MAX_FILE]; memset(names, 0, sizeof(names)); ok_to_create_file = false; fseeko(pfp, p_base, SEEK_SET); p_input_line = p_bline - 1; for (;;) { previous_line = this_line; last_line_was_command = this_is_a_command; stars_last_line = stars_this_line; this_line = ftello(pfp); indent = 0; p_input_line++; if (pgets(false) == 0) { if (first_command_line >= 0) { /* nothing but deletes!? */ p_start = first_command_line; p_sline = fcl_line; retval = ED_DIFF; goto scan_exit; } else { p_start = this_line; p_sline = p_input_line; retval = 0; goto scan_exit; } } for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) { if (*s == '\t') indent += 8 - (indent % 8); else indent++; } for (t = s; isdigit((unsigned char)*t) || *t == ','; t++) ; this_is_a_command = (isdigit((unsigned char)*s) && (*t == 'd' || *t == 'c' || *t == 'a')); if (first_command_line < 0 && this_is_a_command) { first_command_line = this_line; fcl_line = p_input_line; p_indent = indent; /* assume this for now */ } if (!stars_last_line && strnEQ(s, "*** ", 4)) names[OLD_FILE].path = fetchname(s + 4, &names[OLD_FILE].exists, strippath); else if (strnEQ(s, "--- ", 4)) names[NEW_FILE].path = fetchname(s + 4, &names[NEW_FILE].exists, strippath); else if (strnEQ(s, "+++ ", 4)) /* pretend it is the old name */ names[OLD_FILE].path = fetchname(s + 4, &names[OLD_FILE].exists, strippath); else if (strnEQ(s, "Index:", 6)) names[INDEX_FILE].path = fetchname(s + 6, &names[INDEX_FILE].exists, strippath); else if (strnEQ(s, "Prereq:", 7)) { for (t = s + 7; isspace((unsigned char)*t); t++) ; revision = xstrdup(t); for (t = revision; *t && !isspace((unsigned char)*t); t++) ; *t = '\0'; if (*revision == '\0') { free(revision); revision = NULL; } } else if (strnEQ(s, "==== ", 5)) { /* Perforce-style diffs. */ if ((t = strstr(s + 5, " - ")) != NULL) p4_fetchname(&names[NEW_FILE], t + 3); p4_fetchname(&names[OLD_FILE], s + 5); } if ((!diff_type || diff_type == ED_DIFF) && first_command_line >= 0 && strEQ(s, ".\n")) { p_indent = indent; p_start = first_command_line; p_sline = fcl_line; retval = ED_DIFF; goto scan_exit; } if ((!diff_type || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) { if (strnEQ(s + 4, "0,0", 3)) ok_to_create_file = true; p_indent = indent; p_start = this_line; p_sline = p_input_line; retval = UNI_DIFF; goto scan_exit; } stars_this_line = strnEQ(s, "********", 8); if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line && strnEQ(s, "*** ", 4)) { if (strtolinenum(s + 4, &s) == 0) ok_to_create_file = true; /* * If this is a new context diff the character just * at the end of the line is a '*'. */ while (*s && *s != '\n') s++; p_indent = indent; p_start = previous_line; p_sline = p_input_line - 1; retval = (*(s - 1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF); goto scan_exit; } if ((!diff_type || diff_type == NORMAL_DIFF) && last_line_was_command && (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2))) { p_start = previous_line; p_sline = p_input_line - 1; p_indent = indent; retval = NORMAL_DIFF; goto scan_exit; } } scan_exit: if (retval == UNI_DIFF) { /* unswap old and new */ struct file_name tmp = names[OLD_FILE]; names[OLD_FILE] = names[NEW_FILE]; names[NEW_FILE] = tmp; } if (filearg[0] == NULL) { if (posix) filearg[0] = posix_name(names, ok_to_create_file); else { /* Ignore the Index: name for context diffs, like GNU */ if (names[OLD_FILE].path != NULL || names[NEW_FILE].path != NULL) { free(names[INDEX_FILE].path); names[INDEX_FILE].path = NULL; } filearg[0] = best_name(names, ok_to_create_file); } } free(bestguess); bestguess = NULL; if (filearg[0] != NULL) bestguess = xstrdup(filearg[0]); else if (!ok_to_create_file) { /* * We don't want to create a new file but we need a * filename to set bestguess. Avoid setting filearg[0] * so the file is not created automatically. */ if (posix) bestguess = posix_name(names, true); else bestguess = best_name(names, true); } free(names[OLD_FILE].path); free(names[NEW_FILE].path); free(names[INDEX_FILE].path); return retval; } /* * Remember where this patch ends so we know where to start up again. */ static void next_intuit_at(off_t file_pos, LINENUM file_line) { p_base = file_pos; p_bline = file_line; } /* * Basically a verbose fseeko() to the actual diff listing. */ static void skip_to(off_t file_pos, LINENUM file_line) { size_t len; if (p_base > file_pos) fatal("Internal error: seek %lld>%lld\n", (long long)p_base, (long long)file_pos); if (verbose && p_base < file_pos) { fseeko(pfp, p_base, SEEK_SET); say("The text leading up to this was:\n--------------------------\n"); while (ftello(pfp) < file_pos) { len = pgets(false); if (len == 0) fatal("Unexpected end of file\n"); say("|%s", buf); } say("--------------------------\n"); } else fseeko(pfp, file_pos, SEEK_SET); p_input_line = file_line - 1; } /* Make this a function for better debugging. */ static void malformed(void) { fatal("malformed patch at line %ld: %s", p_input_line, buf); /* about as informative as "Syntax error" in C */ } /* * True if the line has been discarded (i.e. it is a line saying * "\ No newline at end of file".) */ static bool remove_special_line(void) { int c; c = fgetc(pfp); if (c == '\\') { do { c = fgetc(pfp); } while (c != EOF && c != '\n'); return true; } if (c != EOF) fseeko(pfp, -1, SEEK_CUR); return false; } /* * True if there is more of the current diff listing to process. */ bool another_hunk(void) { off_t line_beginning; /* file pos of the current line */ LINENUM repl_beginning; /* index of --- line */ LINENUM fillcnt; /* #lines of missing ptrn or repl */ LINENUM fillsrc; /* index of first line to copy */ LINENUM filldst; /* index of first missing line */ - bool ptrn_spaces_eaten; /* ptrn was slightly misformed */ + bool ptrn_spaces_eaten; /* ptrn was slightly malformed */ bool repl_could_be_missing; /* no + or ! lines in this hunk */ bool repl_missing; /* we are now backtracking */ off_t repl_backtrack_position; /* file pos of first repl line */ LINENUM repl_patch_line; /* input line number for same */ LINENUM ptrn_copiable; /* # of copiable lines in ptrn */ char *s; size_t len; int context = 0; while (p_end >= 0) { if (p_end == p_efake) p_end = p_bfake; /* don't free twice */ else free(p_line[p_end]); p_end--; } p_efake = -1; p_max = hunkmax; /* gets reduced when --- found */ if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) { line_beginning = ftello(pfp); repl_beginning = 0; fillcnt = 0; fillsrc = 0; filldst = 0; ptrn_spaces_eaten = false; repl_could_be_missing = true; repl_missing = false; repl_backtrack_position = 0; repl_patch_line = 0; ptrn_copiable = 0; len = pgets(true); p_input_line++; if (len == 0 || strnNE(buf, "********", 8)) { next_intuit_at(line_beginning, p_input_line); return false; } p_context = 100; p_hunk_beg = p_input_line + 1; while (p_end < p_max) { line_beginning = ftello(pfp); len = pgets(true); p_input_line++; if (len == 0) { if (p_max - p_end < 4) { /* assume blank lines got chopped */ strlcpy(buf, " \n", buf_size); } else { if (repl_beginning && repl_could_be_missing) { repl_missing = true; goto hunk_done; } fatal("unexpected end of file in patch\n"); } } p_end++; if (p_end >= hunkmax) fatal("Internal error: hunk larger than hunk " "buffer size"); p_char[p_end] = *buf; p_line[p_end] = NULL; switch (*buf) { case '*': if (strnEQ(buf, "********", 8)) { if (repl_beginning && repl_could_be_missing) { repl_missing = true; goto hunk_done; } else fatal("unexpected end of hunk " "at line %ld\n", p_input_line); } if (p_end != 0) { if (repl_beginning && repl_could_be_missing) { repl_missing = true; goto hunk_done; } fatal("unexpected *** at line %ld: %s", p_input_line, buf); } context = 0; p_line[p_end] = savestr(buf); if (out_of_mem) { p_end--; return false; } for (s = buf; *s && !isdigit((unsigned char)*s); s++) ; if (!*s) malformed(); if (strnEQ(s, "0,0", 3)) memmove(s, s + 2, strlen(s + 2) + 1); p_first = strtolinenum(s, &s); if (*s == ',') { for (; *s && !isdigit((unsigned char)*s); s++) ; if (!*s) malformed(); p_ptrn_lines = strtolinenum(s, &s) - p_first + 1; if (p_ptrn_lines < 0) malformed(); } else if (p_first) p_ptrn_lines = 1; else { p_ptrn_lines = 0; p_first = 1; } if (p_first >= LINENUM_MAX - p_ptrn_lines || p_ptrn_lines >= LINENUM_MAX - 6) malformed(); /* we need this much at least */ p_max = p_ptrn_lines + 6; while (p_max >= hunkmax) grow_hunkmax(); p_max = hunkmax; break; case '-': if (buf[1] == '-') { if (repl_beginning || (p_end != p_ptrn_lines + 1 + (p_char[p_end - 1] == '\n'))) { if (p_end == 1) { /* * `old' lines were omitted; * set up to fill them in * from 'new' context lines. */ p_end = p_ptrn_lines + 1; fillsrc = p_end + 1; filldst = 1; fillcnt = p_ptrn_lines; } else { if (repl_beginning) { if (repl_could_be_missing) { repl_missing = true; goto hunk_done; } fatal("duplicate \"---\" at line %ld--check line numbers at line %ld\n", p_input_line, p_hunk_beg + repl_beginning); } else { fatal("%s \"---\" at line %ld--check line numbers at line %ld\n", (p_end <= p_ptrn_lines ? "Premature" : "Overdue"), p_input_line, p_hunk_beg); } } } repl_beginning = p_end; repl_backtrack_position = ftello(pfp); repl_patch_line = p_input_line; p_line[p_end] = savestr(buf); if (out_of_mem) { p_end--; return false; } p_char[p_end] = '='; for (s = buf; *s && !isdigit((unsigned char)*s); s++) ; if (!*s) malformed(); p_newfirst = strtolinenum(s, &s); if (*s == ',') { for (; *s && !isdigit((unsigned char)*s); s++) ; if (!*s) malformed(); p_repl_lines = strtolinenum(s, &s) - p_newfirst + 1; if (p_repl_lines < 0) malformed(); } else if (p_newfirst) p_repl_lines = 1; else { p_repl_lines = 0; p_newfirst = 1; } if (p_newfirst >= LINENUM_MAX - p_repl_lines || p_repl_lines >= LINENUM_MAX - p_end) malformed(); p_max = p_repl_lines + p_end; if (p_max > MAXHUNKSIZE) fatal("hunk too large (%ld lines) at line %ld: %s", p_max, p_input_line, buf); while (p_max >= hunkmax) grow_hunkmax(); if (p_repl_lines != ptrn_copiable && (p_context != 0 || p_repl_lines != 1)) repl_could_be_missing = false; break; } goto change_line; case '+': case '!': repl_could_be_missing = false; change_line: if (buf[1] == '\n' && canonicalize) strlcpy(buf + 1, " \n", buf_size - 1); if (!isspace((unsigned char)buf[1]) && buf[1] != '>' && buf[1] != '<' && repl_beginning && repl_could_be_missing) { repl_missing = true; goto hunk_done; } if (context >= 0) { if (context < p_context) p_context = context; context = -1000; } p_line[p_end] = savestr(buf + 2); if (out_of_mem) { p_end--; return false; } if (p_end == p_ptrn_lines) { if (remove_special_line()) { int l; l = strlen(p_line[p_end]) - 1; (p_line[p_end])[l] = 0; } } break; case '\t': case '\n': /* assume the 2 spaces got eaten */ if (repl_beginning && repl_could_be_missing && (!ptrn_spaces_eaten || diff_type == NEW_CONTEXT_DIFF)) { repl_missing = true; goto hunk_done; } p_line[p_end] = savestr(buf); if (out_of_mem) { p_end--; return false; } if (p_end != p_ptrn_lines + 1) { ptrn_spaces_eaten |= (repl_beginning != 0); context++; if (!repl_beginning) ptrn_copiable++; p_char[p_end] = ' '; } break; case ' ': if (!isspace((unsigned char)buf[1]) && repl_beginning && repl_could_be_missing) { repl_missing = true; goto hunk_done; } context++; if (!repl_beginning) ptrn_copiable++; p_line[p_end] = savestr(buf + 2); if (out_of_mem) { p_end--; return false; } break; default: if (repl_beginning && repl_could_be_missing) { repl_missing = true; goto hunk_done; } malformed(); } /* set up p_len for strncmp() so we don't have to */ /* assume null termination */ if (p_line[p_end]) p_len[p_end] = strlen(p_line[p_end]); else p_len[p_end] = 0; } hunk_done: if (p_end >= 0 && !repl_beginning) fatal("no --- found in patch at line %ld\n", pch_hunk_beg()); if (repl_missing) { /* reset state back to just after --- */ p_input_line = repl_patch_line; for (p_end--; p_end > repl_beginning; p_end--) free(p_line[p_end]); fseeko(pfp, repl_backtrack_position, SEEK_SET); /* redundant 'new' context lines were omitted - set */ /* up to fill them in from the old file context */ if (!p_context && p_repl_lines == 1) { p_repl_lines = 0; p_max--; } fillsrc = 1; filldst = repl_beginning + 1; fillcnt = p_repl_lines; p_end = p_max; } else if (!p_context && fillcnt == 1) { /* the first hunk was a null hunk with no context */ /* and we were expecting one line -- fix it up. */ while (filldst < p_end) { p_line[filldst] = p_line[filldst + 1]; p_char[filldst] = p_char[filldst + 1]; p_len[filldst] = p_len[filldst + 1]; filldst++; } #if 0 repl_beginning--; /* this doesn't need to be fixed */ #endif p_end--; p_first++; /* do append rather than insert */ fillcnt = 0; p_ptrn_lines = 0; } if (diff_type == CONTEXT_DIFF && (fillcnt || (p_first > 1 && ptrn_copiable > 2 * p_context))) { if (verbose) say("%s\n%s\n%s\n", "(Fascinating--this is really a new-style context diff but without", "the telltale extra asterisks on the *** line that usually indicate", "the new style...)"); diff_type = NEW_CONTEXT_DIFF; } /* if there were omitted context lines, fill them in now */ if (fillcnt) { p_bfake = filldst; /* remember where not to free() */ p_efake = filldst + fillcnt - 1; while (fillcnt-- > 0) { while (fillsrc <= p_end && p_char[fillsrc] != ' ') fillsrc++; if (fillsrc > p_end) fatal("replacement text or line numbers mangled in hunk at line %ld\n", p_hunk_beg); p_line[filldst] = p_line[fillsrc]; p_char[filldst] = p_char[fillsrc]; p_len[filldst] = p_len[fillsrc]; fillsrc++; filldst++; } while (fillsrc <= p_end && fillsrc != repl_beginning && p_char[fillsrc] != ' ') fillsrc++; #ifdef DEBUGGING if (debug & 64) printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n", fillsrc, filldst, repl_beginning, p_end + 1); #endif if (fillsrc != p_end + 1 && fillsrc != repl_beginning) malformed(); if (filldst != p_end + 1 && filldst != repl_beginning) malformed(); } if (p_line[p_end] != NULL) { if (remove_special_line()) { p_len[p_end] -= 1; (p_line[p_end])[p_len[p_end]] = 0; } } } else if (diff_type == UNI_DIFF) { LINENUM fillold; /* index of old lines */ LINENUM fillnew; /* index of new lines */ char ch; line_beginning = ftello(pfp); /* file pos of the current line */ len = pgets(true); p_input_line++; if (len == 0 || strnNE(buf, "@@ -", 4)) { next_intuit_at(line_beginning, p_input_line); return false; } s = buf + 4; if (!*s) malformed(); p_first = strtolinenum(s, &s); if (*s == ',') { p_ptrn_lines = strtolinenum(s + 1, &s); } else p_ptrn_lines = 1; if (*s == ' ') s++; if (*s != '+' || !*++s) malformed(); p_newfirst = strtolinenum(s, &s); if (*s == ',') { p_repl_lines = strtolinenum(s + 1, &s); } else p_repl_lines = 1; if (*s == ' ') s++; if (*s != '@') malformed(); if (p_first >= LINENUM_MAX - p_ptrn_lines || p_newfirst > LINENUM_MAX - p_repl_lines || p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1) malformed(); if (!p_ptrn_lines) p_first++; /* do append rather than insert */ p_max = p_ptrn_lines + p_repl_lines + 1; while (p_max >= hunkmax) grow_hunkmax(); fillold = 1; fillnew = fillold + p_ptrn_lines; p_end = fillnew + p_repl_lines; snprintf(buf, buf_size, "*** %ld,%ld ****\n", p_first, p_first + p_ptrn_lines - 1); p_line[0] = savestr(buf); if (out_of_mem) { p_end = -1; return false; } p_char[0] = '*'; snprintf(buf, buf_size, "--- %ld,%ld ----\n", p_newfirst, p_newfirst + p_repl_lines - 1); p_line[fillnew] = savestr(buf); if (out_of_mem) { p_end = 0; return false; } p_char[fillnew++] = '='; p_context = 100; context = 0; p_hunk_beg = p_input_line + 1; while (fillold <= p_ptrn_lines || fillnew <= p_end) { line_beginning = ftello(pfp); len = pgets(true); p_input_line++; if (len == 0) { if (p_max - fillnew < 3) { /* assume blank lines got chopped */ strlcpy(buf, " \n", buf_size); } else { fatal("unexpected end of file in patch\n"); } } if (*buf == '\t' || *buf == '\n') { ch = ' '; /* assume the space got eaten */ s = savestr(buf); } else { ch = *buf; s = savestr(buf + 1); } if (out_of_mem) { while (--fillnew > p_ptrn_lines) free(p_line[fillnew]); p_end = fillold - 1; return false; } switch (ch) { case '-': if (fillold > p_ptrn_lines) { free(s); p_end = fillnew - 1; malformed(); } p_char[fillold] = ch; p_line[fillold] = s; p_len[fillold++] = strlen(s); if (fillold > p_ptrn_lines) { if (remove_special_line()) { p_len[fillold - 1] -= 1; s[p_len[fillold - 1]] = 0; } } break; case '=': ch = ' '; /* FALL THROUGH */ case ' ': if (fillold > p_ptrn_lines) { free(s); while (--fillnew > p_ptrn_lines) free(p_line[fillnew]); p_end = fillold - 1; malformed(); } context++; p_char[fillold] = ch; p_line[fillold] = s; p_len[fillold++] = strlen(s); s = savestr(s); if (out_of_mem) { while (--fillnew > p_ptrn_lines) free(p_line[fillnew]); p_end = fillold - 1; return false; } if (fillold > p_ptrn_lines) { if (remove_special_line()) { p_len[fillold - 1] -= 1; s[p_len[fillold - 1]] = 0; } } /* FALL THROUGH */ case '+': if (fillnew > p_end) { free(s); while (--fillnew > p_ptrn_lines) free(p_line[fillnew]); p_end = fillold - 1; malformed(); } p_char[fillnew] = ch; p_line[fillnew] = s; p_len[fillnew++] = strlen(s); if (fillold > p_ptrn_lines) { if (remove_special_line()) { p_len[fillnew - 1] -= 1; s[p_len[fillnew - 1]] = 0; } } break; default: p_end = fillnew; malformed(); } if (ch != ' ' && context > 0) { if (context < p_context) p_context = context; context = -1000; } } /* while */ } else { /* normal diff--fake it up */ char hunk_type; int i; LINENUM min, max; line_beginning = ftello(pfp); p_context = 0; len = pgets(true); p_input_line++; if (len == 0 || !isdigit((unsigned char)*buf)) { next_intuit_at(line_beginning, p_input_line); return false; } p_first = strtolinenum(buf, &s); if (*s == ',') { p_ptrn_lines = strtolinenum(s + 1, &s) - p_first + 1; if (p_ptrn_lines < 0) malformed(); } else p_ptrn_lines = (*s != 'a'); hunk_type = *s; if (hunk_type == 'a') p_first++; /* do append rather than insert */ min = strtolinenum(s + 1, &s); if (*s == ',') max = strtolinenum(s + 1, &s); else max = min; if (min < 0 || min > max || max - min == LINENUM_MAX) malformed(); if (hunk_type == 'd') min++; p_newfirst = min; p_repl_lines = max - min + 1; if (p_newfirst > LINENUM_MAX - p_repl_lines || p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1) malformed(); p_end = p_ptrn_lines + p_repl_lines + 1; if (p_end > MAXHUNKSIZE) fatal("hunk too large (%ld lines) at line %ld: %s", p_end, p_input_line, buf); while (p_end >= hunkmax) grow_hunkmax(); snprintf(buf, buf_size, "*** %ld,%ld\n", p_first, p_first + p_ptrn_lines - 1); p_line[0] = savestr(buf); if (out_of_mem) { p_end = -1; return false; } p_char[0] = '*'; for (i = 1; i <= p_ptrn_lines; i++) { len = pgets(true); p_input_line++; if (len == 0) fatal("unexpected end of file in patch at line %ld\n", p_input_line); if (*buf != '<') fatal("< expected at line %ld of patch\n", p_input_line); p_line[i] = savestr(buf + 2); if (out_of_mem) { p_end = i - 1; return false; } p_len[i] = strlen(p_line[i]); p_char[i] = '-'; } if (remove_special_line()) { p_len[i - 1] -= 1; (p_line[i - 1])[p_len[i - 1]] = 0; } if (hunk_type == 'c') { len = pgets(true); p_input_line++; if (len == 0) fatal("unexpected end of file in patch at line %ld\n", p_input_line); if (*buf != '-') fatal("--- expected at line %ld of patch\n", p_input_line); } snprintf(buf, buf_size, "--- %ld,%ld\n", min, max); p_line[i] = savestr(buf); if (out_of_mem) { p_end = i - 1; return false; } p_char[i] = '='; for (i++; i <= p_end; i++) { len = pgets(true); p_input_line++; if (len == 0) fatal("unexpected end of file in patch at line %ld\n", p_input_line); if (*buf != '>') fatal("> expected at line %ld of patch\n", p_input_line); p_line[i] = savestr(buf + 2); if (out_of_mem) { p_end = i - 1; return false; } p_len[i] = strlen(p_line[i]); p_char[i] = '+'; } if (remove_special_line()) { p_len[i - 1] -= 1; (p_line[i - 1])[p_len[i - 1]] = 0; } } if (reverse) /* backwards patch? */ if (!pch_swap()) say("Not enough memory to swap next hunk!\n"); #ifdef DEBUGGING if (debug & 2) { int i; char special; for (i = 0; i <= p_end; i++) { if (i == p_ptrn_lines) special = '^'; else special = ' '; fprintf(stderr, "%3d %c %c %s", i, p_char[i], special, p_line[i]); fflush(stderr); } } #endif if (p_end + 1 < hunkmax)/* paranoia reigns supreme... */ p_char[p_end + 1] = '^'; /* add a stopper for apply_hunk */ return true; } /* * Input a line from the patch file. * Worry about indentation if do_indent is true. * The line is read directly into the buf global variable which * is resized if necessary in order to hold the complete line. * Returns the number of characters read including the terminating * '\n', if any. */ size_t pgets(bool do_indent) { char *line; size_t len; int indent = 0, skipped = 0; line = fgetln(pfp, &len); if (line != NULL) { if (len + 1 > buf_size) { while (len + 1 > buf_size) buf_size *= 2; free(buf); buf = malloc(buf_size); if (buf == NULL) fatal("out of memory\n"); } if (do_indent == 1 && p_indent) { for (; indent < p_indent && (*line == ' ' || *line == '\t' || *line == 'X'); line++, skipped++) { if (*line == '\t') indent += 8 - (indent %7); else indent++; } } memcpy(buf, line, len - skipped); buf[len - skipped] = '\0'; } return len; } /* * Reverse the old and new portions of the current hunk. */ bool pch_swap(void) { char **tp_line; /* the text of the hunk */ unsigned short *tp_len;/* length of each line */ char *tp_char; /* +, -, and ! */ LINENUM i; LINENUM n; bool blankline = false; char *s; i = p_first; p_first = p_newfirst; p_newfirst = i; /* make a scratch copy */ tp_line = p_line; tp_len = p_len; tp_char = p_char; p_line = NULL; /* force set_hunkmax to allocate again */ p_len = NULL; p_char = NULL; set_hunkmax(); if (p_line == NULL || p_len == NULL || p_char == NULL) { free(p_line); p_line = tp_line; free(p_len); p_len = tp_len; free(p_char); p_char = tp_char; return false; /* not enough memory to swap hunk! */ } /* now turn the new into the old */ i = p_ptrn_lines + 1; if (tp_char[i] == '\n') { /* account for possible blank line */ blankline = true; i++; } if (p_efake >= 0) { /* fix non-freeable ptr range */ if (p_efake <= i) n = p_end - i + 1; else n = -i; p_efake += n; p_bfake += n; } for (n = 0; i <= p_end; i++, n++) { p_line[n] = tp_line[i]; p_char[n] = tp_char[i]; if (p_char[n] == '+') p_char[n] = '-'; p_len[n] = tp_len[i]; } if (blankline) { i = p_ptrn_lines + 1; p_line[n] = tp_line[i]; p_char[n] = tp_char[i]; p_len[n] = tp_len[i]; n++; } if (p_char[0] != '=') fatal("Malformed patch at line %ld: expected '=' found '%c'\n", p_input_line, p_char[0]); p_char[0] = '*'; for (s = p_line[0]; *s; s++) if (*s == '-') *s = '*'; /* now turn the old into the new */ if (p_char[0] != '*') fatal("Malformed patch at line %ld: expected '*' found '%c'\n", p_input_line, p_char[0]); tp_char[0] = '='; for (s = tp_line[0]; *s; s++) if (*s == '*') *s = '-'; for (i = 0; n <= p_end; i++, n++) { p_line[n] = tp_line[i]; p_char[n] = tp_char[i]; if (p_char[n] == '-') p_char[n] = '+'; p_len[n] = tp_len[i]; } if (i != p_ptrn_lines + 1) fatal("Malformed patch at line %ld: expected %ld lines, " "got %ld\n", p_input_line, p_ptrn_lines + 1, i); i = p_ptrn_lines; p_ptrn_lines = p_repl_lines; p_repl_lines = i; free(tp_line); free(tp_len); free(tp_char); return true; } /* * Return the specified line position in the old file of the old context. */ LINENUM pch_first(void) { return p_first; } /* * Return the number of lines of old context. */ LINENUM pch_ptrn_lines(void) { return p_ptrn_lines; } /* * Return the probable line position in the new file of the first line. */ LINENUM pch_newfirst(void) { return p_newfirst; } /* * Return the number of lines in the replacement text including context. */ LINENUM pch_repl_lines(void) { return p_repl_lines; } /* * Return the number of lines in the whole hunk. */ LINENUM pch_end(void) { return p_end; } /* * Return the number of context lines before the first changed line. */ LINENUM pch_context(void) { return p_context; } /* * Return the length of a particular patch line. */ unsigned short pch_line_len(LINENUM line) { return p_len[line]; } /* * Return the control character (+, -, *, !, etc) for a patch line. */ char pch_char(LINENUM line) { return p_char[line]; } /* * Return a pointer to a particular patch line. */ char * pfetch(LINENUM line) { return p_line[line]; } /* * Return where in the patch file this hunk began, for error messages. */ LINENUM pch_hunk_beg(void) { return p_hunk_beg; } /* * Apply an ed script by feeding ed itself. */ void do_ed_script(void) { char *t; off_t beginning_of_this_line; FILE *pipefp = NULL; int continuation; if (!skip_rest_of_patch) { if (copy_file(filearg[0], TMPOUTNAME) < 0) { unlink(TMPOUTNAME); fatal("can't create temp file %s", TMPOUTNAME); } snprintf(buf, buf_size, "%s%s%s", _PATH_RED, verbose ? " " : " -s ", TMPOUTNAME); pipefp = popen(buf, "w"); } for (;;) { beginning_of_this_line = ftello(pfp); if (pgets(true) == 0) { next_intuit_at(beginning_of_this_line, p_input_line); break; } p_input_line++; for (t = buf; isdigit((unsigned char)*t) || *t == ','; t++) ; /* POSIX defines allowed commands as {a,c,d,i,s} */ if (isdigit((unsigned char)*buf) && (*t == 'a' || *t == 'c' || *t == 'd' || *t == 'i' || *t == 's')) { if (pipefp != NULL) fputs(buf, pipefp); if (*t == 's') { for (;;) { continuation = 0; t = strchr(buf, '\0') - 1; while (--t >= buf && *t == '\\') continuation = !continuation; if (!continuation || pgets(true) == 0) break; if (pipefp != NULL) fputs(buf, pipefp); } } else if (*t != 'd') { while (pgets(true)) { p_input_line++; if (pipefp != NULL) fputs(buf, pipefp); if (strEQ(buf, ".\n")) break; } } } else { next_intuit_at(beginning_of_this_line, p_input_line); break; } } if (pipefp == NULL) return; fprintf(pipefp, "w\n"); fprintf(pipefp, "q\n"); fflush(pipefp); pclose(pipefp); ignore_signals(); if (!check_only) { if (move_file(TMPOUTNAME, outname) < 0) { toutkeep = true; chmod(TMPOUTNAME, filemode); } else chmod(outname, filemode); } set_signals(1); } /* * Choose the name of the file to be patched based on POSIX rules. * NOTE: the POSIX rules are amazingly stupid and we only follow them * if the user specified --posix or set POSIXLY_CORRECT. */ static char * posix_name(const struct file_name *names, bool assume_exists) { char *path = NULL; int i; /* * POSIX states that the filename will be chosen from one * of the old, new and index names (in that order) if * the file exists relative to CWD after -p stripping. */ for (i = 0; i < MAX_FILE; i++) { if (names[i].path != NULL && names[i].exists) { path = names[i].path; break; } } if (path == NULL && !assume_exists) { /* * No files found, check to see if the diff could be * creating a new file. */ if (path == NULL && ok_to_create_file && names[NEW_FILE].path != NULL) path = names[NEW_FILE].path; } return path ? xstrdup(path) : NULL; } static char * compare_names(const struct file_name *names, bool assume_exists) { size_t min_components, min_baselen, min_len, tmp; char *best = NULL; char *path; int i; /* * The "best" name is the one with the fewest number of path * components, the shortest basename length, and the shortest * overall length (in that order). We only use the Index: file * if neither of the old or new files could be intuited from * the diff header. */ min_components = min_baselen = min_len = SIZE_MAX; for (i = INDEX_FILE; i >= OLD_FILE; i--) { path = names[i].path; if (path == NULL || (!names[i].exists && !assume_exists)) continue; if ((tmp = num_components(path)) > min_components) continue; if (tmp < min_components) { min_components = tmp; best = path; } if ((tmp = strlen(basename(path))) > min_baselen) continue; if (tmp < min_baselen) { min_baselen = tmp; best = path; } if ((tmp = strlen(path)) > min_len) continue; min_len = tmp; best = path; } return best; } /* * Choose the name of the file to be patched based the "best" one * available. */ static char * best_name(const struct file_name *names, bool assume_exists) { char *best; best = compare_names(names, assume_exists); /* No match? Check to see if the diff could be creating a new file. */ if (best == NULL && ok_to_create_file) best = names[NEW_FILE].path; return best ? xstrdup(best) : NULL; } static size_t num_components(const char *path) { size_t n; const char *cp; for (n = 0, cp = path; (cp = strchr(cp, '/')) != NULL; n++, cp++) { while (*cp == '/') cp++; /* skip consecutive slashes */ } return n; } /* * Convert number at NPTR into LINENUM and save address of first * character that is not a digit in ENDPTR. If conversion is not * possible, call fatal. */ static LINENUM strtolinenum(char *nptr, char **endptr) { LINENUM rv; char c; char *p; const char *errstr; for (p = nptr; isdigit((unsigned char)*p); p++) ; if (p == nptr) malformed(); c = *p; *p = '\0'; rv = strtonum(nptr, 0, LINENUM_MAX, &errstr); if (errstr != NULL) fatal("invalid line number at line %ld: `%s' is %s\n", p_input_line, nptr, errstr); *p = c; *endptr = p; return rv; } Index: head/usr.bin/pr/egetopt.c =================================================================== --- head/usr.bin/pr/egetopt.c (revision 289676) +++ head/usr.bin/pr/egetopt.c (revision 289677) @@ -1,217 +1,217 @@ /*- * Copyright (c) 1991 Keith Muller. * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * 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 0 #ifndef lint static char sccsid[] = "@(#)egetopt.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include "extern.h" /* * egetopt: get option letter from argument vector (an extended * version of getopt). * * Non standard additions to the ostr specs are: * 1) '?': immediate value following arg is optional (no white space * between the arg and the value) * 2) '#': +/- followed by a number (with an optional sign but * no white space between the arg and the number). The - may be * combined with other options, but the + cannot. */ int eopterr = 1; /* if error message should be printed */ int eoptind = 1; /* index into parent argv vector */ int eoptopt; /* character checked for validity */ char *eoptarg; /* argument associated with option */ #define BADCH (int)'?' static char emsg[] = ""; int egetopt(int nargc, char * const *nargv, const char *ostr) { static char *place = emsg; /* option letter processing */ char *oli; /* option letter list index */ - static int delim; /* which option delimeter */ + static int delim; /* which option delimiter */ char *p; static char savec = '\0'; if (savec != '\0') { *place = savec; savec = '\0'; } if (!*place) { /* * update scanning pointer */ if ((eoptind >= nargc) || ((*(place = nargv[eoptind]) != '-') && (*place != '+'))) { place = emsg; return (-1); } delim = (int)*place; if (place[1] && *++place == '-' && !place[1]) { /* * found "--" */ ++eoptind; place = emsg; return (-1); } } /* * check option letter */ if ((eoptopt = (int)*place++) == (int)':' || (eoptopt == (int)'?') || !(oli = strchr(ostr, eoptopt))) { /* * if the user didn't specify '-' as an option, * assume it means -1 when by itself. */ if ((eoptopt == (int)'-') && !*place) return (-1); if (strchr(ostr, '#') && (isdigit(eoptopt) || (((eoptopt == (int)'-') || (eoptopt == (int)'+')) && isdigit(*place)))) { /* * # option: +/- with a number is ok */ for (p = place; *p != '\0'; ++p) { if (!isdigit(*p)) break; } eoptarg = place-1; if (*p == '\0') { place = emsg; ++eoptind; } else { place = p; savec = *p; *place = '\0'; } return (delim); } if (!*place) ++eoptind; if (eopterr) { if (!(p = strrchr(*nargv, '/'))) p = *nargv; else ++p; (void)fprintf(stderr, "%s: illegal option -- %c\n", p, eoptopt); } return (BADCH); } if (delim == (int)'+') { /* * '+' is only allowed with numbers */ if (!*place) ++eoptind; if (eopterr) { if (!(p = strrchr(*nargv, '/'))) p = *nargv; else ++p; (void)fprintf(stderr, "%s: illegal '+' delimiter with option -- %c\n", p, eoptopt); } return (BADCH); } ++oli; if ((*oli != ':') && (*oli != '?')) { /* * don't need argument */ eoptarg = NULL; if (!*place) ++eoptind; return (eoptopt); } if (*place) { /* * no white space */ eoptarg = place; } else if (*oli == '?') { /* * no arg, but NOT required */ eoptarg = NULL; } else if (nargc <= ++eoptind) { /* * no arg, but IS required */ place = emsg; if (eopterr) { if (!(p = strrchr(*nargv, '/'))) p = *nargv; else ++p; (void)fprintf(stderr, "%s: option requires an argument -- %c\n", p, eoptopt); } return (BADCH); } else { /* * arg has white space */ eoptarg = nargv[eoptind]; } place = emsg; ++eoptind; return (eoptopt); } Index: head/usr.bin/sed/compile.c =================================================================== --- head/usr.bin/sed/compile.c (revision 289676) +++ head/usr.bin/sed/compile.c (revision 289677) @@ -1,950 +1,950 @@ /*- * Copyright (c) 1992 Diomidis Spinellis. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Diomidis Spinellis of Imperial College, University of London. * * 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. * 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. */ #include __FBSDID("$FreeBSD$"); #ifndef lint static const char sccsid[] = "@(#)compile.c 8.1 (Berkeley) 6/6/93"; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "defs.h" #include "extern.h" #define LHSZ 128 #define LHMASK (LHSZ - 1) static struct labhash { struct labhash *lh_next; u_int lh_hash; struct s_command *lh_cmd; int lh_ref; } *labels[LHSZ]; static char *compile_addr(char *, struct s_addr *); static char *compile_ccl(char **, char *); static char *compile_delimited(char *, char *, int); static char *compile_flags(char *, struct s_subst *); static regex_t *compile_re(char *, int); static char *compile_subst(char *, struct s_subst *); static char *compile_text(void); static char *compile_tr(char *, struct s_tr **); static struct s_command **compile_stream(struct s_command **); static char *duptoeol(char *, const char *); static void enterlabel(struct s_command *); static struct s_command *findlabel(char *); static void fixuplabel(struct s_command *, struct s_command *); static void uselabel(void); /* * Command specification. This is used to drive the command parser. */ struct s_format { char code; /* Command code */ int naddr; /* Number of address args */ enum e_args args; /* Argument type */ }; static struct s_format cmd_fmts[] = { {'{', 2, GROUP}, {'}', 0, ENDGROUP}, {'a', 1, TEXT}, {'b', 2, BRANCH}, {'c', 2, TEXT}, {'d', 2, EMPTY}, {'D', 2, EMPTY}, {'g', 2, EMPTY}, {'G', 2, EMPTY}, {'h', 2, EMPTY}, {'H', 2, EMPTY}, {'i', 1, TEXT}, {'l', 2, EMPTY}, {'n', 2, EMPTY}, {'N', 2, EMPTY}, {'p', 2, EMPTY}, {'P', 2, EMPTY}, {'q', 1, EMPTY}, {'r', 1, RFILE}, {'s', 2, SUBST}, {'t', 2, BRANCH}, {'w', 2, WFILE}, {'x', 2, EMPTY}, {'y', 2, TR}, {'!', 2, NONSEL}, {':', 0, LABEL}, {'#', 0, COMMENT}, {'=', 1, EMPTY}, {'\0', 0, COMMENT}, }; /* The compiled program. */ struct s_command *prog; /* * Compile the program into prog. * Initialise appends. */ void compile(void) { *compile_stream(&prog) = NULL; fixuplabel(prog, NULL); uselabel(); if (appendnum == 0) appends = NULL; else if ((appends = malloc(sizeof(struct s_appends) * appendnum)) == NULL) err(1, "malloc"); if ((match = malloc((maxnsub + 1) * sizeof(regmatch_t))) == NULL) err(1, "malloc"); } #define EATSPACE() do { \ if (p) \ while (*p && isspace((unsigned char)*p)) \ p++; \ } while (0) static struct s_command ** compile_stream(struct s_command **link) { char *p; static char lbuf[_POSIX2_LINE_MAX + 1]; /* To save stack */ struct s_command *cmd, *cmd2, *stack; struct s_format *fp; char re[_POSIX2_LINE_MAX + 1]; int naddr; /* Number of addresses */ stack = 0; for (;;) { if ((p = cu_fgets(lbuf, sizeof(lbuf), NULL)) == NULL) { if (stack != 0) errx(1, "%lu: %s: unexpected EOF (pending }'s)", linenum, fname); return (link); } semicolon: EATSPACE(); if (p) { if (*p == '#' || *p == '\0') continue; else if (*p == ';') { p++; goto semicolon; } } if ((*link = cmd = malloc(sizeof(struct s_command))) == NULL) err(1, "malloc"); link = &cmd->next; cmd->startline = cmd->nonsel = 0; /* First parse the addresses */ naddr = 0; /* Valid characters to start an address */ #define addrchar(c) (strchr("0123456789/\\$", (c))) if (addrchar(*p)) { naddr++; if ((cmd->a1 = malloc(sizeof(struct s_addr))) == NULL) err(1, "malloc"); p = compile_addr(p, cmd->a1); EATSPACE(); /* EXTENSION */ if (*p == ',') { p++; EATSPACE(); /* EXTENSION */ naddr++; if ((cmd->a2 = malloc(sizeof(struct s_addr))) == NULL) err(1, "malloc"); p = compile_addr(p, cmd->a2); EATSPACE(); } else cmd->a2 = 0; } else cmd->a1 = cmd->a2 = 0; nonsel: /* Now parse the command */ if (!*p) errx(1, "%lu: %s: command expected", linenum, fname); cmd->code = *p; for (fp = cmd_fmts; fp->code; fp++) if (fp->code == *p) break; if (!fp->code) errx(1, "%lu: %s: invalid command code %c", linenum, fname, *p); if (naddr > fp->naddr) errx(1, "%lu: %s: command %c expects up to %d address(es), found %d", linenum, fname, *p, fp->naddr, naddr); switch (fp->args) { case NONSEL: /* ! */ p++; EATSPACE(); cmd->nonsel = 1; goto nonsel; case GROUP: /* { */ p++; EATSPACE(); cmd->next = stack; stack = cmd; link = &cmd->u.c; if (*p) goto semicolon; break; case ENDGROUP: /* * Short-circuit command processing, since end of * group is really just a noop. */ cmd->nonsel = 1; if (stack == 0) errx(1, "%lu: %s: unexpected }", linenum, fname); cmd2 = stack; stack = cmd2->next; cmd2->next = cmd; /*FALLTHROUGH*/ case EMPTY: /* d D g G h H l n N p P q x = \0 */ p++; EATSPACE(); if (*p == ';') { p++; link = &cmd->next; goto semicolon; } if (*p) errx(1, "%lu: %s: extra characters at the end of %c command", linenum, fname, cmd->code); break; case TEXT: /* a c i */ p++; EATSPACE(); if (*p != '\\') errx(1, "%lu: %s: command %c expects \\ followed by text", linenum, fname, cmd->code); p++; EATSPACE(); if (*p) errx(1, "%lu: %s: extra characters after \\ at the end of %c command", linenum, fname, cmd->code); cmd->t = compile_text(); break; case COMMENT: /* \0 # */ break; case WFILE: /* w */ p++; EATSPACE(); if (*p == '\0') errx(1, "%lu: %s: filename expected", linenum, fname); cmd->t = duptoeol(p, "w command"); if (aflag) cmd->u.fd = -1; else if ((cmd->u.fd = open(p, O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1) err(1, "%s", p); break; case RFILE: /* r */ p++; EATSPACE(); if (*p == '\0') errx(1, "%lu: %s: filename expected", linenum, fname); else cmd->t = duptoeol(p, "read command"); break; case BRANCH: /* b t */ p++; EATSPACE(); if (*p == '\0') cmd->t = NULL; else cmd->t = duptoeol(p, "branch"); break; case LABEL: /* : */ p++; EATSPACE(); cmd->t = duptoeol(p, "label"); if (strlen(p) == 0) errx(1, "%lu: %s: empty label", linenum, fname); enterlabel(cmd); break; case SUBST: /* s */ p++; if (*p == '\0' || *p == '\\') errx(1, "%lu: %s: substitute pattern can not be delimited by newline or backslash", linenum, fname); if ((cmd->u.s = calloc(1, sizeof(struct s_subst))) == NULL) err(1, "malloc"); p = compile_delimited(p, re, 0); if (p == NULL) errx(1, "%lu: %s: unterminated substitute pattern", linenum, fname); /* Compile RE with no case sensitivity temporarily */ if (*re == '\0') cmd->u.s->re = NULL; else cmd->u.s->re = compile_re(re, 0); --p; p = compile_subst(p, cmd->u.s); p = compile_flags(p, cmd->u.s); /* Recompile RE with case sensitivity from "I" flag if any */ if (*re == '\0') cmd->u.s->re = NULL; else cmd->u.s->re = compile_re(re, cmd->u.s->icase); EATSPACE(); if (*p == ';') { p++; link = &cmd->next; goto semicolon; } break; case TR: /* y */ p++; p = compile_tr(p, &cmd->u.y); EATSPACE(); if (*p == ';') { p++; link = &cmd->next; goto semicolon; } if (*p) errx(1, "%lu: %s: extra text at the end of a transform command", linenum, fname); break; } } } /* - * Get a delimited string. P points to the delimeter of the string; d points + * Get a delimited string. P points to the delimiter of the string; d points * to a buffer area. Newline and delimiter escapes are processed; other * escapes are ignored. * * Returns a pointer to the first character after the final delimiter or NULL * in the case of a non-terminated string. The character array d is filled * with the processed string. */ static char * compile_delimited(char *p, char *d, int is_tr) { char c; c = *p++; if (c == '\0') return (NULL); else if (c == '\\') errx(1, "%lu: %s: \\ can not be used as a string delimiter", linenum, fname); else if (c == '\n') errx(1, "%lu: %s: newline can not be used as a string delimiter", linenum, fname); while (*p) { if (*p == '[' && *p != c) { if ((d = compile_ccl(&p, d)) == NULL) errx(1, "%lu: %s: unbalanced brackets ([])", linenum, fname); continue; } else if (*p == '\\' && p[1] == '[') { *d++ = *p++; } else if (*p == '\\' && p[1] == c) p++; else if (*p == '\\' && p[1] == 'n') { *d++ = '\n'; p += 2; continue; } else if (*p == '\\' && p[1] == '\\') { if (is_tr) p++; else *d++ = *p++; } else if (*p == c) { *d = '\0'; return (p + 1); } *d++ = *p++; } return (NULL); } /* compile_ccl: expand a POSIX character class */ static char * compile_ccl(char **sp, char *t) { int c, d; char *s = *sp; *t++ = *s++; if (*s == '^') *t++ = *s++; if (*s == ']') *t++ = *s++; for (; *s && (*t = *s) != ']'; s++, t++) if (*s == '[' && ((d = *(s+1)) == '.' || d == ':' || d == '=')) { *++t = *++s, t++, s++; for (c = *s; (*t = *s) != ']' || c != d; s++, t++) if ((c = *s) == '\0') return NULL; } return (*s == ']') ? *sp = ++s, ++t : NULL; } /* * Compiles the regular expression in RE and returns a pointer to the compiled * regular expression. * Cflags are passed to regcomp. */ static regex_t * compile_re(char *re, int case_insensitive) { regex_t *rep; int eval, flags; flags = rflags; if (case_insensitive) flags |= REG_ICASE; if ((rep = malloc(sizeof(regex_t))) == NULL) err(1, "malloc"); if ((eval = regcomp(rep, re, flags)) != 0) errx(1, "%lu: %s: RE error: %s", linenum, fname, strregerror(eval, rep)); if (maxnsub < rep->re_nsub) maxnsub = rep->re_nsub; return (rep); } /* * Compile the substitution string of a regular expression and set res to * point to a saved copy of it. Nsub is the number of parenthesized regular * expressions. */ static char * compile_subst(char *p, struct s_subst *s) { static char lbuf[_POSIX2_LINE_MAX + 1]; int asize, size; u_char ref; char c, *text, *op, *sp; int more = 1, sawesc = 0; c = *p++; /* Terminator character */ if (c == '\0') return (NULL); s->maxbref = 0; s->linenum = linenum; asize = 2 * _POSIX2_LINE_MAX + 1; if ((text = malloc(asize)) == NULL) err(1, "malloc"); size = 0; do { op = sp = text + size; for (; *p; p++) { if (*p == '\\' || sawesc) { /* * If this is a continuation from the last * buffer, we won't have a character to * skip over. */ if (sawesc) sawesc = 0; else p++; if (*p == '\0') { /* * This escaped character is continued * in the next part of the line. Note * this fact, then cause the loop to * exit w/ normal EOL case and reenter * above with the new buffer. */ sawesc = 1; p--; continue; } else if (strchr("123456789", *p) != NULL) { *sp++ = '\\'; ref = *p - '0'; if (s->re != NULL && ref > s->re->re_nsub) errx(1, "%lu: %s: \\%c not defined in the RE", linenum, fname, *p); if (s->maxbref < ref) s->maxbref = ref; } else if (*p == '&' || *p == '\\') *sp++ = '\\'; } else if (*p == c) { if (*++p == '\0' && more) { if (cu_fgets(lbuf, sizeof(lbuf), &more)) p = lbuf; } *sp++ = '\0'; size += sp - op; if ((s->new = realloc(text, size)) == NULL) err(1, "realloc"); return (p); } else if (*p == '\n') { errx(1, "%lu: %s: unescaped newline inside substitute pattern", linenum, fname); /* NOTREACHED */ } *sp++ = *p; } size += sp - op; if (asize - size < _POSIX2_LINE_MAX + 1) { asize *= 2; if ((text = realloc(text, asize)) == NULL) err(1, "realloc"); } } while (cu_fgets(p = lbuf, sizeof(lbuf), &more)); errx(1, "%lu: %s: unterminated substitute in regular expression", linenum, fname); /* NOTREACHED */ } /* * Compile the flags of the s command */ static char * compile_flags(char *p, struct s_subst *s) { int gn; /* True if we have seen g or n */ unsigned long nval; char wfile[_POSIX2_LINE_MAX + 1], *q, *eq; s->n = 1; /* Default */ s->p = 0; s->wfile = NULL; s->wfd = -1; s->icase = 0; for (gn = 0;;) { EATSPACE(); /* EXTENSION */ switch (*p) { case 'g': if (gn) errx(1, "%lu: %s: more than one number or 'g' in substitute flags", linenum, fname); gn = 1; s->n = 0; break; case '\0': case '\n': case ';': return (p); case 'p': s->p = 1; break; case 'i': case 'I': s->icase = 1; break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (gn) errx(1, "%lu: %s: more than one number or 'g' in substitute flags", linenum, fname); gn = 1; errno = 0; nval = strtol(p, &p, 10); if (errno == ERANGE || nval > INT_MAX) errx(1, "%lu: %s: overflow in the 'N' substitute flag", linenum, fname); s->n = nval; p--; break; case 'w': p++; #ifdef HISTORIC_PRACTICE if (*p != ' ') { warnx("%lu: %s: space missing before w wfile", linenum, fname); return (p); } #endif EATSPACE(); q = wfile; eq = wfile + sizeof(wfile) - 1; while (*p) { if (*p == '\n') break; if (q >= eq) err(1, "wfile too long"); *q++ = *p++; } *q = '\0'; if (q == wfile) errx(1, "%lu: %s: no wfile specified", linenum, fname); s->wfile = strdup(wfile); if (!aflag && (s->wfd = open(wfile, O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1) err(1, "%s", wfile); return (p); default: errx(1, "%lu: %s: bad flag in substitute command: '%c'", linenum, fname, *p); break; } p++; } } /* * Compile a translation set of strings into a lookup table. */ static char * compile_tr(char *p, struct s_tr **py) { struct s_tr *y; int i; const char *op, *np; char old[_POSIX2_LINE_MAX + 1]; char new[_POSIX2_LINE_MAX + 1]; size_t oclen, oldlen, nclen, newlen; mbstate_t mbs1, mbs2; if ((*py = y = malloc(sizeof(*y))) == NULL) err(1, NULL); y->multis = NULL; y->nmultis = 0; if (*p == '\0' || *p == '\\') errx(1, "%lu: %s: transform pattern can not be delimited by newline or backslash", linenum, fname); p = compile_delimited(p, old, 1); if (p == NULL) errx(1, "%lu: %s: unterminated transform source string", linenum, fname); p = compile_delimited(p - 1, new, 1); if (p == NULL) errx(1, "%lu: %s: unterminated transform target string", linenum, fname); EATSPACE(); op = old; oldlen = mbsrtowcs(NULL, &op, 0, NULL); if (oldlen == (size_t)-1) err(1, NULL); np = new; newlen = mbsrtowcs(NULL, &np, 0, NULL); if (newlen == (size_t)-1) err(1, NULL); if (newlen != oldlen) errx(1, "%lu: %s: transform strings are not the same length", linenum, fname); if (MB_CUR_MAX == 1) { /* * The single-byte encoding case is easy: generate a * lookup table. */ for (i = 0; i <= UCHAR_MAX; i++) y->bytetab[i] = (char)i; for (; *op; op++, np++) y->bytetab[(u_char)*op] = *np; } else { /* * Multi-byte encoding case: generate a lookup table as * above, but only for single-byte characters. The first * bytes of multi-byte characters have their lookup table * entries set to 0, which causes do_tr() to search through * an auxiliary vector of multi-byte mappings. */ memset(&mbs1, 0, sizeof(mbs1)); memset(&mbs2, 0, sizeof(mbs2)); for (i = 0; i <= UCHAR_MAX; i++) y->bytetab[i] = (btowc(i) != WEOF) ? i : 0; while (*op != '\0') { oclen = mbrlen(op, MB_LEN_MAX, &mbs1); if (oclen == (size_t)-1 || oclen == (size_t)-2) errc(1, EILSEQ, NULL); nclen = mbrlen(np, MB_LEN_MAX, &mbs2); if (nclen == (size_t)-1 || nclen == (size_t)-2) errc(1, EILSEQ, NULL); if (oclen == 1 && nclen == 1) y->bytetab[(u_char)*op] = *np; else { y->bytetab[(u_char)*op] = 0; y->multis = realloc(y->multis, (y->nmultis + 1) * sizeof(*y->multis)); if (y->multis == NULL) err(1, NULL); i = y->nmultis++; y->multis[i].fromlen = oclen; memcpy(y->multis[i].from, op, oclen); y->multis[i].tolen = nclen; memcpy(y->multis[i].to, np, nclen); } op += oclen; np += nclen; } } return (p); } /* * Compile the text following an a or i command. */ static char * compile_text(void) { int asize, esc_nl, size; char *text, *p, *op, *s; char lbuf[_POSIX2_LINE_MAX + 1]; asize = 2 * _POSIX2_LINE_MAX + 1; if ((text = malloc(asize)) == NULL) err(1, "malloc"); size = 0; while (cu_fgets(lbuf, sizeof(lbuf), NULL)) { op = s = text + size; p = lbuf; EATSPACE(); for (esc_nl = 0; *p != '\0'; p++) { if (*p == '\\' && p[1] != '\0' && *++p == '\n') esc_nl = 1; *s++ = *p; } size += s - op; if (!esc_nl) { *s = '\0'; break; } if (asize - size < _POSIX2_LINE_MAX + 1) { asize *= 2; if ((text = realloc(text, asize)) == NULL) err(1, "realloc"); } } text[size] = '\0'; if ((p = realloc(text, size + 1)) == NULL) err(1, "realloc"); return (p); } /* * Get an address and return a pointer to the first character after * it. Fill the structure pointed to according to the address. */ static char * compile_addr(char *p, struct s_addr *a) { char *end, re[_POSIX2_LINE_MAX + 1]; int icase; icase = 0; a->type = 0; switch (*p) { case '\\': /* Context address */ ++p; /* FALLTHROUGH */ case '/': /* Context address */ p = compile_delimited(p, re, 0); if (p == NULL) errx(1, "%lu: %s: unterminated regular expression", linenum, fname); /* Check for case insensitive regexp flag */ if (*p == 'I') { icase = 1; p++; } if (*re == '\0') a->u.r = NULL; else a->u.r = compile_re(re, icase); a->type = AT_RE; return (p); case '$': /* Last line */ a->type = AT_LAST; return (p + 1); case '+': /* Relative line number */ a->type = AT_RELLINE; p++; /* FALLTHROUGH */ /* Line number */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (a->type == 0) a->type = AT_LINE; a->u.l = strtol(p, &end, 10); return (end); default: errx(1, "%lu: %s: expected context address", linenum, fname); return (NULL); } } /* * duptoeol -- * Return a copy of all the characters up to \n or \0. */ static char * duptoeol(char *s, const char *ctype) { size_t len; int ws; char *p, *start; ws = 0; for (start = s; *s != '\0' && *s != '\n'; ++s) ws = isspace((unsigned char)*s); *s = '\0'; if (ws) warnx("%lu: %s: whitespace after %s", linenum, fname, ctype); len = s - start + 1; if ((p = malloc(len)) == NULL) err(1, "malloc"); return (memmove(p, start, len)); } /* * Convert goto label names to addresses, and count a and r commands, in * the given subset of the script. Free the memory used by labels in b * and t commands (but not by :). * * TODO: Remove } nodes */ static void fixuplabel(struct s_command *cp, struct s_command *end) { for (; cp != end; cp = cp->next) switch (cp->code) { case 'a': case 'r': appendnum++; break; case 'b': case 't': /* Resolve branch target. */ if (cp->t == NULL) { cp->u.c = NULL; break; } if ((cp->u.c = findlabel(cp->t)) == NULL) errx(1, "%lu: %s: undefined label '%s'", linenum, fname, cp->t); free(cp->t); break; case '{': /* Do interior commands. */ fixuplabel(cp->u.c, cp->next); break; } } /* * Associate the given command label for later lookup. */ static void enterlabel(struct s_command *cp) { struct labhash **lhp, *lh; u_char *p; u_int h, c; for (h = 0, p = (u_char *)cp->t; (c = *p) != 0; p++) h = (h << 5) + h + c; lhp = &labels[h & LHMASK]; for (lh = *lhp; lh != NULL; lh = lh->lh_next) if (lh->lh_hash == h && strcmp(cp->t, lh->lh_cmd->t) == 0) errx(1, "%lu: %s: duplicate label '%s'", linenum, fname, cp->t); if ((lh = malloc(sizeof *lh)) == NULL) err(1, "malloc"); lh->lh_next = *lhp; lh->lh_hash = h; lh->lh_cmd = cp; lh->lh_ref = 0; *lhp = lh; } /* * Find the label contained in the command l in the command linked * list cp. L is excluded from the search. Return NULL if not found. */ static struct s_command * findlabel(char *name) { struct labhash *lh; u_char *p; u_int h, c; for (h = 0, p = (u_char *)name; (c = *p) != 0; p++) h = (h << 5) + h + c; for (lh = labels[h & LHMASK]; lh != NULL; lh = lh->lh_next) { if (lh->lh_hash == h && strcmp(name, lh->lh_cmd->t) == 0) { lh->lh_ref = 1; return (lh->lh_cmd); } } return (NULL); } /* * Warn about any unused labels. As a side effect, release the label hash * table space. */ static void uselabel(void) { struct labhash *lh, *next; int i; for (i = 0; i < LHSZ; i++) { for (lh = labels[i]; lh != NULL; lh = next) { next = lh->lh_next; if (!lh->lh_ref) warnx("%lu: %s: unused label '%s'", linenum, fname, lh->lh_cmd->t); free(lh); } } } Index: head/usr.bin/sockstat/sockstat.c =================================================================== --- head/usr.bin/sockstat/sockstat.c (revision 289676) +++ head/usr.bin/sockstat/sockstat.c (revision 289677) @@ -1,1218 +1,1218 @@ /*- * Copyright (c) 2002 Dag-Erling Coïdan Smørgrav * 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 * in this position and unchanged. * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 #include #include #define TCPSTATES /* load state names */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define sstosin(ss) ((struct sockaddr_in *)(ss)) #define sstosin6(ss) ((struct sockaddr_in6 *)(ss)) #define sstosun(ss) ((struct sockaddr_un *)(ss)) #define sstosa(ss) ((struct sockaddr *)(ss)) static int opt_4; /* Show IPv4 sockets */ static int opt_6; /* Show IPv6 sockets */ static int opt_c; /* Show connected sockets */ static int opt_j; /* Show specified jail */ static int opt_L; /* Don't show IPv4 or IPv6 loopback sockets */ static int opt_l; /* Show listening sockets */ static int opt_s; /* Show protocol state if applicable */ static int opt_u; /* Show Unix domain sockets */ static int opt_v; /* Verbose mode */ /* * Default protocols to use if no -P was defined. */ static const char *default_protos[] = {"sctp", "tcp", "udp", "divert" }; static size_t default_numprotos = nitems(default_protos); static int *protos; /* protocols to use */ static size_t numprotos; /* allocated size of protos[] */ static int *ports; #define INT_BIT (sizeof(int)*CHAR_BIT) #define SET_PORT(p) do { ports[p / INT_BIT] |= 1 << (p % INT_BIT); } while (0) #define CHK_PORT(p) (ports[p / INT_BIT] & (1 << (p % INT_BIT))) struct addr { struct sockaddr_storage address; struct addr *next; }; struct sock { void *socket; void *pcb; int shown; int vflag; int family; int proto; int state; const char *protoname; struct addr *laddr; struct addr *faddr; struct sock *next; }; #define HASHSIZE 1009 static struct sock *sockhash[HASHSIZE]; static struct xfile *xfiles; static int nxfiles; static int xprintf(const char *fmt, ...) { va_list ap; int len; va_start(ap, fmt); len = vprintf(fmt, ap); va_end(ap); if (len < 0) err(1, "printf()"); return (len); } static int get_proto_type(const char *proto) { struct protoent *pent; if (strlen(proto) == 0) return (0); pent = getprotobyname(proto); if (pent == NULL) { warn("getprotobyname"); return (-1); } return (pent->p_proto); } static void init_protos(int num) { int proto_count = 0; if (num > 0) { proto_count = num; } else { /* Find the maximum number of possible protocols. */ while (getprotoent() != NULL) proto_count++; endprotoent(); } if ((protos = malloc(sizeof(int) * proto_count)) == NULL) err(1, "malloc"); numprotos = proto_count; } static int parse_protos(char *protospec) { char *prot; int proto_type, proto_index; if (protospec == NULL) return (-1); init_protos(0); proto_index = 0; while ((prot = strsep(&protospec, ",")) != NULL) { if (strlen(prot) == 0) continue; proto_type = get_proto_type(prot); if (proto_type != -1) protos[proto_index++] = proto_type; } numprotos = proto_index; return (proto_index); } static void parse_ports(const char *portspec) { const char *p, *q; int port, end; if (ports == NULL) if ((ports = calloc(65536 / INT_BIT, sizeof(int))) == NULL) err(1, "calloc()"); p = portspec; while (*p != '\0') { if (!isdigit(*p)) errx(1, "syntax error in port range"); for (q = p; *q != '\0' && isdigit(*q); ++q) /* nothing */ ; for (port = 0; p < q; ++p) port = port * 10 + digittoint(*p); if (port < 0 || port > 65535) errx(1, "invalid port number"); SET_PORT(port); switch (*p) { case '-': ++p; break; case ',': ++p; /* fall through */ case '\0': default: continue; } for (q = p; *q != '\0' && isdigit(*q); ++q) /* nothing */ ; for (end = 0; p < q; ++p) end = end * 10 + digittoint(*p); if (end < port || end > 65535) errx(1, "invalid port number"); while (port++ < end) SET_PORT(port); if (*p == ',') ++p; } } static void sockaddr(struct sockaddr_storage *ss, int af, void *addr, int port) { struct sockaddr_in *sin4; struct sockaddr_in6 *sin6; bzero(ss, sizeof(*ss)); switch (af) { case AF_INET: sin4 = sstosin(ss); sin4->sin_len = sizeof(*sin4); sin4->sin_family = af; sin4->sin_port = port; sin4->sin_addr = *(struct in_addr *)addr; break; case AF_INET6: sin6 = sstosin6(ss); sin6->sin6_len = sizeof(*sin6); sin6->sin6_family = af; sin6->sin6_port = port; sin6->sin6_addr = *(struct in6_addr *)addr; #define s6_addr16 __u6_addr.__u6_addr16 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]); sin6->sin6_addr.s6_addr16[1] = 0; } break; default: abort(); } } static void free_socket(struct sock *sock) { struct addr *cur, *next; cur = sock->laddr; while (cur != NULL) { next = cur->next; free(cur); cur = next; } cur = sock->faddr; while (cur != NULL) { next = cur->next; free(cur); cur = next; } free(sock); } static void gather_sctp(void) { struct sock *sock; struct addr *laddr, *prev_laddr, *faddr, *prev_faddr; struct xsctp_inpcb *xinpcb; struct xsctp_tcb *xstcb; struct xsctp_raddr *xraddr; struct xsctp_laddr *xladdr; const char *varname; size_t len, offset; char *buf; int hash, vflag; int no_stcb, local_all_loopback, foreign_all_loopback; vflag = 0; if (opt_4) vflag |= INP_IPV4; if (opt_6) vflag |= INP_IPV6; varname = "net.inet.sctp.assoclist"; if (sysctlbyname(varname, 0, &len, 0, 0) < 0) { if (errno != ENOENT) err(1, "sysctlbyname()"); return; } if ((buf = (char *)malloc(len)) == NULL) { err(1, "malloc()"); return; } if (sysctlbyname(varname, buf, &len, 0, 0) < 0) { err(1, "sysctlbyname()"); free(buf); return; } xinpcb = (struct xsctp_inpcb *)(void *)buf; offset = sizeof(struct xsctp_inpcb); while ((offset < len) && (xinpcb->last == 0)) { if ((sock = calloc(1, sizeof *sock)) == NULL) err(1, "malloc()"); sock->socket = xinpcb->socket; sock->proto = IPPROTO_SCTP; sock->protoname = "sctp"; if (xinpcb->maxqlen == 0) sock->state = SCTP_CLOSED; else sock->state = SCTP_LISTEN; if (xinpcb->flags & SCTP_PCB_FLAGS_BOUND_V6) { sock->family = AF_INET6; sock->vflag = INP_IPV6; } else { sock->family = AF_INET; sock->vflag = INP_IPV4; } prev_laddr = NULL; local_all_loopback = 1; while (offset < len) { xladdr = (struct xsctp_laddr *)(void *)(buf + offset); offset += sizeof(struct xsctp_laddr); if (xladdr->last == 1) break; if ((laddr = calloc(1, sizeof(struct addr))) == NULL) err(1, "malloc()"); switch (xladdr->address.sa.sa_family) { case AF_INET: #define __IN_IS_ADDR_LOOPBACK(pina) \ ((ntohl((pina)->s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) if (!__IN_IS_ADDR_LOOPBACK(&xladdr->address.sin.sin_addr)) local_all_loopback = 0; #undef __IN_IS_ADDR_LOOPBACK sockaddr(&laddr->address, AF_INET, &xladdr->address.sin.sin_addr, htons(xinpcb->local_port)); break; case AF_INET6: if (!IN6_IS_ADDR_LOOPBACK(&xladdr->address.sin6.sin6_addr)) local_all_loopback = 0; sockaddr(&laddr->address, AF_INET6, &xladdr->address.sin6.sin6_addr, htons(xinpcb->local_port)); break; default: - errx(1, "adress family %d not supported", + errx(1, "address family %d not supported", xladdr->address.sa.sa_family); } laddr->next = NULL; if (prev_laddr == NULL) sock->laddr = laddr; else prev_laddr->next = laddr; prev_laddr = laddr; } if (sock->laddr == NULL) { if ((sock->laddr = calloc(1, sizeof(struct addr))) == NULL) err(1, "malloc()"); sock->laddr->address.ss_family = sock->family; if (sock->family == AF_INET) sock->laddr->address.ss_len = sizeof(struct sockaddr_in); else sock->laddr->address.ss_len = sizeof(struct sockaddr_in); local_all_loopback = 0; } if ((sock->faddr = calloc(1, sizeof(struct addr))) == NULL) err(1, "malloc()"); sock->faddr->address.ss_family = sock->family; if (sock->family == AF_INET) sock->faddr->address.ss_len = sizeof(struct sockaddr_in); else sock->faddr->address.ss_len = sizeof(struct sockaddr_in); no_stcb = 1; while (offset < len) { xstcb = (struct xsctp_tcb *)(void *)(buf + offset); offset += sizeof(struct xsctp_tcb); if (no_stcb) { if (opt_l && (!opt_L || !local_all_loopback) && ((xinpcb->flags & SCTP_PCB_FLAGS_UDPTYPE) || (xstcb->last == 1))) { hash = (int)((uintptr_t)sock->socket % HASHSIZE); sock->next = sockhash[hash]; sockhash[hash] = sock; } else { free_socket(sock); } } if (xstcb->last == 1) break; no_stcb = 0; if (opt_c) { if ((sock = calloc(1, sizeof *sock)) == NULL) err(1, "malloc()"); sock->socket = xinpcb->socket; sock->proto = IPPROTO_SCTP; sock->protoname = "sctp"; sock->state = (int)xstcb->state; if (xinpcb->flags & SCTP_PCB_FLAGS_BOUND_V6) { sock->family = AF_INET6; sock->vflag = INP_IPV6; } else { sock->family = AF_INET; sock->vflag = INP_IPV4; } } prev_laddr = NULL; local_all_loopback = 1; while (offset < len) { xladdr = (struct xsctp_laddr *)(void *)(buf + offset); offset += sizeof(struct xsctp_laddr); if (xladdr->last == 1) break; if (!opt_c) continue; if ((laddr = calloc(1, sizeof(struct addr))) == NULL) err(1, "malloc()"); switch (xladdr->address.sa.sa_family) { case AF_INET: #define __IN_IS_ADDR_LOOPBACK(pina) \ ((ntohl((pina)->s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) if (!__IN_IS_ADDR_LOOPBACK(&xladdr->address.sin.sin_addr)) local_all_loopback = 0; #undef __IN_IS_ADDR_LOOPBACK sockaddr(&laddr->address, AF_INET, &xladdr->address.sin.sin_addr, htons(xstcb->local_port)); break; case AF_INET6: if (!IN6_IS_ADDR_LOOPBACK(&xladdr->address.sin6.sin6_addr)) local_all_loopback = 0; sockaddr(&laddr->address, AF_INET6, &xladdr->address.sin6.sin6_addr, htons(xstcb->local_port)); break; default: - errx(1, "adress family %d not supported", + errx(1, "address family %d not supported", xladdr->address.sa.sa_family); } laddr->next = NULL; if (prev_laddr == NULL) sock->laddr = laddr; else prev_laddr->next = laddr; prev_laddr = laddr; } prev_faddr = NULL; foreign_all_loopback = 1; while (offset < len) { xraddr = (struct xsctp_raddr *)(void *)(buf + offset); offset += sizeof(struct xsctp_raddr); if (xraddr->last == 1) break; if (!opt_c) continue; if ((faddr = calloc(1, sizeof(struct addr))) == NULL) err(1, "malloc()"); switch (xraddr->address.sa.sa_family) { case AF_INET: #define __IN_IS_ADDR_LOOPBACK(pina) \ ((ntohl((pina)->s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) if (!__IN_IS_ADDR_LOOPBACK(&xraddr->address.sin.sin_addr)) foreign_all_loopback = 0; #undef __IN_IS_ADDR_LOOPBACK sockaddr(&faddr->address, AF_INET, &xraddr->address.sin.sin_addr, htons(xstcb->remote_port)); break; case AF_INET6: if (!IN6_IS_ADDR_LOOPBACK(&xraddr->address.sin6.sin6_addr)) foreign_all_loopback = 0; sockaddr(&faddr->address, AF_INET6, &xraddr->address.sin6.sin6_addr, htons(xstcb->remote_port)); break; default: - errx(1, "adress family %d not supported", + errx(1, "address family %d not supported", xraddr->address.sa.sa_family); } faddr->next = NULL; if (prev_faddr == NULL) sock->faddr = faddr; else prev_faddr->next = faddr; prev_faddr = faddr; } if (opt_c) { if (!opt_L || !(local_all_loopback || foreign_all_loopback)) { hash = (int)((uintptr_t)sock->socket % HASHSIZE); sock->next = sockhash[hash]; sockhash[hash] = sock; } else { free_socket(sock); } } } xinpcb = (struct xsctp_inpcb *)(void *)(buf + offset); offset += sizeof(struct xsctp_inpcb); } free(buf); } static void gather_inet(int proto) { struct xinpgen *xig, *exig; struct xinpcb *xip; struct xtcpcb *xtp; struct inpcb *inp; struct xsocket *so; struct sock *sock; struct addr *laddr, *faddr; const char *varname, *protoname; size_t len, bufsize; void *buf; int hash, retry, vflag; vflag = 0; if (opt_4) vflag |= INP_IPV4; if (opt_6) vflag |= INP_IPV6; switch (proto) { case IPPROTO_TCP: varname = "net.inet.tcp.pcblist"; protoname = "tcp"; break; case IPPROTO_UDP: varname = "net.inet.udp.pcblist"; protoname = "udp"; break; case IPPROTO_DIVERT: varname = "net.inet.divert.pcblist"; protoname = "div"; break; default: errx(1, "protocol %d not supported", proto); } buf = NULL; bufsize = 8192; retry = 5; do { for (;;) { if ((buf = realloc(buf, bufsize)) == NULL) err(1, "realloc()"); len = bufsize; if (sysctlbyname(varname, buf, &len, NULL, 0) == 0) break; if (errno == ENOENT) goto out; if (errno != ENOMEM || len != bufsize) err(1, "sysctlbyname()"); bufsize *= 2; } xig = (struct xinpgen *)buf; exig = (struct xinpgen *)(void *) ((char *)buf + len - sizeof *exig); if (xig->xig_len != sizeof *xig || exig->xig_len != sizeof *exig) errx(1, "struct xinpgen size mismatch"); } while (xig->xig_gen != exig->xig_gen && retry--); if (xig->xig_gen != exig->xig_gen && opt_v) warnx("warning: data may be inconsistent"); for (;;) { xig = (struct xinpgen *)(void *)((char *)xig + xig->xig_len); if (xig >= exig) break; xip = (struct xinpcb *)xig; xtp = (struct xtcpcb *)xig; switch (proto) { case IPPROTO_TCP: if (xtp->xt_len != sizeof(*xtp)) { warnx("struct xtcpcb size mismatch"); goto out; } inp = &xtp->xt_inp; so = &xtp->xt_socket; protoname = xtp->xt_tp.t_flags & TF_TOE ? "toe" : "tcp"; break; case IPPROTO_UDP: case IPPROTO_DIVERT: if (xip->xi_len != sizeof(*xip)) { warnx("struct xinpcb size mismatch"); goto out; } inp = &xip->xi_inp; so = &xip->xi_socket; break; default: errx(1, "protocol %d not supported", proto); } if ((inp->inp_vflag & vflag) == 0) continue; if (inp->inp_vflag & INP_IPV4) { if ((inp->inp_fport == 0 && !opt_l) || (inp->inp_fport != 0 && !opt_c)) continue; #define __IN_IS_ADDR_LOOPBACK(pina) \ ((ntohl((pina)->s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) if (opt_L && (__IN_IS_ADDR_LOOPBACK(&inp->inp_faddr) || __IN_IS_ADDR_LOOPBACK(&inp->inp_laddr))) continue; #undef __IN_IS_ADDR_LOOPBACK } else if (inp->inp_vflag & INP_IPV6) { if ((inp->inp_fport == 0 && !opt_l) || (inp->inp_fport != 0 && !opt_c)) continue; if (opt_L && (IN6_IS_ADDR_LOOPBACK(&inp->in6p_faddr) || IN6_IS_ADDR_LOOPBACK(&inp->in6p_laddr))) continue; } else { if (opt_v) warnx("invalid vflag 0x%x", inp->inp_vflag); continue; } if ((sock = calloc(1, sizeof(*sock))) == NULL) err(1, "malloc()"); if ((laddr = calloc(1, sizeof *laddr)) == NULL) err(1, "malloc()"); if ((faddr = calloc(1, sizeof *faddr)) == NULL) err(1, "malloc()"); sock->socket = so->xso_so; sock->proto = proto; if (inp->inp_vflag & INP_IPV4) { sock->family = AF_INET; sockaddr(&laddr->address, sock->family, &inp->inp_laddr, inp->inp_lport); sockaddr(&faddr->address, sock->family, &inp->inp_faddr, inp->inp_fport); } else if (inp->inp_vflag & INP_IPV6) { sock->family = AF_INET6; sockaddr(&laddr->address, sock->family, &inp->in6p_laddr, inp->inp_lport); sockaddr(&faddr->address, sock->family, &inp->in6p_faddr, inp->inp_fport); } laddr->next = NULL; faddr->next = NULL; sock->laddr = laddr; sock->faddr = faddr; sock->vflag = inp->inp_vflag; if (proto == IPPROTO_TCP) sock->state = xtp->xt_tp.t_state; sock->protoname = protoname; hash = (int)((uintptr_t)sock->socket % HASHSIZE); sock->next = sockhash[hash]; sockhash[hash] = sock; } out: free(buf); } static void gather_unix(int proto) { struct xunpgen *xug, *exug; struct xunpcb *xup; struct sock *sock; struct addr *laddr, *faddr; const char *varname, *protoname; size_t len, bufsize; void *buf; int hash, retry; switch (proto) { case SOCK_STREAM: varname = "net.local.stream.pcblist"; protoname = "stream"; break; case SOCK_DGRAM: varname = "net.local.dgram.pcblist"; protoname = "dgram"; break; case SOCK_SEQPACKET: varname = "net.local.seqpacket.pcblist"; protoname = "seqpac"; break; default: abort(); } buf = NULL; bufsize = 8192; retry = 5; do { for (;;) { if ((buf = realloc(buf, bufsize)) == NULL) err(1, "realloc()"); len = bufsize; if (sysctlbyname(varname, buf, &len, NULL, 0) == 0) break; if (errno != ENOMEM || len != bufsize) err(1, "sysctlbyname()"); bufsize *= 2; } xug = (struct xunpgen *)buf; exug = (struct xunpgen *)(void *) ((char *)buf + len - sizeof(*exug)); if (xug->xug_len != sizeof(*xug) || exug->xug_len != sizeof(*exug)) { warnx("struct xinpgen size mismatch"); goto out; } } while (xug->xug_gen != exug->xug_gen && retry--); if (xug->xug_gen != exug->xug_gen && opt_v) warnx("warning: data may be inconsistent"); for (;;) { xug = (struct xunpgen *)(void *)((char *)xug + xug->xug_len); if (xug >= exug) break; xup = (struct xunpcb *)xug; if (xup->xu_len != sizeof(*xup)) { warnx("struct xunpcb size mismatch"); goto out; } if ((xup->xu_unp.unp_conn == NULL && !opt_l) || (xup->xu_unp.unp_conn != NULL && !opt_c)) continue; if ((sock = calloc(1, sizeof(*sock))) == NULL) err(1, "malloc()"); if ((laddr = calloc(1, sizeof *laddr)) == NULL) err(1, "malloc()"); if ((faddr = calloc(1, sizeof *faddr)) == NULL) err(1, "malloc()"); sock->socket = xup->xu_socket.xso_so; sock->pcb = xup->xu_unpp; sock->proto = proto; sock->family = AF_UNIX; sock->protoname = protoname; if (xup->xu_unp.unp_addr != NULL) laddr->address = *(struct sockaddr_storage *)(void *)&xup->xu_addr; else if (xup->xu_unp.unp_conn != NULL) *(void **)&(faddr->address) = xup->xu_unp.unp_conn; laddr->next = NULL; faddr->next = NULL; sock->laddr = laddr; sock->faddr = faddr; hash = (int)((uintptr_t)sock->socket % HASHSIZE); sock->next = sockhash[hash]; sockhash[hash] = sock; } out: free(buf); } static void getfiles(void) { size_t len, olen; olen = len = sizeof(*xfiles); if ((xfiles = malloc(len)) == NULL) err(1, "malloc()"); while (sysctlbyname("kern.file", xfiles, &len, 0, 0) == -1) { if (errno != ENOMEM || len != olen) err(1, "sysctlbyname()"); olen = len *= 2; if ((xfiles = realloc(xfiles, len)) == NULL) err(1, "realloc()"); } if (len > 0 && xfiles->xf_size != sizeof(*xfiles)) errx(1, "struct xfile size mismatch"); nxfiles = len / sizeof(*xfiles); } static int printaddr(struct sockaddr_storage *ss) { struct sockaddr_un *sun; char addrstr[NI_MAXHOST] = { '\0', '\0' }; int error, off, port = 0; switch (ss->ss_family) { case AF_INET: if (inet_lnaof(sstosin(ss)->sin_addr) == INADDR_ANY) addrstr[0] = '*'; port = ntohs(sstosin(ss)->sin_port); break; case AF_INET6: if (IN6_IS_ADDR_UNSPECIFIED(&sstosin6(ss)->sin6_addr)) addrstr[0] = '*'; port = ntohs(sstosin6(ss)->sin6_port); break; case AF_UNIX: sun = sstosun(ss); off = (int)((char *)&sun->sun_path - (char *)sun); return (xprintf("%.*s", sun->sun_len - off, sun->sun_path)); } if (addrstr[0] == '\0') { error = getnameinfo(sstosa(ss), ss->ss_len, addrstr, sizeof(addrstr), NULL, 0, NI_NUMERICHOST); if (error) errx(1, "getnameinfo()"); } if (port == 0) return xprintf("%s:*", addrstr); else return xprintf("%s:%d", addrstr, port); } static const char * getprocname(pid_t pid) { static struct kinfo_proc proc; size_t len; int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = (int)pid; len = sizeof(proc); if (sysctl(mib, nitems(mib), &proc, &len, NULL, 0) == -1) { /* Do not warn if the process exits before we get its name. */ if (errno != ESRCH) warn("sysctl()"); return ("??"); } return (proc.ki_comm); } static int getprocjid(pid_t pid) { static struct kinfo_proc proc; size_t len; int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = (int)pid; len = sizeof(proc); if (sysctl(mib, nitems(mib), &proc, &len, NULL, 0) == -1) { /* Do not warn if the process exits before we get its jid. */ if (errno != ESRCH) warn("sysctl()"); return (-1); } return (proc.ki_jid); } static int check_ports(struct sock *s) { int port; struct addr *addr; if (ports == NULL) return (1); if ((s->family != AF_INET) && (s->family != AF_INET6)) return (1); for (addr = s->laddr; addr != NULL; addr = addr->next) { if (s->family == AF_INET) port = ntohs(sstosin(&addr->address)->sin_port); else port = ntohs(sstosin6(&addr->address)->sin6_port); if (CHK_PORT(port)) return (1); } for (addr = s->faddr; addr != NULL; addr = addr->next) { if (s->family == AF_INET) port = ntohs(sstosin(&addr->address)->sin_port); else port = ntohs(sstosin6(&addr->address)->sin6_port); if (CHK_PORT(port)) return (1); } return (0); } static const char * sctp_state(int state) { switch (state) { case SCTP_CLOSED: return "CLOSED"; break; case SCTP_BOUND: return "BOUND"; break; case SCTP_LISTEN: return "LISTEN"; break; case SCTP_COOKIE_WAIT: return "COOKIE_WAIT"; break; case SCTP_COOKIE_ECHOED: return "COOKIE_ECHOED"; break; case SCTP_ESTABLISHED: return "ESTABLISHED"; break; case SCTP_SHUTDOWN_SENT: return "SHUTDOWN_SENT"; break; case SCTP_SHUTDOWN_RECEIVED: return "SHUTDOWN_RECEIVED"; break; case SCTP_SHUTDOWN_ACK_SENT: return "SHUTDOWN_ACK_SENT"; break; case SCTP_SHUTDOWN_PENDING: return "SHUTDOWN_PENDING"; break; default: return "UNKNOWN"; break; } } static void displaysock(struct sock *s, int pos) { void *p; int hash, first; struct addr *laddr, *faddr; struct sock *s_tmp; while (pos < 29) pos += xprintf(" "); pos += xprintf("%s", s->protoname); if (s->vflag & INP_IPV4) pos += xprintf("4 "); if (s->vflag & INP_IPV6) pos += xprintf("6 "); laddr = s->laddr; faddr = s->faddr; first = 1; while (laddr != NULL || faddr != NULL) { while (pos < 36) pos += xprintf(" "); switch (s->family) { case AF_INET: case AF_INET6: if (laddr != NULL) { pos += printaddr(&laddr->address); if (s->family == AF_INET6 && pos >= 58) pos += xprintf(" "); } while (pos < 58) pos += xprintf(" "); if (faddr != NULL) pos += printaddr(&faddr->address); break; case AF_UNIX: if ((laddr == NULL) || (faddr == NULL)) errx(1, "laddr = %p or faddr = %p is NULL", (void *)laddr, (void *)faddr); /* server */ if (laddr->address.ss_len > 0) { pos += printaddr(&laddr->address); break; } /* client */ p = *(void **)&(faddr->address); if (p == NULL) { pos += xprintf("(not connected)"); break; } pos += xprintf("-> "); for (hash = 0; hash < HASHSIZE; ++hash) { for (s_tmp = sockhash[hash]; s_tmp != NULL; s_tmp = s_tmp->next) if (s_tmp->pcb == p) break; if (s_tmp != NULL) break; } if (s_tmp == NULL || s_tmp->laddr == NULL || s_tmp->laddr->address.ss_len == 0) pos += xprintf("??"); else pos += printaddr(&s_tmp->laddr->address); break; default: abort(); } if (first && opt_s && (s->proto == IPPROTO_SCTP || s->proto == IPPROTO_TCP)) { while (pos < 80) pos += xprintf(" "); switch (s->proto) { case IPPROTO_SCTP: pos += xprintf("%s", sctp_state(s->state)); break; case IPPROTO_TCP: if (s->state >= 0 && s->state < TCP_NSTATES) pos += xprintf("%s", tcpstates[s->state]); else pos += xprintf("?"); break; } } if (laddr != NULL) laddr = laddr->next; if (faddr != NULL) faddr = faddr->next; if ((laddr != NULL) || (faddr != NULL)) { xprintf("\n"); pos = 0; } first = 0; } xprintf("\n"); } static void display(void) { struct passwd *pwd; struct xfile *xf; struct sock *s; int hash, n, pos; printf("%-8s %-10s %-5s %-2s %-6s %-21s %-21s", "USER", "COMMAND", "PID", "FD", "PROTO", "LOCAL ADDRESS", "FOREIGN ADDRESS"); if (opt_s) printf(" %-12s", "STATE"); printf("\n"); setpassent(1); for (xf = xfiles, n = 0; n < nxfiles; ++n, ++xf) { if (xf->xf_data == NULL) continue; if (opt_j >= 0 && opt_j != getprocjid(xf->xf_pid)) continue; hash = (int)((uintptr_t)xf->xf_data % HASHSIZE); for (s = sockhash[hash]; s != NULL; s = s->next) { if ((void *)s->socket != xf->xf_data) continue; if (!check_ports(s)) continue; s->shown = 1; pos = 0; if ((pwd = getpwuid(xf->xf_uid)) == NULL) pos += xprintf("%lu ", (u_long)xf->xf_uid); else pos += xprintf("%s ", pwd->pw_name); while (pos < 9) pos += xprintf(" "); pos += xprintf("%.10s", getprocname(xf->xf_pid)); while (pos < 20) pos += xprintf(" "); pos += xprintf("%lu ", (u_long)xf->xf_pid); while (pos < 26) pos += xprintf(" "); pos += xprintf("%d ", xf->xf_fd); displaysock(s, pos); } } if (opt_j >= 0) return; for (hash = 0; hash < HASHSIZE; hash++) { for (s = sockhash[hash]; s != NULL; s = s->next) { if (s->shown) continue; if (!check_ports(s)) continue; pos = 0; pos += xprintf("%-8s %-10s %-5s %-2s ", "?", "?", "?", "?"); displaysock(s, pos); } } } static int set_default_protos(void) { struct protoent *prot; const char *pname; size_t pindex; init_protos(default_numprotos); for (pindex = 0; pindex < default_numprotos; pindex++) { pname = default_protos[pindex]; prot = getprotobyname(pname); if (prot == NULL) err(1, "getprotobyname: %s", pname); protos[pindex] = prot->p_proto; } numprotos = pindex; return (pindex); } static void usage(void) { fprintf(stderr, "usage: sockstat [-46cLlsu] [-j jid] [-p ports] [-P protocols]\n"); exit(1); } int main(int argc, char *argv[]) { int protos_defined = -1; int o, i; opt_j = -1; while ((o = getopt(argc, argv, "46cj:Llp:P:suv")) != -1) switch (o) { case '4': opt_4 = 1; break; case '6': opt_6 = 1; break; case 'c': opt_c = 1; break; case 'j': opt_j = atoi(optarg); break; case 'L': opt_L = 1; break; case 'l': opt_l = 1; break; case 'p': parse_ports(optarg); break; case 'P': protos_defined = parse_protos(optarg); break; case 's': opt_s = 1; break; case 'u': opt_u = 1; break; case 'v': ++opt_v; break; default: usage(); } argc -= optind; argv += optind; if (argc > 0) usage(); if ((!opt_4 && !opt_6) && protos_defined != -1) opt_4 = opt_6 = 1; if (!opt_4 && !opt_6 && !opt_u) opt_4 = opt_6 = opt_u = 1; if ((opt_4 || opt_6) && protos_defined == -1) protos_defined = set_default_protos(); if (!opt_c && !opt_l) opt_c = opt_l = 1; if (opt_4 || opt_6) { for (i = 0; i < protos_defined; i++) if (protos[i] == IPPROTO_SCTP) gather_sctp(); else gather_inet(protos[i]); } if (opt_u || (protos_defined == -1 && !opt_4 && !opt_6)) { gather_unix(SOCK_STREAM); gather_unix(SOCK_DGRAM); gather_unix(SOCK_SEQPACKET); } getfiles(); display(); exit(0); } Index: head/usr.bin/vgrind/regexp.c =================================================================== --- head/usr.bin/vgrind/regexp.c (revision 289676) +++ head/usr.bin/vgrind/regexp.c (revision 289677) @@ -1,596 +1,596 @@ /* * Copyright (c) 1980, 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. * 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. */ #include __FBSDID("$FreeBSD$"); #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif #ifndef lint static const char sccsid[] = "@(#)regexp.c 8.1 (Berkeley) 6/6/93"; #endif #include #include #include #include #include "extern.h" static void expconv(void); bool _escaped; /* true if we are currently x_escaped */ char *s_start; /* start of string */ bool l_onecase; /* true if upper and lower equivalent */ #define makelower(c) (isupper((c)) ? tolower((c)) : (c)) /* STRNCMP - like strncmp except that we convert the * first string to lower case before comparing * if l_onecase is set. */ int STRNCMP(register char *s1, register char *s2, register int len) { if (l_onecase) { do if (*s2 - makelower(*s1)) return (*s2 - makelower(*s1)); else { s2++; s1++; } while (--len); } else { do if (*s2 - *s1) return (*s2 - *s1); else { s2++; s1++; } while (--len); } return(0); } /* The following routine converts an irregular expression to * internal format. * * Either meta symbols (\a \d or \p) or character strings or * operations ( alternation or parenthesizing ) can be * specified. Each starts with a descriptor byte. The descriptor * byte has STR set for strings, META set for meta symbols * and OPER set for operations. * The descriptor byte can also have the OPT bit set if the object * defined is optional. Also ALT can be set to indicate an alternation. * * For metasymbols the byte following the descriptor byte identities * the meta symbol (containing an ascii 'a', 'd', 'p', '|', or '('). For * strings the byte after the descriptor is a character count for * the string: * * meta symbols := descriptor * symbol * * strings := descriptor * character count * the string * * operations := descriptor * symbol * character count */ /* * handy macros for accessing parts of match blocks */ #define MSYM(A) (*(A+1)) /* symbol in a meta symbol block */ #define MNEXT(A) (A+2) /* character following a metasymbol block */ #define OSYM(A) (*(A+1)) /* symbol in an operation block */ #define OCNT(A) (*(A+2)) /* character count */ #define ONEXT(A) (A+3) /* next character after the operation */ #define OPTR(A) (A+*(A+2)) /* place pointed to by the operator */ #define SCNT(A) (*(A+1)) /* byte count of a string */ #define SSTR(A) (A+2) /* address of the string */ #define SNEXT(A) (A+2+*(A+1)) /* character following the string */ /* * bit flags in the descriptor */ #define OPT 1 #define STR 2 #define META 4 #define ALT 8 #define OPER 16 static char *ccre; /* pointer to current position in converted exp*/ static char *ure; /* pointer current position in unconverted exp */ /* re: unconverted irregular expression */ char * convexp(char *re) { register char *cre; /* pointer to converted regular expression */ /* allocate room for the converted expression */ if (re == NULL) return (NULL); if (*re == '\0') return (NULL); cre = malloc(4 * strlen(re) + 3); ccre = cre; ure = re; /* start the conversion with a \a */ *cre = META | OPT; MSYM(cre) = 'a'; ccre = MNEXT(cre); /* start the conversion (its recursive) */ expconv (); *ccre = 0; return (cre); } static void expconv() { register char *cs; /* pointer to current symbol in converted exp */ register char c; /* character being processed */ register char *acs; /* pinter to last alternate */ register int temp; /* let the conversion begin */ acs = NULL; cs = NULL; while (*ure) { switch (c = *ure++) { case '\\': switch (c = *ure++) { /* escaped characters are just characters */ default: if (cs == NULL || (*cs & STR) == 0) { cs = ccre; *cs = STR; SCNT(cs) = 1; ccre += 2; } else SCNT(cs)++; *ccre++ = c; break; /* normal(?) metacharacters */ case 'a': case 'd': case 'e': case 'p': if (acs != NULL && acs != cs) { do { temp = OCNT(acs); OCNT(acs) = ccre - acs; acs -= temp; } while (temp != 0); acs = NULL; } cs = ccre; *cs = META; MSYM(cs) = c; ccre = MNEXT(cs); break; } break; /* just put the symbol in */ case '^': case '$': if (acs != NULL && acs != cs) { do { temp = OCNT(acs); OCNT(acs) = ccre - acs; acs -= temp; } while (temp != 0); acs = NULL; } cs = ccre; *cs = META; MSYM(cs) = c; ccre = MNEXT(cs); break; /* mark the last match sequence as optional */ case '?': if (cs) *cs = *cs | OPT; break; /* recurse and define a subexpression */ case '(': if (acs != NULL && acs != cs) { do { temp = OCNT(acs); OCNT(acs) = ccre - acs; acs -= temp; } while (temp != 0); acs = NULL; } cs = ccre; *cs = OPER; OSYM(cs) = '('; ccre = ONEXT(cs); expconv(); OCNT(cs) = ccre - cs; /* offset to next symbol */ break; /* return from a recursion */ case ')': if (acs != NULL) { do { temp = OCNT(acs); OCNT(acs) = ccre - acs; acs -= temp; } while (temp != 0); acs = NULL; } cs = ccre; *cs = META; MSYM(cs) = c; ccre = MNEXT(cs); return; /* mark the last match sequence as having an alternate */ /* the third byte will contain an offset to jump over the */ /* alternate match in case the first did not fail */ case '|': if (acs != NULL && acs != cs) OCNT(ccre) = ccre - acs; /* make a back pointer */ else OCNT(ccre) = 0; *cs |= ALT; cs = ccre; *cs = OPER; OSYM(cs) = '|'; ccre = ONEXT(cs); acs = cs; /* remember that the pointer is to be filles */ break; /* if its not a metasymbol just build a scharacter string */ default: if (cs == NULL || (*cs & STR) == 0) { cs = ccre; *cs = STR; SCNT(cs) = 1; ccre = SSTR(cs); } else SCNT(cs)++; *ccre++ = c; break; } } if (acs != NULL) { do { temp = OCNT(acs); OCNT(acs) = ccre - acs; acs -= temp; } while (temp != 0); acs = NULL; } return; } /* end of convertre */ /* - * The following routine recognises an irregular expresion + * The following routine recognises an irregular expression * with the following special characters: * * \? - means last match was optional * \a - matches any number of characters * \d - matches any number of spaces and tabs * \p - matches any number of alphanumeric * characters. The * characters matched will be copied into * the area pointed to by 'name'. * \| - alternation * \( \) - grouping used mostly for alternation and * optionality * * The irregular expression must be translated to internal form * prior to calling this routine * * The value returned is the pointer to the first non \a * character matched. */ /* * s: string to check for a match in * re: a converted irregular expression * mstring: where to put whatever matches a \p */ char * expmatch (register char *s, register char *re, register char *mstring) { register char *cs; /* the current symbol */ register char *ptr,*s1; /* temporary pointer */ bool matched; /* a temporary bool */ /* initial conditions */ if (re == NULL) return (NULL); cs = re; matched = false; /* loop till expression string is exhausted (or at least pretty tired) */ while (*cs) { switch (*cs & (OPER | STR | META)) { /* try to match a string */ case STR: matched = !STRNCMP (s, SSTR(cs), SCNT(cs)); if (matched) { /* hoorah it matches */ s += SCNT(cs); cs = SNEXT(cs); } else if (*cs & ALT) { /* alternation, skip to next expression */ cs = SNEXT(cs); } else if (*cs & OPT) { /* the match is optional */ cs = SNEXT(cs); matched = 1; /* indicate a successful match */ } else { /* no match, error return */ return (NULL); } break; /* an operator, do something fancy */ case OPER: switch (OSYM(cs)) { /* this is an alternation */ case '|': if (matched) /* last thing in the alternation was a match, skip ahead */ cs = OPTR(cs); else /* no match, keep trying */ cs = ONEXT(cs); break; /* this is a grouping, recurse */ case '(': ptr = expmatch(s, ONEXT(cs), mstring); if (ptr != NULL) { /* the subexpression matched */ matched = 1; s = ptr; } else if (*cs & ALT) { /* alternation, skip to next expression */ matched = 0; } else if (*cs & OPT) { /* the match is optional */ matched = 1; /* indicate a successful match */ } else { /* no match, error return */ return (NULL); } cs = OPTR(cs); break; } break; /* try to match a metasymbol */ case META: switch (MSYM(cs)) { /* try to match anything and remember what was matched */ case 'p': /* * This is really the same as trying the match the * remaining parts of the expression to any subset * of the string. */ s1 = s; do { ptr = expmatch(s1, MNEXT(cs), mstring); if (ptr != NULL && s1 != s) { /* we have a match, remember the match */ strncpy (mstring, s, s1 - s); mstring[s1 - s] = '\0'; return (ptr); } else if (ptr != NULL && (*cs & OPT)) { /* it was aoptional so no match is ok */ return (ptr); } else if (ptr != NULL) { /* not optional and we still matched */ return (NULL); } if (!(isalnum(*s1) || *s1 == '_' || /* C++ destructor */ *s1 == '~' || /* C++ scope operator */ (strlen(s1) > 1 && *s1 == ':' && s1[1] == ':' && (s1++, true)))) return (NULL); if (*s1 == '\\') _escaped = _escaped ? false : true; else _escaped = false; } while (*s1++); return (NULL); /* try to match anything */ case 'a': /* * This is really the same as trying the match the * remaining parts of the expression to any subset * of the string. */ s1 = s; do { ptr = expmatch(s1, MNEXT(cs), mstring); if (ptr != NULL && s1 != s) { /* we have a match */ return (ptr); } else if (ptr != NULL && (*cs & OPT)) { /* it was aoptional so no match is ok */ return (ptr); } else if (ptr != NULL) { /* not optional and we still matched */ return (NULL); } if (*s1 == '\\') _escaped = _escaped ? false : true; else _escaped = false; } while (*s1++); return (NULL); /* fail if we are currently _escaped */ case 'e': if (_escaped) return(NULL); cs = MNEXT(cs); break; /* match any number of tabs and spaces */ case 'd': ptr = s; while (*s == ' ' || *s == '\t') s++; if (s != ptr || s == s_start) { /* match, be happy */ matched = 1; cs = MNEXT(cs); } else if (*s == '\n' || *s == '\0') { /* match, be happy */ matched = 1; cs = MNEXT(cs); } else if (*cs & ALT) { /* try the next part */ matched = 0; cs = MNEXT(cs); } else if (*cs & OPT) { /* doesn't matter */ matched = 1; cs = MNEXT(cs); } else /* no match, error return */ return (NULL); break; /* check for end of line */ case '$': if (*s == '\0' || *s == '\n') { /* match, be happy */ s++; matched = 1; cs = MNEXT(cs); } else if (*cs & ALT) { /* try the next part */ matched = 0; cs = MNEXT(cs); } else if (*cs & OPT) { /* doesn't matter */ matched = 1; cs = MNEXT(cs); } else /* no match, error return */ return (NULL); break; /* check for start of line */ case '^': if (s == s_start) { /* match, be happy */ matched = 1; cs = MNEXT(cs); } else if (*cs & ALT) { /* try the next part */ matched = 0; cs = MNEXT(cs); } else if (*cs & OPT) { /* doesn't matter */ matched = 1; cs = MNEXT(cs); } else /* no match, error return */ return (NULL); break; /* end of a subexpression, return success */ case ')': return (s); } break; } } return (s); } Index: head/usr.sbin/bhyve/pci_emul.c =================================================================== --- head/usr.sbin/bhyve/pci_emul.c (revision 289676) +++ head/usr.sbin/bhyve/pci_emul.c (revision 289677) @@ -1,2108 +1,2108 @@ /*- * Copyright (c) 2011 NetApp, Inc. * 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 NETAPP, INC ``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 NETAPP, INC 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. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "acpi.h" #include "bhyverun.h" #include "inout.h" #include "ioapic.h" #include "mem.h" #include "pci_emul.h" #include "pci_irq.h" #include "pci_lpc.h" #define CONF1_ADDR_PORT 0x0cf8 #define CONF1_DATA_PORT 0x0cfc #define CONF1_ENABLE 0x80000000ul #define MAXBUSES (PCI_BUSMAX + 1) #define MAXSLOTS (PCI_SLOTMAX + 1) #define MAXFUNCS (PCI_FUNCMAX + 1) struct funcinfo { char *fi_name; char *fi_param; struct pci_devinst *fi_devi; }; struct intxinfo { int ii_count; int ii_pirq_pin; int ii_ioapic_irq; }; struct slotinfo { struct intxinfo si_intpins[4]; struct funcinfo si_funcs[MAXFUNCS]; }; struct businfo { uint16_t iobase, iolimit; /* I/O window */ uint32_t membase32, memlimit32; /* mmio window below 4GB */ uint64_t membase64, memlimit64; /* mmio window above 4GB */ struct slotinfo slotinfo[MAXSLOTS]; }; static struct businfo *pci_businfo[MAXBUSES]; SET_DECLARE(pci_devemu_set, struct pci_devemu); static uint64_t pci_emul_iobase; static uint64_t pci_emul_membase32; static uint64_t pci_emul_membase64; #define PCI_EMUL_IOBASE 0x2000 #define PCI_EMUL_IOLIMIT 0x10000 #define PCI_EMUL_ECFG_BASE 0xE0000000 /* 3.5GB */ #define PCI_EMUL_ECFG_SIZE (MAXBUSES * 1024 * 1024) /* 1MB per bus */ SYSRES_MEM(PCI_EMUL_ECFG_BASE, PCI_EMUL_ECFG_SIZE); #define PCI_EMUL_MEMLIMIT32 PCI_EMUL_ECFG_BASE #define PCI_EMUL_MEMBASE64 0xD000000000UL #define PCI_EMUL_MEMLIMIT64 0xFD00000000UL static struct pci_devemu *pci_emul_finddev(char *name); static void pci_lintr_route(struct pci_devinst *pi); static void pci_lintr_update(struct pci_devinst *pi); static void pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func, int coff, int bytes, uint32_t *val); static __inline void CFGWRITE(struct pci_devinst *pi, int coff, uint32_t val, int bytes) { if (bytes == 1) pci_set_cfgdata8(pi, coff, val); else if (bytes == 2) pci_set_cfgdata16(pi, coff, val); else pci_set_cfgdata32(pi, coff, val); } static __inline uint32_t CFGREAD(struct pci_devinst *pi, int coff, int bytes) { if (bytes == 1) return (pci_get_cfgdata8(pi, coff)); else if (bytes == 2) return (pci_get_cfgdata16(pi, coff)); else return (pci_get_cfgdata32(pi, coff)); } /* * I/O access */ /* * Slot options are in the form: * * ::,[,] * [:],[,] * * slot is 0..31 * func is 0..7 * emul is a string describing the type of PCI device e.g. virtio-net * config is an optional string, depending on the device, that can be * used for configuration. * Examples are: * 1,virtio-net,tap0 * 3:0,dummy */ static void pci_parse_slot_usage(char *aopt) { fprintf(stderr, "Invalid PCI slot info field \"%s\"\n", aopt); } int pci_parse_slot(char *opt) { struct businfo *bi; struct slotinfo *si; char *emul, *config, *str, *cp; int error, bnum, snum, fnum; error = -1; str = strdup(opt); emul = config = NULL; if ((cp = strchr(str, ',')) != NULL) { *cp = '\0'; emul = cp + 1; if ((cp = strchr(emul, ',')) != NULL) { *cp = '\0'; config = cp + 1; } } else { pci_parse_slot_usage(opt); goto done; } /* :: */ if (sscanf(str, "%d:%d:%d", &bnum, &snum, &fnum) != 3) { bnum = 0; /* : */ if (sscanf(str, "%d:%d", &snum, &fnum) != 2) { fnum = 0; /* */ if (sscanf(str, "%d", &snum) != 1) { snum = -1; } } } if (bnum < 0 || bnum >= MAXBUSES || snum < 0 || snum >= MAXSLOTS || fnum < 0 || fnum >= MAXFUNCS) { pci_parse_slot_usage(opt); goto done; } if (pci_businfo[bnum] == NULL) pci_businfo[bnum] = calloc(1, sizeof(struct businfo)); bi = pci_businfo[bnum]; si = &bi->slotinfo[snum]; if (si->si_funcs[fnum].fi_name != NULL) { fprintf(stderr, "pci slot %d:%d already occupied!\n", snum, fnum); goto done; } if (pci_emul_finddev(emul) == NULL) { fprintf(stderr, "pci slot %d:%d: unknown device \"%s\"\n", snum, fnum, emul); goto done; } error = 0; si->si_funcs[fnum].fi_name = emul; si->si_funcs[fnum].fi_param = config; done: if (error) free(str); return (error); } static int pci_valid_pba_offset(struct pci_devinst *pi, uint64_t offset) { if (offset < pi->pi_msix.pba_offset) return (0); if (offset >= pi->pi_msix.pba_offset + pi->pi_msix.pba_size) { return (0); } return (1); } int pci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size, uint64_t value) { int msix_entry_offset; int tab_index; char *dest; /* support only 4 or 8 byte writes */ if (size != 4 && size != 8) return (-1); /* * Return if table index is beyond what device supports */ tab_index = offset / MSIX_TABLE_ENTRY_SIZE; if (tab_index >= pi->pi_msix.table_count) return (-1); msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; /* support only aligned writes */ if ((msix_entry_offset % size) != 0) return (-1); dest = (char *)(pi->pi_msix.table + tab_index); dest += msix_entry_offset; if (size == 4) *((uint32_t *)dest) = value; else *((uint64_t *)dest) = value; return (0); } uint64_t pci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size) { char *dest; int msix_entry_offset; int tab_index; uint64_t retval = ~0; /* * The PCI standard only allows 4 and 8 byte accesses to the MSI-X - * table but we also allow 1 byte access to accomodate reads from + * table but we also allow 1 byte access to accommodate reads from * ddb. */ if (size != 1 && size != 4 && size != 8) return (retval); msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; /* support only aligned reads */ if ((msix_entry_offset % size) != 0) { return (retval); } tab_index = offset / MSIX_TABLE_ENTRY_SIZE; if (tab_index < pi->pi_msix.table_count) { /* valid MSI-X Table access */ dest = (char *)(pi->pi_msix.table + tab_index); dest += msix_entry_offset; if (size == 1) retval = *((uint8_t *)dest); else if (size == 4) retval = *((uint32_t *)dest); else retval = *((uint64_t *)dest); } else if (pci_valid_pba_offset(pi, offset)) { /* return 0 for PBA access */ retval = 0; } return (retval); } int pci_msix_table_bar(struct pci_devinst *pi) { if (pi->pi_msix.table != NULL) return (pi->pi_msix.table_bar); else return (-1); } int pci_msix_pba_bar(struct pci_devinst *pi) { if (pi->pi_msix.table != NULL) return (pi->pi_msix.pba_bar); else return (-1); } static int pci_emul_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, uint32_t *eax, void *arg) { struct pci_devinst *pdi = arg; struct pci_devemu *pe = pdi->pi_d; uint64_t offset; int i; for (i = 0; i <= PCI_BARMAX; i++) { if (pdi->pi_bar[i].type == PCIBAR_IO && port >= pdi->pi_bar[i].addr && port + bytes <= pdi->pi_bar[i].addr + pdi->pi_bar[i].size) { offset = port - pdi->pi_bar[i].addr; if (in) *eax = (*pe->pe_barread)(ctx, vcpu, pdi, i, offset, bytes); else (*pe->pe_barwrite)(ctx, vcpu, pdi, i, offset, bytes, *eax); return (0); } } return (-1); } static int pci_emul_mem_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, int size, uint64_t *val, void *arg1, long arg2) { struct pci_devinst *pdi = arg1; struct pci_devemu *pe = pdi->pi_d; uint64_t offset; int bidx = (int) arg2; assert(bidx <= PCI_BARMAX); assert(pdi->pi_bar[bidx].type == PCIBAR_MEM32 || pdi->pi_bar[bidx].type == PCIBAR_MEM64); assert(addr >= pdi->pi_bar[bidx].addr && addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size); offset = addr - pdi->pi_bar[bidx].addr; if (dir == MEM_F_WRITE) { if (size == 8) { (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, 4, *val & 0xffffffff); (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset + 4, 4, *val >> 32); } else { (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, size, *val); } } else { if (size == 8) { *val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, offset, 4); *val |= (*pe->pe_barread)(ctx, vcpu, pdi, bidx, offset + 4, 4) << 32; } else { *val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, offset, size); } } return (0); } static int pci_emul_alloc_resource(uint64_t *baseptr, uint64_t limit, uint64_t size, uint64_t *addr) { uint64_t base; assert((size & (size - 1)) == 0); /* must be a power of 2 */ base = roundup2(*baseptr, size); if (base + size <= limit) { *addr = base; *baseptr = base + size; return (0); } else return (-1); } int pci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type, uint64_t size) { return (pci_emul_alloc_pbar(pdi, idx, 0, type, size)); } /* * Register (or unregister) the MMIO or I/O region associated with the BAR * register 'idx' of an emulated pci device. */ static void modify_bar_registration(struct pci_devinst *pi, int idx, int registration) { int error; struct inout_port iop; struct mem_range mr; switch (pi->pi_bar[idx].type) { case PCIBAR_IO: bzero(&iop, sizeof(struct inout_port)); iop.name = pi->pi_name; iop.port = pi->pi_bar[idx].addr; iop.size = pi->pi_bar[idx].size; if (registration) { iop.flags = IOPORT_F_INOUT; iop.handler = pci_emul_io_handler; iop.arg = pi; error = register_inout(&iop); } else error = unregister_inout(&iop); break; case PCIBAR_MEM32: case PCIBAR_MEM64: bzero(&mr, sizeof(struct mem_range)); mr.name = pi->pi_name; mr.base = pi->pi_bar[idx].addr; mr.size = pi->pi_bar[idx].size; if (registration) { mr.flags = MEM_F_RW; mr.handler = pci_emul_mem_handler; mr.arg1 = pi; mr.arg2 = idx; error = register_mem(&mr); } else error = unregister_mem(&mr); break; default: error = EINVAL; break; } assert(error == 0); } static void unregister_bar(struct pci_devinst *pi, int idx) { modify_bar_registration(pi, idx, 0); } static void register_bar(struct pci_devinst *pi, int idx) { modify_bar_registration(pi, idx, 1); } /* Are we decoding i/o port accesses for the emulated pci device? */ static int porten(struct pci_devinst *pi) { uint16_t cmd; cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); return (cmd & PCIM_CMD_PORTEN); } /* Are we decoding memory accesses for the emulated pci device? */ static int memen(struct pci_devinst *pi) { uint16_t cmd; cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); return (cmd & PCIM_CMD_MEMEN); } /* * Update the MMIO or I/O address that is decoded by the BAR register. * * If the pci device has enabled the address space decoding then intercept * the address range decoded by the BAR register. */ static void update_bar_address(struct pci_devinst *pi, uint64_t addr, int idx, int type) { int decode; if (pi->pi_bar[idx].type == PCIBAR_IO) decode = porten(pi); else decode = memen(pi); if (decode) unregister_bar(pi, idx); switch (type) { case PCIBAR_IO: case PCIBAR_MEM32: pi->pi_bar[idx].addr = addr; break; case PCIBAR_MEM64: pi->pi_bar[idx].addr &= ~0xffffffffUL; pi->pi_bar[idx].addr |= addr; break; case PCIBAR_MEMHI64: pi->pi_bar[idx].addr &= 0xffffffff; pi->pi_bar[idx].addr |= addr; break; default: assert(0); } if (decode) register_bar(pi, idx); } int pci_emul_alloc_pbar(struct pci_devinst *pdi, int idx, uint64_t hostbase, enum pcibar_type type, uint64_t size) { int error; uint64_t *baseptr, limit, addr, mask, lobits, bar; assert(idx >= 0 && idx <= PCI_BARMAX); if ((size & (size - 1)) != 0) size = 1UL << flsl(size); /* round up to a power of 2 */ /* Enforce minimum BAR sizes required by the PCI standard */ if (type == PCIBAR_IO) { if (size < 4) size = 4; } else { if (size < 16) size = 16; } switch (type) { case PCIBAR_NONE: baseptr = NULL; addr = mask = lobits = 0; break; case PCIBAR_IO: baseptr = &pci_emul_iobase; limit = PCI_EMUL_IOLIMIT; mask = PCIM_BAR_IO_BASE; lobits = PCIM_BAR_IO_SPACE; break; case PCIBAR_MEM64: /* * XXX * Some drivers do not work well if the 64-bit BAR is allocated * above 4GB. Allow for this by allocating small requests under * 4GB unless then allocation size is larger than some arbitrary * number (32MB currently). */ if (size > 32 * 1024 * 1024) { /* * XXX special case for device requiring peer-peer DMA */ if (size == 0x100000000UL) baseptr = &hostbase; else baseptr = &pci_emul_membase64; limit = PCI_EMUL_MEMLIMIT64; mask = PCIM_BAR_MEM_BASE; lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | PCIM_BAR_MEM_PREFETCH; break; } else { baseptr = &pci_emul_membase32; limit = PCI_EMUL_MEMLIMIT32; mask = PCIM_BAR_MEM_BASE; lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64; } break; case PCIBAR_MEM32: baseptr = &pci_emul_membase32; limit = PCI_EMUL_MEMLIMIT32; mask = PCIM_BAR_MEM_BASE; lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; break; default: printf("pci_emul_alloc_base: invalid bar type %d\n", type); assert(0); } if (baseptr != NULL) { error = pci_emul_alloc_resource(baseptr, limit, size, &addr); if (error != 0) return (error); } pdi->pi_bar[idx].type = type; pdi->pi_bar[idx].addr = addr; pdi->pi_bar[idx].size = size; /* Initialize the BAR register in config space */ bar = (addr & mask) | lobits; pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar); if (type == PCIBAR_MEM64) { assert(idx + 1 <= PCI_BARMAX); pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64; pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32); } register_bar(pdi, idx); return (0); } #define CAP_START_OFFSET 0x40 static int pci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen) { int i, capoff, reallen; uint16_t sts; assert(caplen > 0); reallen = roundup2(caplen, 4); /* dword aligned */ sts = pci_get_cfgdata16(pi, PCIR_STATUS); if ((sts & PCIM_STATUS_CAPPRESENT) == 0) capoff = CAP_START_OFFSET; else capoff = pi->pi_capend + 1; /* Check if we have enough space */ if (capoff + reallen > PCI_REGMAX + 1) return (-1); /* Set the previous capability pointer */ if ((sts & PCIM_STATUS_CAPPRESENT) == 0) { pci_set_cfgdata8(pi, PCIR_CAP_PTR, capoff); pci_set_cfgdata16(pi, PCIR_STATUS, sts|PCIM_STATUS_CAPPRESENT); } else pci_set_cfgdata8(pi, pi->pi_prevcap + 1, capoff); /* Copy the capability */ for (i = 0; i < caplen; i++) pci_set_cfgdata8(pi, capoff + i, capdata[i]); /* Set the next capability pointer */ pci_set_cfgdata8(pi, capoff + 1, 0); pi->pi_prevcap = capoff; pi->pi_capend = capoff + reallen - 1; return (0); } static struct pci_devemu * pci_emul_finddev(char *name) { struct pci_devemu **pdpp, *pdp; SET_FOREACH(pdpp, pci_devemu_set) { pdp = *pdpp; if (!strcmp(pdp->pe_emu, name)) { return (pdp); } } return (NULL); } static int pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot, int func, struct funcinfo *fi) { struct pci_devinst *pdi; int err; pdi = calloc(1, sizeof(struct pci_devinst)); pdi->pi_vmctx = ctx; pdi->pi_bus = bus; pdi->pi_slot = slot; pdi->pi_func = func; pthread_mutex_init(&pdi->pi_lintr.lock, NULL); pdi->pi_lintr.pin = 0; pdi->pi_lintr.state = IDLE; pdi->pi_lintr.pirq_pin = 0; pdi->pi_lintr.ioapic_irq = 0; pdi->pi_d = pde; snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot); /* Disable legacy interrupts */ pci_set_cfgdata8(pdi, PCIR_INTLINE, 255); pci_set_cfgdata8(pdi, PCIR_INTPIN, 0); pci_set_cfgdata8(pdi, PCIR_COMMAND, PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); err = (*pde->pe_init)(ctx, pdi, fi->fi_param); if (err == 0) fi->fi_devi = pdi; else free(pdi); return (err); } void pci_populate_msicap(struct msicap *msicap, int msgnum, int nextptr) { int mmc; CTASSERT(sizeof(struct msicap) == 14); /* Number of msi messages must be a power of 2 between 1 and 32 */ assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32); mmc = ffs(msgnum) - 1; bzero(msicap, sizeof(struct msicap)); msicap->capid = PCIY_MSI; msicap->nextptr = nextptr; msicap->msgctrl = PCIM_MSICTRL_64BIT | (mmc << 1); } int pci_emul_add_msicap(struct pci_devinst *pi, int msgnum) { struct msicap msicap; pci_populate_msicap(&msicap, msgnum, 0); return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap))); } static void pci_populate_msixcap(struct msixcap *msixcap, int msgnum, int barnum, uint32_t msix_tab_size) { CTASSERT(sizeof(struct msixcap) == 12); assert(msix_tab_size % 4096 == 0); bzero(msixcap, sizeof(struct msixcap)); msixcap->capid = PCIY_MSIX; /* * Message Control Register, all fields set to * zero except for the Table Size. * Note: Table size N is encoded as N-1 */ msixcap->msgctrl = msgnum - 1; /* * MSI-X BAR setup: * - MSI-X table start at offset 0 * - PBA table starts at a 4K aligned offset after the MSI-X table */ msixcap->table_info = barnum & PCIM_MSIX_BIR_MASK; msixcap->pba_info = msix_tab_size | (barnum & PCIM_MSIX_BIR_MASK); } static void pci_msix_table_init(struct pci_devinst *pi, int table_entries) { int i, table_size; assert(table_entries > 0); assert(table_entries <= MAX_MSIX_TABLE_ENTRIES); table_size = table_entries * MSIX_TABLE_ENTRY_SIZE; pi->pi_msix.table = calloc(1, table_size); /* set mask bit of vector control register */ for (i = 0; i < table_entries; i++) pi->pi_msix.table[i].vector_control |= PCIM_MSIX_VCTRL_MASK; } int pci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum) { uint32_t tab_size; struct msixcap msixcap; assert(msgnum >= 1 && msgnum <= MAX_MSIX_TABLE_ENTRIES); assert(barnum >= 0 && barnum <= PCIR_MAX_BAR_0); tab_size = msgnum * MSIX_TABLE_ENTRY_SIZE; /* Align table size to nearest 4K */ tab_size = roundup2(tab_size, 4096); pi->pi_msix.table_bar = barnum; pi->pi_msix.pba_bar = barnum; pi->pi_msix.table_offset = 0; pi->pi_msix.table_count = msgnum; pi->pi_msix.pba_offset = tab_size; pi->pi_msix.pba_size = PBA_SIZE(msgnum); pci_msix_table_init(pi, msgnum); pci_populate_msixcap(&msixcap, msgnum, barnum, tab_size); /* allocate memory for MSI-X Table and PBA */ pci_emul_alloc_bar(pi, barnum, PCIBAR_MEM32, tab_size + pi->pi_msix.pba_size); return (pci_emul_add_capability(pi, (u_char *)&msixcap, sizeof(msixcap))); } void msixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, int bytes, uint32_t val) { uint16_t msgctrl, rwmask; int off, table_bar; off = offset - capoff; table_bar = pi->pi_msix.table_bar; /* Message Control Register */ if (off == 2 && bytes == 2) { rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK; msgctrl = pci_get_cfgdata16(pi, offset); msgctrl &= ~rwmask; msgctrl |= val & rwmask; val = msgctrl; pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE; pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK; pci_lintr_update(pi); } CFGWRITE(pi, offset, val, bytes); } void msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, int bytes, uint32_t val) { uint16_t msgctrl, rwmask, msgdata, mme; uint32_t addrlo; /* * If guest is writing to the message control register make sure * we do not overwrite read-only fields. */ if ((offset - capoff) == 2 && bytes == 2) { rwmask = PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE; msgctrl = pci_get_cfgdata16(pi, offset); msgctrl &= ~rwmask; msgctrl |= val & rwmask; val = msgctrl; addrlo = pci_get_cfgdata32(pi, capoff + 4); if (msgctrl & PCIM_MSICTRL_64BIT) msgdata = pci_get_cfgdata16(pi, capoff + 12); else msgdata = pci_get_cfgdata16(pi, capoff + 8); mme = msgctrl & PCIM_MSICTRL_MME_MASK; pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0; if (pi->pi_msi.enabled) { pi->pi_msi.addr = addrlo; pi->pi_msi.msg_data = msgdata; pi->pi_msi.maxmsgnum = 1 << (mme >> 4); } else { pi->pi_msi.maxmsgnum = 0; } pci_lintr_update(pi); } CFGWRITE(pi, offset, val, bytes); } void pciecap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, int bytes, uint32_t val) { /* XXX don't write to the readonly parts */ CFGWRITE(pi, offset, val, bytes); } #define PCIECAP_VERSION 0x2 int pci_emul_add_pciecap(struct pci_devinst *pi, int type) { int err; struct pciecap pciecap; CTASSERT(sizeof(struct pciecap) == 60); if (type != PCIEM_TYPE_ROOT_PORT) return (-1); bzero(&pciecap, sizeof(pciecap)); pciecap.capid = PCIY_EXPRESS; pciecap.pcie_capabilities = PCIECAP_VERSION | PCIEM_TYPE_ROOT_PORT; pciecap.link_capabilities = 0x411; /* gen1, x1 */ pciecap.link_status = 0x11; /* gen1, x1 */ err = pci_emul_add_capability(pi, (u_char *)&pciecap, sizeof(pciecap)); return (err); } /* * This function assumes that 'coff' is in the capabilities region of the * config space. */ static void pci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val) { int capid; uint8_t capoff, nextoff; /* Do not allow un-aligned writes */ if ((offset & (bytes - 1)) != 0) return; /* Find the capability that we want to update */ capoff = CAP_START_OFFSET; while (1) { nextoff = pci_get_cfgdata8(pi, capoff + 1); if (nextoff == 0) break; if (offset >= capoff && offset < nextoff) break; capoff = nextoff; } assert(offset >= capoff); /* * Capability ID and Next Capability Pointer are readonly. * However, some o/s's do 4-byte writes that include these. * For this case, trim the write back to 2 bytes and adjust * the data. */ if (offset == capoff || offset == capoff + 1) { if (offset == capoff && bytes == 4) { bytes = 2; offset += 2; val >>= 16; } else return; } capid = pci_get_cfgdata8(pi, capoff); switch (capid) { case PCIY_MSI: msicap_cfgwrite(pi, capoff, offset, bytes, val); break; case PCIY_MSIX: msixcap_cfgwrite(pi, capoff, offset, bytes, val); break; case PCIY_EXPRESS: pciecap_cfgwrite(pi, capoff, offset, bytes, val); break; default: break; } } static int pci_emul_iscap(struct pci_devinst *pi, int offset) { uint16_t sts; sts = pci_get_cfgdata16(pi, PCIR_STATUS); if ((sts & PCIM_STATUS_CAPPRESENT) != 0) { if (offset >= CAP_START_OFFSET && offset <= pi->pi_capend) return (1); } return (0); } static int pci_emul_fallback_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, int size, uint64_t *val, void *arg1, long arg2) { /* * Ignore writes; return 0xff's for reads. The mem read code * will take care of truncating to the correct size. */ if (dir == MEM_F_READ) { *val = 0xffffffffffffffff; } return (0); } static int pci_emul_ecfg_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, int bytes, uint64_t *val, void *arg1, long arg2) { int bus, slot, func, coff, in; coff = addr & 0xfff; func = (addr >> 12) & 0x7; slot = (addr >> 15) & 0x1f; bus = (addr >> 20) & 0xff; in = (dir == MEM_F_READ); if (in) *val = ~0UL; pci_cfgrw(ctx, vcpu, in, bus, slot, func, coff, bytes, (uint32_t *)val); return (0); } uint64_t pci_ecfg_base(void) { return (PCI_EMUL_ECFG_BASE); } #define BUSIO_ROUNDUP 32 #define BUSMEM_ROUNDUP (1024 * 1024) int init_pci(struct vmctx *ctx) { struct mem_range mr; struct pci_devemu *pde; struct businfo *bi; struct slotinfo *si; struct funcinfo *fi; size_t lowmem; int bus, slot, func; int error; pci_emul_iobase = PCI_EMUL_IOBASE; pci_emul_membase32 = vm_get_lowmem_limit(ctx); pci_emul_membase64 = PCI_EMUL_MEMBASE64; for (bus = 0; bus < MAXBUSES; bus++) { if ((bi = pci_businfo[bus]) == NULL) continue; /* * Keep track of the i/o and memory resources allocated to * this bus. */ bi->iobase = pci_emul_iobase; bi->membase32 = pci_emul_membase32; bi->membase64 = pci_emul_membase64; for (slot = 0; slot < MAXSLOTS; slot++) { si = &bi->slotinfo[slot]; for (func = 0; func < MAXFUNCS; func++) { fi = &si->si_funcs[func]; if (fi->fi_name == NULL) continue; pde = pci_emul_finddev(fi->fi_name); assert(pde != NULL); error = pci_emul_init(ctx, pde, bus, slot, func, fi); if (error) return (error); } } /* * Add some slop to the I/O and memory resources decoded by * this bus to give a guest some flexibility if it wants to * reprogram the BARs. */ pci_emul_iobase += BUSIO_ROUNDUP; pci_emul_iobase = roundup2(pci_emul_iobase, BUSIO_ROUNDUP); bi->iolimit = pci_emul_iobase; pci_emul_membase32 += BUSMEM_ROUNDUP; pci_emul_membase32 = roundup2(pci_emul_membase32, BUSMEM_ROUNDUP); bi->memlimit32 = pci_emul_membase32; pci_emul_membase64 += BUSMEM_ROUNDUP; pci_emul_membase64 = roundup2(pci_emul_membase64, BUSMEM_ROUNDUP); bi->memlimit64 = pci_emul_membase64; } /* * PCI backends are initialized before routing INTx interrupts * so that LPC devices are able to reserve ISA IRQs before * routing PIRQ pins. */ for (bus = 0; bus < MAXBUSES; bus++) { if ((bi = pci_businfo[bus]) == NULL) continue; for (slot = 0; slot < MAXSLOTS; slot++) { si = &bi->slotinfo[slot]; for (func = 0; func < MAXFUNCS; func++) { fi = &si->si_funcs[func]; if (fi->fi_devi == NULL) continue; pci_lintr_route(fi->fi_devi); } } } lpc_pirq_routed(); /* * The guest physical memory map looks like the following: * [0, lowmem) guest system memory * [lowmem, lowmem_limit) memory hole (may be absent) * [lowmem_limit, 0xE0000000) PCI hole (32-bit BAR allocation) * [0xE0000000, 0xF0000000) PCI extended config window * [0xF0000000, 4GB) LAPIC, IOAPIC, HPET, firmware * [4GB, 4GB + highmem) */ /* * Accesses to memory addresses that are not allocated to system * memory or PCI devices return 0xff's. */ lowmem = vm_get_lowmem_size(ctx); bzero(&mr, sizeof(struct mem_range)); mr.name = "PCI hole"; mr.flags = MEM_F_RW | MEM_F_IMMUTABLE; mr.base = lowmem; mr.size = (4ULL * 1024 * 1024 * 1024) - lowmem; mr.handler = pci_emul_fallback_handler; error = register_mem_fallback(&mr); assert(error == 0); /* PCI extended config space */ bzero(&mr, sizeof(struct mem_range)); mr.name = "PCI ECFG"; mr.flags = MEM_F_RW | MEM_F_IMMUTABLE; mr.base = PCI_EMUL_ECFG_BASE; mr.size = PCI_EMUL_ECFG_SIZE; mr.handler = pci_emul_ecfg_handler; error = register_mem(&mr); assert(error == 0); return (0); } static void pci_apic_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq, void *arg) { dsdt_line(" Package ()"); dsdt_line(" {"); dsdt_line(" 0x%X,", slot << 16 | 0xffff); dsdt_line(" 0x%02X,", pin - 1); dsdt_line(" Zero,"); dsdt_line(" 0x%X", ioapic_irq); dsdt_line(" },"); } static void pci_pirq_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq, void *arg) { char *name; name = lpc_pirq_name(pirq_pin); if (name == NULL) return; dsdt_line(" Package ()"); dsdt_line(" {"); dsdt_line(" 0x%X,", slot << 16 | 0xffff); dsdt_line(" 0x%02X,", pin - 1); dsdt_line(" %s,", name); dsdt_line(" 0x00"); dsdt_line(" },"); free(name); } /* * A bhyve virtual machine has a flat PCI hierarchy with a root port * corresponding to each PCI bus. */ static void pci_bus_write_dsdt(int bus) { struct businfo *bi; struct slotinfo *si; struct pci_devinst *pi; int count, func, slot; /* * If there are no devices on this 'bus' then just return. */ if ((bi = pci_businfo[bus]) == NULL) { /* * Bus 0 is special because it decodes the I/O ports used * for PCI config space access even if there are no devices * on it. */ if (bus != 0) return; } dsdt_line(" Device (PC%02X)", bus); dsdt_line(" {"); dsdt_line(" Name (_HID, EisaId (\"PNP0A03\"))"); dsdt_line(" Name (_ADR, Zero)"); dsdt_line(" Method (_BBN, 0, NotSerialized)"); dsdt_line(" {"); dsdt_line(" Return (0x%08X)", bus); dsdt_line(" }"); dsdt_line(" Name (_CRS, ResourceTemplate ()"); dsdt_line(" {"); dsdt_line(" WordBusNumber (ResourceProducer, MinFixed, " "MaxFixed, PosDecode,"); dsdt_line(" 0x0000, // Granularity"); dsdt_line(" 0x%04X, // Range Minimum", bus); dsdt_line(" 0x%04X, // Range Maximum", bus); dsdt_line(" 0x0000, // Translation Offset"); dsdt_line(" 0x0001, // Length"); dsdt_line(" ,, )"); if (bus == 0) { dsdt_indent(3); dsdt_fixed_ioport(0xCF8, 8); dsdt_unindent(3); dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " "PosDecode, EntireRange,"); dsdt_line(" 0x0000, // Granularity"); dsdt_line(" 0x0000, // Range Minimum"); dsdt_line(" 0x0CF7, // Range Maximum"); dsdt_line(" 0x0000, // Translation Offset"); dsdt_line(" 0x0CF8, // Length"); dsdt_line(" ,, , TypeStatic)"); dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " "PosDecode, EntireRange,"); dsdt_line(" 0x0000, // Granularity"); dsdt_line(" 0x0D00, // Range Minimum"); dsdt_line(" 0x%04X, // Range Maximum", PCI_EMUL_IOBASE - 1); dsdt_line(" 0x0000, // Translation Offset"); dsdt_line(" 0x%04X, // Length", PCI_EMUL_IOBASE - 0x0D00); dsdt_line(" ,, , TypeStatic)"); if (bi == NULL) { dsdt_line(" })"); goto done; } } assert(bi != NULL); /* i/o window */ dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " "PosDecode, EntireRange,"); dsdt_line(" 0x0000, // Granularity"); dsdt_line(" 0x%04X, // Range Minimum", bi->iobase); dsdt_line(" 0x%04X, // Range Maximum", bi->iolimit - 1); dsdt_line(" 0x0000, // Translation Offset"); dsdt_line(" 0x%04X, // Length", bi->iolimit - bi->iobase); dsdt_line(" ,, , TypeStatic)"); /* mmio window (32-bit) */ dsdt_line(" DWordMemory (ResourceProducer, PosDecode, " "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); dsdt_line(" 0x00000000, // Granularity"); dsdt_line(" 0x%08X, // Range Minimum\n", bi->membase32); dsdt_line(" 0x%08X, // Range Maximum\n", bi->memlimit32 - 1); dsdt_line(" 0x00000000, // Translation Offset"); dsdt_line(" 0x%08X, // Length\n", bi->memlimit32 - bi->membase32); dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); /* mmio window (64-bit) */ dsdt_line(" QWordMemory (ResourceProducer, PosDecode, " "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); dsdt_line(" 0x0000000000000000, // Granularity"); dsdt_line(" 0x%016lX, // Range Minimum\n", bi->membase64); dsdt_line(" 0x%016lX, // Range Maximum\n", bi->memlimit64 - 1); dsdt_line(" 0x0000000000000000, // Translation Offset"); dsdt_line(" 0x%016lX, // Length\n", bi->memlimit64 - bi->membase64); dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); dsdt_line(" })"); count = pci_count_lintr(bus); if (count != 0) { dsdt_indent(2); dsdt_line("Name (PPRT, Package ()"); dsdt_line("{"); pci_walk_lintr(bus, pci_pirq_prt_entry, NULL); dsdt_line("})"); dsdt_line("Name (APRT, Package ()"); dsdt_line("{"); pci_walk_lintr(bus, pci_apic_prt_entry, NULL); dsdt_line("})"); dsdt_line("Method (_PRT, 0, NotSerialized)"); dsdt_line("{"); dsdt_line(" If (PICM)"); dsdt_line(" {"); dsdt_line(" Return (APRT)"); dsdt_line(" }"); dsdt_line(" Else"); dsdt_line(" {"); dsdt_line(" Return (PPRT)"); dsdt_line(" }"); dsdt_line("}"); dsdt_unindent(2); } dsdt_indent(2); for (slot = 0; slot < MAXSLOTS; slot++) { si = &bi->slotinfo[slot]; for (func = 0; func < MAXFUNCS; func++) { pi = si->si_funcs[func].fi_devi; if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL) pi->pi_d->pe_write_dsdt(pi); } } dsdt_unindent(2); done: dsdt_line(" }"); } void pci_write_dsdt(void) { int bus; dsdt_indent(1); dsdt_line("Name (PICM, 0x00)"); dsdt_line("Method (_PIC, 1, NotSerialized)"); dsdt_line("{"); dsdt_line(" Store (Arg0, PICM)"); dsdt_line("}"); dsdt_line(""); dsdt_line("Scope (_SB)"); dsdt_line("{"); for (bus = 0; bus < MAXBUSES; bus++) pci_bus_write_dsdt(bus); dsdt_line("}"); dsdt_unindent(1); } int pci_bus_configured(int bus) { assert(bus >= 0 && bus < MAXBUSES); return (pci_businfo[bus] != NULL); } int pci_msi_enabled(struct pci_devinst *pi) { return (pi->pi_msi.enabled); } int pci_msi_maxmsgnum(struct pci_devinst *pi) { if (pi->pi_msi.enabled) return (pi->pi_msi.maxmsgnum); else return (0); } int pci_msix_enabled(struct pci_devinst *pi) { return (pi->pi_msix.enabled && !pi->pi_msi.enabled); } void pci_generate_msix(struct pci_devinst *pi, int index) { struct msix_table_entry *mte; if (!pci_msix_enabled(pi)) return; if (pi->pi_msix.function_mask) return; if (index >= pi->pi_msix.table_count) return; mte = &pi->pi_msix.table[index]; if ((mte->vector_control & PCIM_MSIX_VCTRL_MASK) == 0) { /* XXX Set PBA bit if interrupt is disabled */ vm_lapic_msi(pi->pi_vmctx, mte->addr, mte->msg_data); } } void pci_generate_msi(struct pci_devinst *pi, int index) { if (pci_msi_enabled(pi) && index < pci_msi_maxmsgnum(pi)) { vm_lapic_msi(pi->pi_vmctx, pi->pi_msi.addr, pi->pi_msi.msg_data + index); } } static bool pci_lintr_permitted(struct pci_devinst *pi) { uint16_t cmd; cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); return (!(pi->pi_msi.enabled || pi->pi_msix.enabled || (cmd & PCIM_CMD_INTxDIS))); } void pci_lintr_request(struct pci_devinst *pi) { struct businfo *bi; struct slotinfo *si; int bestpin, bestcount, pin; bi = pci_businfo[pi->pi_bus]; assert(bi != NULL); /* * Just allocate a pin from our slot. The pin will be * assigned IRQs later when interrupts are routed. */ si = &bi->slotinfo[pi->pi_slot]; bestpin = 0; bestcount = si->si_intpins[0].ii_count; for (pin = 1; pin < 4; pin++) { if (si->si_intpins[pin].ii_count < bestcount) { bestpin = pin; bestcount = si->si_intpins[pin].ii_count; } } si->si_intpins[bestpin].ii_count++; pi->pi_lintr.pin = bestpin + 1; pci_set_cfgdata8(pi, PCIR_INTPIN, bestpin + 1); } static void pci_lintr_route(struct pci_devinst *pi) { struct businfo *bi; struct intxinfo *ii; if (pi->pi_lintr.pin == 0) return; bi = pci_businfo[pi->pi_bus]; assert(bi != NULL); ii = &bi->slotinfo[pi->pi_slot].si_intpins[pi->pi_lintr.pin - 1]; /* * Attempt to allocate an I/O APIC pin for this intpin if one * is not yet assigned. */ if (ii->ii_ioapic_irq == 0) ii->ii_ioapic_irq = ioapic_pci_alloc_irq(); assert(ii->ii_ioapic_irq > 0); /* * Attempt to allocate a PIRQ pin for this intpin if one is * not yet assigned. */ if (ii->ii_pirq_pin == 0) ii->ii_pirq_pin = pirq_alloc_pin(pi->pi_vmctx); assert(ii->ii_pirq_pin > 0); pi->pi_lintr.ioapic_irq = ii->ii_ioapic_irq; pi->pi_lintr.pirq_pin = ii->ii_pirq_pin; pci_set_cfgdata8(pi, PCIR_INTLINE, pirq_irq(ii->ii_pirq_pin)); } void pci_lintr_assert(struct pci_devinst *pi) { assert(pi->pi_lintr.pin > 0); pthread_mutex_lock(&pi->pi_lintr.lock); if (pi->pi_lintr.state == IDLE) { if (pci_lintr_permitted(pi)) { pi->pi_lintr.state = ASSERTED; pci_irq_assert(pi); } else pi->pi_lintr.state = PENDING; } pthread_mutex_unlock(&pi->pi_lintr.lock); } void pci_lintr_deassert(struct pci_devinst *pi) { assert(pi->pi_lintr.pin > 0); pthread_mutex_lock(&pi->pi_lintr.lock); if (pi->pi_lintr.state == ASSERTED) { pi->pi_lintr.state = IDLE; pci_irq_deassert(pi); } else if (pi->pi_lintr.state == PENDING) pi->pi_lintr.state = IDLE; pthread_mutex_unlock(&pi->pi_lintr.lock); } static void pci_lintr_update(struct pci_devinst *pi) { pthread_mutex_lock(&pi->pi_lintr.lock); if (pi->pi_lintr.state == ASSERTED && !pci_lintr_permitted(pi)) { pci_irq_deassert(pi); pi->pi_lintr.state = PENDING; } else if (pi->pi_lintr.state == PENDING && pci_lintr_permitted(pi)) { pi->pi_lintr.state = ASSERTED; pci_irq_assert(pi); } pthread_mutex_unlock(&pi->pi_lintr.lock); } int pci_count_lintr(int bus) { int count, slot, pin; struct slotinfo *slotinfo; count = 0; if (pci_businfo[bus] != NULL) { for (slot = 0; slot < MAXSLOTS; slot++) { slotinfo = &pci_businfo[bus]->slotinfo[slot]; for (pin = 0; pin < 4; pin++) { if (slotinfo->si_intpins[pin].ii_count != 0) count++; } } } return (count); } void pci_walk_lintr(int bus, pci_lintr_cb cb, void *arg) { struct businfo *bi; struct slotinfo *si; struct intxinfo *ii; int slot, pin; if ((bi = pci_businfo[bus]) == NULL) return; for (slot = 0; slot < MAXSLOTS; slot++) { si = &bi->slotinfo[slot]; for (pin = 0; pin < 4; pin++) { ii = &si->si_intpins[pin]; if (ii->ii_count != 0) cb(bus, slot, pin + 1, ii->ii_pirq_pin, ii->ii_ioapic_irq, arg); } } } /* * Return 1 if the emulated device in 'slot' is a multi-function device. * Return 0 otherwise. */ static int pci_emul_is_mfdev(int bus, int slot) { struct businfo *bi; struct slotinfo *si; int f, numfuncs; numfuncs = 0; if ((bi = pci_businfo[bus]) != NULL) { si = &bi->slotinfo[slot]; for (f = 0; f < MAXFUNCS; f++) { if (si->si_funcs[f].fi_devi != NULL) { numfuncs++; } } } return (numfuncs > 1); } /* * Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on * whether or not is a multi-function being emulated in the pci 'slot'. */ static void pci_emul_hdrtype_fixup(int bus, int slot, int off, int bytes, uint32_t *rv) { int mfdev; if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) { mfdev = pci_emul_is_mfdev(bus, slot); switch (bytes) { case 1: case 2: *rv &= ~PCIM_MFDEV; if (mfdev) { *rv |= PCIM_MFDEV; } break; case 4: *rv &= ~(PCIM_MFDEV << 16); if (mfdev) { *rv |= (PCIM_MFDEV << 16); } break; } } } static void pci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes) { int i, rshift; uint32_t cmd, cmd2, changed, old, readonly; cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); /* stash old value */ /* * From PCI Local Bus Specification 3.0 sections 6.2.2 and 6.2.3. * * XXX Bits 8, 11, 12, 13, 14 and 15 in the status register are * 'write 1 to clear'. However these bits are not set to '1' by * any device emulation so it is simpler to treat them as readonly. */ rshift = (coff & 0x3) * 8; readonly = 0xFFFFF880 >> rshift; old = CFGREAD(pi, coff, bytes); new &= ~readonly; new |= (old & readonly); CFGWRITE(pi, coff, new, bytes); /* update config */ cmd2 = pci_get_cfgdata16(pi, PCIR_COMMAND); /* get updated value */ changed = cmd ^ cmd2; /* * If the MMIO or I/O address space decoding has changed then * register/unregister all BARs that decode that address space. */ for (i = 0; i <= PCI_BARMAX; i++) { switch (pi->pi_bar[i].type) { case PCIBAR_NONE: case PCIBAR_MEMHI64: break; case PCIBAR_IO: /* I/O address space decoding changed? */ if (changed & PCIM_CMD_PORTEN) { if (porten(pi)) register_bar(pi, i); else unregister_bar(pi, i); } break; case PCIBAR_MEM32: case PCIBAR_MEM64: /* MMIO address space decoding changed? */ if (changed & PCIM_CMD_MEMEN) { if (memen(pi)) register_bar(pi, i); else unregister_bar(pi, i); } break; default: assert(0); } } /* * If INTx has been unmasked and is pending, assert the * interrupt. */ pci_lintr_update(pi); } static void pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func, int coff, int bytes, uint32_t *eax) { struct businfo *bi; struct slotinfo *si; struct pci_devinst *pi; struct pci_devemu *pe; int idx, needcfg; uint64_t addr, bar, mask; if ((bi = pci_businfo[bus]) != NULL) { si = &bi->slotinfo[slot]; pi = si->si_funcs[func].fi_devi; } else pi = NULL; /* * Just return if there is no device at this slot:func or if the * the guest is doing an un-aligned access. */ if (pi == NULL || (bytes != 1 && bytes != 2 && bytes != 4) || (coff & (bytes - 1)) != 0) { if (in) *eax = 0xffffffff; return; } /* * Ignore all writes beyond the standard config space and return all * ones on reads. */ if (coff >= PCI_REGMAX + 1) { if (in) { *eax = 0xffffffff; /* * Extended capabilities begin at offset 256 in config * space. Absence of extended capabilities is signaled * with all 0s in the extended capability header at * offset 256. */ if (coff <= PCI_REGMAX + 4) *eax = 0x00000000; } return; } pe = pi->pi_d; /* * Config read */ if (in) { /* Let the device emulation override the default handler */ if (pe->pe_cfgread != NULL) { needcfg = pe->pe_cfgread(ctx, vcpu, pi, coff, bytes, eax); } else { needcfg = 1; } if (needcfg) *eax = CFGREAD(pi, coff, bytes); pci_emul_hdrtype_fixup(bus, slot, coff, bytes, eax); } else { /* Let the device emulation override the default handler */ if (pe->pe_cfgwrite != NULL && (*pe->pe_cfgwrite)(ctx, vcpu, pi, coff, bytes, *eax) == 0) return; /* * Special handling for write to BAR registers */ if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) { /* * Ignore writes to BAR registers that are not * 4-byte aligned. */ if (bytes != 4 || (coff & 0x3) != 0) return; idx = (coff - PCIR_BAR(0)) / 4; mask = ~(pi->pi_bar[idx].size - 1); switch (pi->pi_bar[idx].type) { case PCIBAR_NONE: pi->pi_bar[idx].addr = bar = 0; break; case PCIBAR_IO: addr = *eax & mask; addr &= 0xffff; bar = addr | PCIM_BAR_IO_SPACE; /* * Register the new BAR value for interception */ if (addr != pi->pi_bar[idx].addr) { update_bar_address(pi, addr, idx, PCIBAR_IO); } break; case PCIBAR_MEM32: addr = bar = *eax & mask; bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; if (addr != pi->pi_bar[idx].addr) { update_bar_address(pi, addr, idx, PCIBAR_MEM32); } break; case PCIBAR_MEM64: addr = bar = *eax & mask; bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | PCIM_BAR_MEM_PREFETCH; if (addr != (uint32_t)pi->pi_bar[idx].addr) { update_bar_address(pi, addr, idx, PCIBAR_MEM64); } break; case PCIBAR_MEMHI64: mask = ~(pi->pi_bar[idx - 1].size - 1); addr = ((uint64_t)*eax << 32) & mask; bar = addr >> 32; if (bar != pi->pi_bar[idx - 1].addr >> 32) { update_bar_address(pi, addr, idx - 1, PCIBAR_MEMHI64); } break; default: assert(0); } pci_set_cfgdata32(pi, coff, bar); } else if (pci_emul_iscap(pi, coff)) { pci_emul_capwrite(pi, coff, bytes, *eax); } else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) { pci_emul_cmdsts_write(pi, coff, *eax, bytes); } else { CFGWRITE(pi, coff, *eax, bytes); } } } static int cfgenable, cfgbus, cfgslot, cfgfunc, cfgoff; static int pci_emul_cfgaddr(struct vmctx *ctx, int vcpu, int in, int port, int bytes, uint32_t *eax, void *arg) { uint32_t x; if (bytes != 4) { if (in) *eax = (bytes == 2) ? 0xffff : 0xff; return (0); } if (in) { x = (cfgbus << 16) | (cfgslot << 11) | (cfgfunc << 8) | cfgoff; if (cfgenable) x |= CONF1_ENABLE; *eax = x; } else { x = *eax; cfgenable = (x & CONF1_ENABLE) == CONF1_ENABLE; cfgoff = x & PCI_REGMAX; cfgfunc = (x >> 8) & PCI_FUNCMAX; cfgslot = (x >> 11) & PCI_SLOTMAX; cfgbus = (x >> 16) & PCI_BUSMAX; } return (0); } INOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_INOUT, pci_emul_cfgaddr); static int pci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes, uint32_t *eax, void *arg) { int coff; assert(bytes == 1 || bytes == 2 || bytes == 4); coff = cfgoff + (port - CONF1_DATA_PORT); if (cfgenable) { pci_cfgrw(ctx, vcpu, in, cfgbus, cfgslot, cfgfunc, coff, bytes, eax); } else { /* Ignore accesses to cfgdata if not enabled by cfgaddr */ if (in) *eax = 0xffffffff; } return (0); } INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata); INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata); INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata); INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata); #define PCI_EMUL_TEST #ifdef PCI_EMUL_TEST /* * Define a dummy test device */ #define DIOSZ 8 #define DMEMSZ 4096 struct pci_emul_dsoftc { uint8_t ioregs[DIOSZ]; uint8_t memregs[2][DMEMSZ]; }; #define PCI_EMUL_MSI_MSGS 4 #define PCI_EMUL_MSIX_MSGS 16 static int pci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, char *opts) { int error; struct pci_emul_dsoftc *sc; sc = calloc(1, sizeof(struct pci_emul_dsoftc)); pi->pi_arg = sc; pci_set_cfgdata16(pi, PCIR_DEVICE, 0x0001); pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10DD); pci_set_cfgdata8(pi, PCIR_CLASS, 0x02); error = pci_emul_add_msicap(pi, PCI_EMUL_MSI_MSGS); assert(error == 0); error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, DIOSZ); assert(error == 0); error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ); assert(error == 0); error = pci_emul_alloc_bar(pi, 2, PCIBAR_MEM32, DMEMSZ); assert(error == 0); return (0); } static void pci_emul_diow(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, uint64_t offset, int size, uint64_t value) { int i; struct pci_emul_dsoftc *sc = pi->pi_arg; if (baridx == 0) { if (offset + size > DIOSZ) { printf("diow: iow too large, offset %ld size %d\n", offset, size); return; } if (size == 1) { sc->ioregs[offset] = value & 0xff; } else if (size == 2) { *(uint16_t *)&sc->ioregs[offset] = value & 0xffff; } else if (size == 4) { *(uint32_t *)&sc->ioregs[offset] = value; } else { printf("diow: iow unknown size %d\n", size); } /* * Special magic value to generate an interrupt */ if (offset == 4 && size == 4 && pci_msi_enabled(pi)) pci_generate_msi(pi, value % pci_msi_maxmsgnum(pi)); if (value == 0xabcdef) { for (i = 0; i < pci_msi_maxmsgnum(pi); i++) pci_generate_msi(pi, i); } } if (baridx == 1 || baridx == 2) { if (offset + size > DMEMSZ) { printf("diow: memw too large, offset %ld size %d\n", offset, size); return; } i = baridx - 1; /* 'memregs' index */ if (size == 1) { sc->memregs[i][offset] = value; } else if (size == 2) { *(uint16_t *)&sc->memregs[i][offset] = value; } else if (size == 4) { *(uint32_t *)&sc->memregs[i][offset] = value; } else if (size == 8) { *(uint64_t *)&sc->memregs[i][offset] = value; } else { printf("diow: memw unknown size %d\n", size); } /* * magic interrupt ?? */ } if (baridx > 2) { printf("diow: unknown bar idx %d\n", baridx); } } static uint64_t pci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, uint64_t offset, int size) { struct pci_emul_dsoftc *sc = pi->pi_arg; uint32_t value; int i; if (baridx == 0) { if (offset + size > DIOSZ) { printf("dior: ior too large, offset %ld size %d\n", offset, size); return (0); } if (size == 1) { value = sc->ioregs[offset]; } else if (size == 2) { value = *(uint16_t *) &sc->ioregs[offset]; } else if (size == 4) { value = *(uint32_t *) &sc->ioregs[offset]; } else { printf("dior: ior unknown size %d\n", size); } } if (baridx == 1 || baridx == 2) { if (offset + size > DMEMSZ) { printf("dior: memr too large, offset %ld size %d\n", offset, size); return (0); } i = baridx - 1; /* 'memregs' index */ if (size == 1) { value = sc->memregs[i][offset]; } else if (size == 2) { value = *(uint16_t *) &sc->memregs[i][offset]; } else if (size == 4) { value = *(uint32_t *) &sc->memregs[i][offset]; } else if (size == 8) { value = *(uint64_t *) &sc->memregs[i][offset]; } else { printf("dior: ior unknown size %d\n", size); } } if (baridx > 2) { printf("dior: unknown bar idx %d\n", baridx); return (0); } return (value); } struct pci_devemu pci_dummy = { .pe_emu = "dummy", .pe_init = pci_emul_dinit, .pe_barwrite = pci_emul_diow, .pe_barread = pci_emul_dior }; PCI_EMUL_SET(pci_dummy); #endif /* PCI_EMUL_TEST */ Index: head/usr.sbin/bsdconfig/bsdconfig =================================================================== --- head/usr.sbin/bsdconfig/bsdconfig (revision 289676) +++ head/usr.sbin/bsdconfig/bsdconfig (revision 289677) @@ -1,428 +1,428 @@ #!/bin/sh #- # Copyright (c) 2012 Ron McDowell # Copyright (c) 2012-2014 Devin Teske # 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. # # $FreeBSD$ # ############################################################ INCLUDES # When common.subr is included, it automatically scans "$@" for `-d' and/or # `-D file' arguments to conditionally enable debugging. Similarly, when # dialog.subr is included, it automatically scans "$@" for `-X' and/or `-S'. # To prevent this scanning from becoming confused by extra options, define # any/all extra arguments to use in the optstring to getopts when scanning # for dedicated options such as those described. # # NOTE: This needs to be declared before including `common.subr'. # NOTE: You really only need to list flags that require an argument as unknown # flags are silently accepted unless they take an argument (in which case # the following argument will terminate option processing unless it looks # like a flag). # GETOPTS_EXTRA="f:" BSDCFG_SHARE="/usr/share/bsdconfig" . $BSDCFG_SHARE/common.subr || exit 1 f_dprintf "%s: loading includes..." "$0" f_include $BSDCFG_SHARE/dialog.subr f_include $BSDCFG_SHARE/mustberoot.subr f_include $BSDCFG_SHARE/strings.subr BSDCFG_LIBE="/usr/libexec/bsdconfig" f_include_lang $BSDCFG_LIBE/include/messages.subr BSDCONFIG_HELPFILE=$BSDCFG_LIBE/include/bsdconfig.hlp USAGE_HELPFILE=$BSDCFG_LIBE/include/usage.hlp ############################################################ CONFIGURATION # # Alternate `local' libexec directory for add-on modules (e.g., from ports) # BSDCFG_LOCAL_LIBE="/usr/local/libexec/bsdconfig" ############################################################ FUNCTIONS # usage # # display usage and exit # usage() { local index="INDEX" local cmd_list # Calculated below cd $BSDCFG_LIBE # No need to preserve CWD (headed toward exit) # Test for language-specific indices f_quietly ls */"$index.${LANG:-$LC_ALL}" && index="$index.${LANG:-$LC_ALL}" cmd_list=$( awk '/^menu_selection="/ { sub(/\|.*/, "") sub(/^menu_selection="/, "") print }' */$index | sort ) local alt_cmd_list # Calculated below (if $BSDCFG_LOCAL_LIBE exists) if f_quietly cd $BSDCFG_LOCAL_LIBE; then # No need to preserve CWD (headed toward exit) # Test for language-specific indices f_quietly ls */"$index.${LANG:-$LC_ALL}" && index="$index.${LANG:-$LC_ALL}" alt_cmd_list=$( awk '/^menu_selection="/ { sub(/\|.*/, "") sub(/^menu_selection="/, "") print }' */$index 2> /dev/null | sort ) # Conflate lists, removing duplicates cmd_list=$( printf "%s\n%s\n" \ "$cmd_list" "$alt_cmd_list" | sort -u ) fi # # Determine the longest command-length (in characters) # local longest_cmd longest_cmd=$( echo "$cmd_list" | f_longest_line_length ) f_dprintf "longest_cmd=[%s]" "$longest_cmd" # # Determine the maximum width of terminal/console # local max_size="$( stty size 2> /dev/null )" : ${max_size:="24 80"} local max_width="${max_size#*[$IFS]}" f_dprintf "max_width=[%s]" "$max_width" # # Using the longest command-length as the width of a single column, # determine if we can use more than one column to display commands. # local x=$longest_cmd ncols=1 - x=$(( $x + 8 )) # Accomodate leading tab character + x=$(( $x + 8 )) # Accommodate leading tab character x=$(( $x + 3 + $longest_cmd )) # Preload end of next column while [ $x -lt $max_width ]; do ncols=$(( $ncols + 1 )) x=$(( $x + 3 + $longest_cmd )) done f_dprintf "ncols=[%u] x=[%u]" $ncols $x # # Re-format the command-list into multiple columns # cmd_list=$( eval "$( echo "$cmd_list" | awk -v ncols=$ncols -v size=$longest_cmd ' BEGIN { n = 0 row_item[1] = "" } function print_row() { fmt = "printf \"\\t%-" size "s" for (i = 1; i < cur_col; i++) fmt = fmt " %-" size "s" fmt = fmt "\\n\"" printf "%s", fmt for (i = 1; i <= cur_col; i++) printf " \"%s\"", row_item[i] print "" } { n++ cur_col = (( n - 1 ) % ncols ) + 1 printf "f_dprintf \"row_item[%u]=[%%s]\" \"%s\"\n", cur_col, $0 row_item[cur_col] = $0 if ( cur_col == ncols ) print_row() } END { if ( cur_col < ncols ) print_row() }' )" ) f_usage $BSDCFG_LIBE/USAGE \ "PROGRAM_NAME" "$pgm" \ "COMMAND_LIST" "$cmd_list" # Never reached } # dialog_menu_main # # Display the dialog(1)-based application main menu. # dialog_menu_main() { local title="$DIALOG_TITLE" local btitle="$DIALOG_BACKTITLE" local prompt="$msg_menu_text" local menu_list=" 'X' '$msg_exit' '$msg_exit_bsdconfig' '1' '$msg_usage' '$msg_quick_start_how_to_use_this_menu_system' " # END-QUOTE local defaultitem= # Calculated below local hline= # # Pick up the base modules (directories named `[0-9][0-9][0-9].*') # local menuitem menu_title menu_help menu_selection index=2 for menuitem in $( cd $BSDCFG_LIBE && ls -d [0-9][0-9][0-9].* ); do [ -f "$BSDCFG_LIBE/$menuitem/INDEX" ] || continue [ $index -lt ${#DIALOG_MENU_TAGS} ] || break menu_program= menu_title= menu_help= f_include_lang $BSDCFG_LIBE/$menuitem/INDEX [ "$menu_program" ] || continue case "$menu_program" in /*) : already fully qualified ;; *) menu_program="$menuitem/$menu_program" esac tag=$( f_substr "$DIALOG_MENU_TAGS" $index 1 ) setvar "menu_program$tag" "$menu_program" f_shell_escape "$menu_title" menu_title f_shell_escape "$menu_help" menu_help menu_list="$menu_list '$tag' '$menu_title' '$menu_help'" index=$(( $index + 1 )) done # # Process the `local' libexec sources. # # Whereas modules in $BSDCFG_LIBE must be named [0-9][0-9][0-9].* # modules in $BSDCFG_LOCAL_LIBE should NOT be named this way (making it # more practical for port-maintainers). # # This also has the fortunate side-effect of making the de-duplication # effort rather simple (because so-called `base' modules must be named # differently than add-on modules). # local separator_added= for menuitem in $( cd "$BSDCFG_LOCAL_LIBE" 2> /dev/null && ls -d * ) do # Skip the module if it looks like a `base' module case "$menuitem" in [0-9][0-9][0-9].*) continue;; esac [ -f "$BSDCFG_LOCAL_LIBE/$menuitem/INDEX" ] || continue [ $index -lt ${#DIALOG_MENU_TAGS} ] || break menu_program= menu_title= menu_help= f_include_lang $BSDCFG_LOCAL_LIBE/$menuitem/INDEX || continue [ "$menu_program" ] || continue if [ ! "$separator_added" ]; then menu_list="$menu_list '-' '-' ''" separator_added=1 fi case "$menu_program" in /*) : already fully qualified ;; *) menu_program="$BSDCFG_LOCAL_LIBE/$menuitem/$menu_program" esac tag=$( f_substr "$DIALOG_MENU_TAGS" $index 1 ) setvar "menu_program$tag" "$menu_program" f_shell_escape "$menu_title" menu_title f_shell_escape "$menu_help" menu_help menu_list="$menu_list '$tag' '$menu_title' '$menu_help'" index=$(( $index + 1 )) done local height width rows eval f_dialog_menu_with_help_size height width rows \ \"\$title\" \ \"\$btitle\" \ \"\$prompt\" \ \"\$hline\" \ $menu_list # Obtain default-item from previously stored selection f_dialog_default_fetch defaultitem local menu_choice menu_choice=$( eval $DIALOG \ --clear \ --title \"\$title\" \ --backtitle \"\$btitle\" \ --hline \"\$hline\" \ --item-help \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_exit_bsdconfig\" \ --help-button \ --help-label \"\$msg_help\" \ ${USE_XDIALOG:+--help \"\"} \ --default-item \"\$defaultitem\" \ --menu \"\$prompt\" \ $height $width $rows \ $menu_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) local retval=$? f_dialog_data_sanitize menu_choice f_dialog_menutag_store "$menu_choice" # Only update default-item on success [ $retval -eq $DIALOG_OK ] && f_dialog_default_store "$menu_choice" return $retval } ############################################################ MAIN # # If $0 is not "bsdconfig", interpret it either as a keyword to a menuitem or # as a valid resword (see script.subr for additional details about reswords). # if [ "$pgm" != "bsdconfig" ]; then if indexfile=$( f_index_file "$pgm" ) && cmd=$( f_index_menusel_command "$indexfile" "$pgm" ) then f_dprintf "pgm=[%s] cmd=[%s] *=[%s]" "$pgm" "$cmd" "$*" exec "$cmd" "$@" || exit 1 else f_include $BSDCFG_SHARE/script.subr for resword in $RESWORDS; do [ "$pgm" = "$resword" ] || continue # Found a match f_dprintf "pgm=[%s] A valid resWord!" "$pgm" f_dispatch $resword $resword "$@" exit $? done fi fi # # Process command-line arguments # scripts_loaded=0 while getopts f:h$GETOPTS_STDARGS flag; do case "$flag" in f) [ $scripts_loaded -eq 0 ] && f_include $BSDCFG_SHARE/script.subr f_script_load "$OPTARG" scripts_loaded=$(( $scripts_loaded + 1 )) ;; h|\?) usage ;; esac done shift $(( $OPTIND - 1 )) # If we've loaded any scripts, do not continue any further [ $scripts_loaded -gt 0 ] && exit # # Initialize # f_dialog_title "$msg_main_menu" [ "$SECURE" ] && f_mustberoot_init # Incorporate rc-file if it exists [ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" # # If a non-option argument was passed, process it as a menuitem selection... # if [ "$1" ]; then # # ...unless it's a long-option for usage. # case "$1" in -help|--help|-\?) usage # Not reached esac # # Find the INDEX (possibly i18n) claiming this keyword and get the # command to execute from the menu_selection line. # if ! { indexfile=$( f_index_file "$1" ) && cmd=$( f_index_menusel_command "$indexfile" "$1" ) }; then # no matches, display usage (which shows valid keywords) f_err "%s: %s: $msg_not_found\n" "$pgm" "$1" usage # Not reached fi f_dprintf "cmd=[%s] *=[%s]" "$cmd" "$*" shift exec $cmd ${USE_XDIALOG:+-X} "$@" || exit 1 # Not reached fi # # Launch application main menu # while :; do dialog_menu_main retval=$? f_dialog_menutag_fetch mtag f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" if [ $retval -eq $DIALOG_HELP ]; then f_show_help "$BSDCONFIG_HELPFILE" continue elif [ $retval -ne $DIALOG_OK ]; then f_die fi case "$mtag" in X) break ;; 1) # Usage f_show_help "$USAGE_HELPFILE" continue esac # Anything else is a dynamically loaded menuitem f_getvar menu_program$mtag menu_program case "$menu_program" in /*) cmd="$menu_program" ;; *) cmd="$BSDCFG_LIBE/$menu_program" esac f_dprintf "cmd=[%s]" "$cmd" $cmd ${USE_XDIALOG:+-X} done exit $SUCCESS ################################################################################ # END ################################################################################ Index: head/usr.sbin/config/config.h =================================================================== --- head/usr.sbin/config/config.h (revision 289676) +++ head/usr.sbin/config/config.h (revision 289677) @@ -1,211 +1,211 @@ /* * Copyright (c) 1980, 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. * 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. * * @(#)config.h 8.1 (Berkeley) 6/6/93 * $FreeBSD$ */ /* * Config. */ #include #include #include #include struct cfgfile { STAILQ_ENTRY(cfgfile) cfg_next; char *cfg_path; }; STAILQ_HEAD(, cfgfile) cfgfiles; struct file_list { STAILQ_ENTRY(file_list) f_next; char *f_fn; /* the name */ int f_type; /* type */ u_char f_flags; /* see below */ char *f_compilewith; /* special make rule if present */ - char *f_depends; /* additional dependancies */ + char *f_depends; /* additional dependencies */ char *f_clean; /* File list to add to clean rule */ char *f_warn; /* warning message */ const char *f_objprefix; /* prefix string for object name */ }; struct files_name { char *f_name; STAILQ_ENTRY(files_name) f_next; }; /* * Types. */ #define NORMAL 1 #define PROFILING 3 #define NODEPEND 4 #define LOCAL 5 #define DEVDONE 0x80000000 #define TYPEMASK 0x7fffffff /* * Attributes (flags). */ #define NO_IMPLCT_RULE 1 #define NO_OBJ 2 #define BEFORE_DEPEND 4 #define NOWERROR 16 struct device { int d_done; /* processed */ char *d_name; /* name of device (e.g. rk11) */ #define UNKNOWN -2 /* -2 means not set yet */ STAILQ_ENTRY(device) d_next; /* Next one in list */ }; struct config { char *s_sysname; }; /* * Config has a global notion of which machine type is * being used. It uses the name of the machine in choosing * files and directories. Thus if the name of the machine is ``i386'', * it will build from ``Makefile.i386'' and use ``../i386/inline'' * in the makerules, etc. machinearch is the global notion of the * MACHINE_ARCH for this MACHINE. */ char *machinename; char *machinearch; /* * For each machine, a set of CPU's may be specified as supported. * These and the options (below) are put in the C flags in the makefile. */ struct cputype { char *cpu_name; SLIST_ENTRY(cputype) cpu_next; }; SLIST_HEAD(, cputype) cputype; /* * A set of options may also be specified which are like CPU types, * but which may also specify values for the options. * A separate set of options may be defined for make-style options. */ struct opt { char *op_name; char *op_value; int op_ownfile; /* true = own file, false = makefile */ SLIST_ENTRY(opt) op_next; SLIST_ENTRY(opt) op_append; }; SLIST_HEAD(opt_head, opt) opt, mkopt, rmopts; struct opt_list { char *o_name; char *o_file; int o_flags; #define OL_ALIAS 1 SLIST_ENTRY(opt_list) o_next; }; SLIST_HEAD(, opt_list) otab; struct hint { char *hint_name; STAILQ_ENTRY(hint) hint_next; }; STAILQ_HEAD(hint_head, hint) hints; struct includepath { char *path; SLIST_ENTRY(includepath) path_next; }; SLIST_HEAD(, includepath) includepath; /* * Tag present in the kernelconf.tmlp template file. It's mandatory for those * two strings to be the same. Otherwise you'll get into trouble. */ #define KERNCONFTAG "%%KERNCONFFILE%%" /* * Faked option to note, that the configuration file has been taken from the * kernel file and inclusion of DEFAULTS etc.. isn't nessesery, because we * already have a list of all required devices. */ #define OPT_AUTOGEN "CONFIG_AUTOGENERATED" extern char *ident; extern char *env; extern char kernconfstr[]; extern int do_trace; extern int envmode; extern int hintmode; extern int incignore; char *get_word(FILE *); char *get_quoted_word(FILE *); char *path(const char *); char *raisestr(char *); void remember(const char *); void moveifchanged(const char *, const char *); int yylex(void); void options(void); void makefile(void); void makeenv(void); void makehints(void); void headers(void); void cfgfile_add(const char *); void cfgfile_removeall(void); FILE *open_makefile_template(void); extern STAILQ_HEAD(device_head, device) dtab; extern char errbuf[80]; extern int yyline; extern const char *yyfile; extern STAILQ_HEAD(file_list_head, file_list) ftab; extern STAILQ_HEAD(files_name_head, files_name) fntab; extern int profiling; extern int debugging; extern int found_defaults; extern int maxusers; extern char *PREFIX; /* Config file name - for error messages */ extern char srcdir[]; /* root of the kernel source tree */ #define eq(a,b) (!strcmp(a,b)) #define ns(s) strdup(s) Index: head/usr.sbin/ctld/ctld.c =================================================================== --- head/usr.sbin/ctld/ctld.c (revision 289676) +++ head/usr.sbin/ctld/ctld.c (revision 289677) @@ -1,2618 +1,2618 @@ /*- * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * 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 #include #include #include #include #include #include #include "ctld.h" #include "isns.h" bool proxy_mode = false; static volatile bool sighup_received = false; static volatile bool sigterm_received = false; static volatile bool sigalrm_received = false; static int nchildren = 0; static uint16_t last_portal_group_tag = 0xff; static void usage(void) { fprintf(stderr, "usage: ctld [-d][-f config-file]\n"); exit(1); } char * checked_strdup(const char *s) { char *c; c = strdup(s); if (c == NULL) log_err(1, "strdup"); return (c); } struct conf * conf_new(void) { struct conf *conf; conf = calloc(1, sizeof(*conf)); if (conf == NULL) log_err(1, "calloc"); TAILQ_INIT(&conf->conf_luns); TAILQ_INIT(&conf->conf_targets); TAILQ_INIT(&conf->conf_auth_groups); TAILQ_INIT(&conf->conf_ports); TAILQ_INIT(&conf->conf_portal_groups); TAILQ_INIT(&conf->conf_pports); TAILQ_INIT(&conf->conf_isns); conf->conf_isns_period = 900; conf->conf_isns_timeout = 5; conf->conf_debug = 0; conf->conf_timeout = 60; conf->conf_maxproc = 30; return (conf); } void conf_delete(struct conf *conf) { struct lun *lun, *ltmp; struct target *targ, *tmp; struct auth_group *ag, *cagtmp; struct portal_group *pg, *cpgtmp; struct pport *pp, *pptmp; struct isns *is, *istmp; assert(conf->conf_pidfh == NULL); TAILQ_FOREACH_SAFE(lun, &conf->conf_luns, l_next, ltmp) lun_delete(lun); TAILQ_FOREACH_SAFE(targ, &conf->conf_targets, t_next, tmp) target_delete(targ); TAILQ_FOREACH_SAFE(ag, &conf->conf_auth_groups, ag_next, cagtmp) auth_group_delete(ag); TAILQ_FOREACH_SAFE(pg, &conf->conf_portal_groups, pg_next, cpgtmp) portal_group_delete(pg); TAILQ_FOREACH_SAFE(pp, &conf->conf_pports, pp_next, pptmp) pport_delete(pp); TAILQ_FOREACH_SAFE(is, &conf->conf_isns, i_next, istmp) isns_delete(is); assert(TAILQ_EMPTY(&conf->conf_ports)); free(conf->conf_pidfile_path); free(conf); } static struct auth * auth_new(struct auth_group *ag) { struct auth *auth; auth = calloc(1, sizeof(*auth)); if (auth == NULL) log_err(1, "calloc"); auth->a_auth_group = ag; TAILQ_INSERT_TAIL(&ag->ag_auths, auth, a_next); return (auth); } static void auth_delete(struct auth *auth) { TAILQ_REMOVE(&auth->a_auth_group->ag_auths, auth, a_next); free(auth->a_user); free(auth->a_secret); free(auth->a_mutual_user); free(auth->a_mutual_secret); free(auth); } const struct auth * auth_find(const struct auth_group *ag, const char *user) { const struct auth *auth; TAILQ_FOREACH(auth, &ag->ag_auths, a_next) { if (strcmp(auth->a_user, user) == 0) return (auth); } return (NULL); } static void auth_check_secret_length(struct auth *auth) { size_t len; len = strlen(auth->a_secret); if (len > 16) { if (auth->a_auth_group->ag_name != NULL) log_warnx("secret for user \"%s\", auth-group \"%s\", " "is too long; it should be at most 16 characters " "long", auth->a_user, auth->a_auth_group->ag_name); else log_warnx("secret for user \"%s\", target \"%s\", " "is too long; it should be at most 16 characters " "long", auth->a_user, auth->a_auth_group->ag_target->t_name); } if (len < 12) { if (auth->a_auth_group->ag_name != NULL) log_warnx("secret for user \"%s\", auth-group \"%s\", " "is too short; it should be at least 12 characters " "long", auth->a_user, auth->a_auth_group->ag_name); else log_warnx("secret for user \"%s\", target \"%s\", " "is too short; it should be at least 16 characters " "long", auth->a_user, auth->a_auth_group->ag_target->t_name); } if (auth->a_mutual_secret != NULL) { len = strlen(auth->a_mutual_secret); if (len > 16) { if (auth->a_auth_group->ag_name != NULL) log_warnx("mutual secret for user \"%s\", " "auth-group \"%s\", is too long; it should " "be at most 16 characters long", auth->a_user, auth->a_auth_group->ag_name); else log_warnx("mutual secret for user \"%s\", " "target \"%s\", is too long; it should " "be at most 16 characters long", auth->a_user, auth->a_auth_group->ag_target->t_name); } if (len < 12) { if (auth->a_auth_group->ag_name != NULL) log_warnx("mutual secret for user \"%s\", " "auth-group \"%s\", is too short; it " "should be at least 12 characters long", auth->a_user, auth->a_auth_group->ag_name); else log_warnx("mutual secret for user \"%s\", " "target \"%s\", is too short; it should be " "at least 16 characters long", auth->a_user, auth->a_auth_group->ag_target->t_name); } } } const struct auth * auth_new_chap(struct auth_group *ag, const char *user, const char *secret) { struct auth *auth; if (ag->ag_type == AG_TYPE_UNKNOWN) ag->ag_type = AG_TYPE_CHAP; if (ag->ag_type != AG_TYPE_CHAP) { if (ag->ag_name != NULL) log_warnx("cannot mix \"chap\" authentication with " "other types for auth-group \"%s\"", ag->ag_name); else log_warnx("cannot mix \"chap\" authentication with " "other types for target \"%s\"", ag->ag_target->t_name); return (NULL); } auth = auth_new(ag); auth->a_user = checked_strdup(user); auth->a_secret = checked_strdup(secret); auth_check_secret_length(auth); return (auth); } const struct auth * auth_new_chap_mutual(struct auth_group *ag, const char *user, const char *secret, const char *user2, const char *secret2) { struct auth *auth; if (ag->ag_type == AG_TYPE_UNKNOWN) ag->ag_type = AG_TYPE_CHAP_MUTUAL; if (ag->ag_type != AG_TYPE_CHAP_MUTUAL) { if (ag->ag_name != NULL) log_warnx("cannot mix \"chap-mutual\" authentication " "with other types for auth-group \"%s\"", ag->ag_name); else log_warnx("cannot mix \"chap-mutual\" authentication " "with other types for target \"%s\"", ag->ag_target->t_name); return (NULL); } auth = auth_new(ag); auth->a_user = checked_strdup(user); auth->a_secret = checked_strdup(secret); auth->a_mutual_user = checked_strdup(user2); auth->a_mutual_secret = checked_strdup(secret2); auth_check_secret_length(auth); return (auth); } const struct auth_name * auth_name_new(struct auth_group *ag, const char *name) { struct auth_name *an; an = calloc(1, sizeof(*an)); if (an == NULL) log_err(1, "calloc"); an->an_auth_group = ag; an->an_initator_name = checked_strdup(name); TAILQ_INSERT_TAIL(&ag->ag_names, an, an_next); return (an); } static void auth_name_delete(struct auth_name *an) { TAILQ_REMOVE(&an->an_auth_group->ag_names, an, an_next); free(an->an_initator_name); free(an); } bool auth_name_defined(const struct auth_group *ag) { if (TAILQ_EMPTY(&ag->ag_names)) return (false); return (true); } const struct auth_name * auth_name_find(const struct auth_group *ag, const char *name) { const struct auth_name *auth_name; TAILQ_FOREACH(auth_name, &ag->ag_names, an_next) { if (strcmp(auth_name->an_initator_name, name) == 0) return (auth_name); } return (NULL); } int auth_name_check(const struct auth_group *ag, const char *initiator_name) { if (!auth_name_defined(ag)) return (0); if (auth_name_find(ag, initiator_name) == NULL) return (1); return (0); } const struct auth_portal * auth_portal_new(struct auth_group *ag, const char *portal) { struct auth_portal *ap; char *net, *mask, *str, *tmp; int len, dm, m; ap = calloc(1, sizeof(*ap)); if (ap == NULL) log_err(1, "calloc"); ap->ap_auth_group = ag; ap->ap_initator_portal = checked_strdup(portal); mask = str = checked_strdup(portal); net = strsep(&mask, "/"); if (net[0] == '[') net++; len = strlen(net); if (len == 0) goto error; if (net[len - 1] == ']') net[len - 1] = 0; if (strchr(net, ':') != NULL) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ap->ap_sa; sin6->sin6_len = sizeof(*sin6); sin6->sin6_family = AF_INET6; if (inet_pton(AF_INET6, net, &sin6->sin6_addr) <= 0) goto error; dm = 128; } else { struct sockaddr_in *sin = (struct sockaddr_in *)&ap->ap_sa; sin->sin_len = sizeof(*sin); sin->sin_family = AF_INET; if (inet_pton(AF_INET, net, &sin->sin_addr) <= 0) goto error; dm = 32; } if (mask != NULL) { m = strtol(mask, &tmp, 0); if (m < 0 || m > dm || tmp[0] != 0) goto error; } else m = dm; ap->ap_mask = m; free(str); TAILQ_INSERT_TAIL(&ag->ag_portals, ap, ap_next); return (ap); error: free(ap); log_errx(1, "Incorrect initiator portal '%s'", portal); return (NULL); } static void auth_portal_delete(struct auth_portal *ap) { TAILQ_REMOVE(&ap->ap_auth_group->ag_portals, ap, ap_next); free(ap->ap_initator_portal); free(ap); } bool auth_portal_defined(const struct auth_group *ag) { if (TAILQ_EMPTY(&ag->ag_portals)) return (false); return (true); } const struct auth_portal * auth_portal_find(const struct auth_group *ag, const struct sockaddr_storage *ss) { const struct auth_portal *ap; const uint8_t *a, *b; int i; uint8_t bmask; TAILQ_FOREACH(ap, &ag->ag_portals, ap_next) { if (ap->ap_sa.ss_family != ss->ss_family) continue; if (ss->ss_family == AF_INET) { a = (const uint8_t *) &((const struct sockaddr_in *)ss)->sin_addr; b = (const uint8_t *) &((const struct sockaddr_in *)&ap->ap_sa)->sin_addr; } else { a = (const uint8_t *) &((const struct sockaddr_in6 *)ss)->sin6_addr; b = (const uint8_t *) &((const struct sockaddr_in6 *)&ap->ap_sa)->sin6_addr; } for (i = 0; i < ap->ap_mask / 8; i++) { if (a[i] != b[i]) goto next; } if (ap->ap_mask % 8) { bmask = 0xff << (8 - (ap->ap_mask % 8)); if ((a[i] & bmask) != (b[i] & bmask)) goto next; } return (ap); next: ; } return (NULL); } int auth_portal_check(const struct auth_group *ag, const struct sockaddr_storage *sa) { if (!auth_portal_defined(ag)) return (0); if (auth_portal_find(ag, sa) == NULL) return (1); return (0); } struct auth_group * auth_group_new(struct conf *conf, const char *name) { struct auth_group *ag; if (name != NULL) { ag = auth_group_find(conf, name); if (ag != NULL) { log_warnx("duplicated auth-group \"%s\"", name); return (NULL); } } ag = calloc(1, sizeof(*ag)); if (ag == NULL) log_err(1, "calloc"); if (name != NULL) ag->ag_name = checked_strdup(name); TAILQ_INIT(&ag->ag_auths); TAILQ_INIT(&ag->ag_names); TAILQ_INIT(&ag->ag_portals); ag->ag_conf = conf; TAILQ_INSERT_TAIL(&conf->conf_auth_groups, ag, ag_next); return (ag); } void auth_group_delete(struct auth_group *ag) { struct auth *auth, *auth_tmp; struct auth_name *auth_name, *auth_name_tmp; struct auth_portal *auth_portal, *auth_portal_tmp; TAILQ_REMOVE(&ag->ag_conf->conf_auth_groups, ag, ag_next); TAILQ_FOREACH_SAFE(auth, &ag->ag_auths, a_next, auth_tmp) auth_delete(auth); TAILQ_FOREACH_SAFE(auth_name, &ag->ag_names, an_next, auth_name_tmp) auth_name_delete(auth_name); TAILQ_FOREACH_SAFE(auth_portal, &ag->ag_portals, ap_next, auth_portal_tmp) auth_portal_delete(auth_portal); free(ag->ag_name); free(ag); } struct auth_group * auth_group_find(const struct conf *conf, const char *name) { struct auth_group *ag; TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { if (ag->ag_name != NULL && strcmp(ag->ag_name, name) == 0) return (ag); } return (NULL); } int auth_group_set_type(struct auth_group *ag, const char *str) { int type; if (strcmp(str, "none") == 0) { type = AG_TYPE_NO_AUTHENTICATION; } else if (strcmp(str, "deny") == 0) { type = AG_TYPE_DENY; } else if (strcmp(str, "chap") == 0) { type = AG_TYPE_CHAP; } else if (strcmp(str, "chap-mutual") == 0) { type = AG_TYPE_CHAP_MUTUAL; } else { if (ag->ag_name != NULL) log_warnx("invalid auth-type \"%s\" for auth-group " "\"%s\"", str, ag->ag_name); else log_warnx("invalid auth-type \"%s\" for target " "\"%s\"", str, ag->ag_target->t_name); return (1); } if (ag->ag_type != AG_TYPE_UNKNOWN && ag->ag_type != type) { if (ag->ag_name != NULL) { log_warnx("cannot set auth-type to \"%s\" for " "auth-group \"%s\"; already has a different " "type", str, ag->ag_name); } else { log_warnx("cannot set auth-type to \"%s\" for target " "\"%s\"; already has a different type", str, ag->ag_target->t_name); } return (1); } ag->ag_type = type; return (0); } static struct portal * portal_new(struct portal_group *pg) { struct portal *portal; portal = calloc(1, sizeof(*portal)); if (portal == NULL) log_err(1, "calloc"); TAILQ_INIT(&portal->p_targets); portal->p_portal_group = pg; TAILQ_INSERT_TAIL(&pg->pg_portals, portal, p_next); return (portal); } static void portal_delete(struct portal *portal) { TAILQ_REMOVE(&portal->p_portal_group->pg_portals, portal, p_next); if (portal->p_ai != NULL) freeaddrinfo(portal->p_ai); free(portal->p_listen); free(portal); } struct portal_group * portal_group_new(struct conf *conf, const char *name) { struct portal_group *pg; pg = portal_group_find(conf, name); if (pg != NULL) { log_warnx("duplicated portal-group \"%s\"", name); return (NULL); } pg = calloc(1, sizeof(*pg)); if (pg == NULL) log_err(1, "calloc"); pg->pg_name = checked_strdup(name); TAILQ_INIT(&pg->pg_portals); TAILQ_INIT(&pg->pg_ports); pg->pg_conf = conf; pg->pg_tag = 0; /* Assigned later in conf_apply(). */ TAILQ_INSERT_TAIL(&conf->conf_portal_groups, pg, pg_next); return (pg); } void portal_group_delete(struct portal_group *pg) { struct portal *portal, *tmp; struct port *port, *tport; TAILQ_FOREACH_SAFE(port, &pg->pg_ports, p_pgs, tport) port_delete(port); TAILQ_REMOVE(&pg->pg_conf->conf_portal_groups, pg, pg_next); TAILQ_FOREACH_SAFE(portal, &pg->pg_portals, p_next, tmp) portal_delete(portal); free(pg->pg_name); free(pg->pg_offload); free(pg->pg_redirection); free(pg); } struct portal_group * portal_group_find(const struct conf *conf, const char *name) { struct portal_group *pg; TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { if (strcmp(pg->pg_name, name) == 0) return (pg); } return (NULL); } static int parse_addr_port(char *arg, const char *def_port, struct addrinfo **ai) { struct addrinfo hints; char *str, *addr, *ch; const char *port; int error, colons = 0; str = arg = strdup(arg); if (arg[0] == '[') { /* * IPv6 address in square brackets, perhaps with port. */ arg++; addr = strsep(&arg, "]"); if (arg == NULL) return (1); if (arg[0] == '\0') { port = def_port; } else if (arg[0] == ':') { port = arg + 1; } else { free(str); return (1); } } else { /* * Either IPv6 address without brackets - and without * a port - or IPv4 address. Just count the colons. */ for (ch = arg; *ch != '\0'; ch++) { if (*ch == ':') colons++; } if (colons > 1) { addr = arg; port = def_port; } else { addr = strsep(&arg, ":"); if (arg == NULL) port = def_port; else port = arg; } } memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; error = getaddrinfo(addr, port, &hints, ai); free(str); return ((error != 0) ? 1 : 0); } int portal_group_add_listen(struct portal_group *pg, const char *value, bool iser) { struct portal *portal; portal = portal_new(pg); portal->p_listen = checked_strdup(value); portal->p_iser = iser; if (parse_addr_port(portal->p_listen, "3260", &portal->p_ai)) { log_warnx("invalid listen address %s", portal->p_listen); portal_delete(portal); return (1); } /* * XXX: getaddrinfo(3) may return multiple addresses; we should turn * those into multiple portals. */ return (0); } int isns_new(struct conf *conf, const char *addr) { struct isns *isns; isns = calloc(1, sizeof(*isns)); if (isns == NULL) log_err(1, "calloc"); isns->i_conf = conf; TAILQ_INSERT_TAIL(&conf->conf_isns, isns, i_next); isns->i_addr = checked_strdup(addr); if (parse_addr_port(isns->i_addr, "3205", &isns->i_ai)) { log_warnx("invalid iSNS address %s", isns->i_addr); isns_delete(isns); return (1); } /* * XXX: getaddrinfo(3) may return multiple addresses; we should turn * those into multiple servers. */ return (0); } void isns_delete(struct isns *isns) { TAILQ_REMOVE(&isns->i_conf->conf_isns, isns, i_next); free(isns->i_addr); if (isns->i_ai != NULL) freeaddrinfo(isns->i_ai); free(isns); } static int isns_do_connect(struct isns *isns) { int s; s = socket(isns->i_ai->ai_family, isns->i_ai->ai_socktype, isns->i_ai->ai_protocol); if (s < 0) { log_warn("socket(2) failed for %s", isns->i_addr); return (-1); } if (connect(s, isns->i_ai->ai_addr, isns->i_ai->ai_addrlen)) { log_warn("connect(2) failed for %s", isns->i_addr); close(s); return (-1); } return(s); } static int isns_do_register(struct isns *isns, int s, const char *hostname) { struct conf *conf = isns->i_conf; struct target *target; struct portal *portal; struct portal_group *pg; struct port *port; struct isns_req *req; int res = 0; uint32_t error; req = isns_req_create(ISNS_FUNC_DEVATTRREG, ISNS_FLAG_CLIENT); isns_req_add_str(req, 32, TAILQ_FIRST(&conf->conf_targets)->t_name); isns_req_add_delim(req); isns_req_add_str(req, 1, hostname); isns_req_add_32(req, 2, 2); /* 2 -- iSCSI */ isns_req_add_32(req, 6, conf->conf_isns_period); TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { if (pg->pg_unassigned) continue; TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { isns_req_add_addr(req, 16, portal->p_ai); isns_req_add_port(req, 17, portal->p_ai); } } TAILQ_FOREACH(target, &conf->conf_targets, t_next) { isns_req_add_str(req, 32, target->t_name); isns_req_add_32(req, 33, 1); /* 1 -- Target*/ if (target->t_alias != NULL) isns_req_add_str(req, 34, target->t_alias); TAILQ_FOREACH(port, &target->t_ports, p_ts) { if ((pg = port->p_portal_group) == NULL) continue; isns_req_add_32(req, 51, pg->pg_tag); TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { isns_req_add_addr(req, 49, portal->p_ai); isns_req_add_port(req, 50, portal->p_ai); } } } res = isns_req_send(s, req); if (res < 0) { log_warn("send(2) failed for %s", isns->i_addr); goto quit; } res = isns_req_receive(s, req); if (res < 0) { log_warn("receive(2) failed for %s", isns->i_addr); goto quit; } error = isns_req_get_status(req); if (error != 0) { log_warnx("iSNS register error %d for %s", error, isns->i_addr); res = -1; } quit: isns_req_free(req); return (res); } static int isns_do_check(struct isns *isns, int s, const char *hostname) { struct conf *conf = isns->i_conf; struct isns_req *req; int res = 0; uint32_t error; req = isns_req_create(ISNS_FUNC_DEVATTRQRY, ISNS_FLAG_CLIENT); isns_req_add_str(req, 32, TAILQ_FIRST(&conf->conf_targets)->t_name); isns_req_add_str(req, 1, hostname); isns_req_add_delim(req); isns_req_add(req, 2, 0, NULL); res = isns_req_send(s, req); if (res < 0) { log_warn("send(2) failed for %s", isns->i_addr); goto quit; } res = isns_req_receive(s, req); if (res < 0) { log_warn("receive(2) failed for %s", isns->i_addr); goto quit; } error = isns_req_get_status(req); if (error != 0) { log_warnx("iSNS check error %d for %s", error, isns->i_addr); res = -1; } quit: isns_req_free(req); return (res); } static int isns_do_deregister(struct isns *isns, int s, const char *hostname) { struct conf *conf = isns->i_conf; struct isns_req *req; int res = 0; uint32_t error; req = isns_req_create(ISNS_FUNC_DEVDEREG, ISNS_FLAG_CLIENT); isns_req_add_str(req, 32, TAILQ_FIRST(&conf->conf_targets)->t_name); isns_req_add_delim(req); isns_req_add_str(req, 1, hostname); res = isns_req_send(s, req); if (res < 0) { log_warn("send(2) failed for %s", isns->i_addr); goto quit; } res = isns_req_receive(s, req); if (res < 0) { log_warn("receive(2) failed for %s", isns->i_addr); goto quit; } error = isns_req_get_status(req); if (error != 0) { log_warnx("iSNS deregister error %d for %s", error, isns->i_addr); res = -1; } quit: isns_req_free(req); return (res); } void isns_register(struct isns *isns, struct isns *oldisns) { struct conf *conf = isns->i_conf; int s; char hostname[256]; if (TAILQ_EMPTY(&conf->conf_targets) || TAILQ_EMPTY(&conf->conf_portal_groups)) return; set_timeout(conf->conf_isns_timeout, false); s = isns_do_connect(isns); if (s < 0) { set_timeout(0, false); return; } gethostname(hostname, sizeof(hostname)); if (oldisns == NULL || TAILQ_EMPTY(&oldisns->i_conf->conf_targets)) oldisns = isns; isns_do_deregister(oldisns, s, hostname); isns_do_register(isns, s, hostname); close(s); set_timeout(0, false); } void isns_check(struct isns *isns) { struct conf *conf = isns->i_conf; int s, res; char hostname[256]; if (TAILQ_EMPTY(&conf->conf_targets) || TAILQ_EMPTY(&conf->conf_portal_groups)) return; set_timeout(conf->conf_isns_timeout, false); s = isns_do_connect(isns); if (s < 0) { set_timeout(0, false); return; } gethostname(hostname, sizeof(hostname)); res = isns_do_check(isns, s, hostname); if (res < 0) { isns_do_deregister(isns, s, hostname); isns_do_register(isns, s, hostname); } close(s); set_timeout(0, false); } void isns_deregister(struct isns *isns) { struct conf *conf = isns->i_conf; int s; char hostname[256]; if (TAILQ_EMPTY(&conf->conf_targets) || TAILQ_EMPTY(&conf->conf_portal_groups)) return; set_timeout(conf->conf_isns_timeout, false); s = isns_do_connect(isns); if (s < 0) return; gethostname(hostname, sizeof(hostname)); isns_do_deregister(isns, s, hostname); close(s); set_timeout(0, false); } int portal_group_set_filter(struct portal_group *pg, const char *str) { int filter; if (strcmp(str, "none") == 0) { filter = PG_FILTER_NONE; } else if (strcmp(str, "portal") == 0) { filter = PG_FILTER_PORTAL; } else if (strcmp(str, "portal-name") == 0) { filter = PG_FILTER_PORTAL_NAME; } else if (strcmp(str, "portal-name-auth") == 0) { filter = PG_FILTER_PORTAL_NAME_AUTH; } else { log_warnx("invalid discovery-filter \"%s\" for portal-group " "\"%s\"; valid values are \"none\", \"portal\", " "\"portal-name\", and \"portal-name-auth\"", str, pg->pg_name); return (1); } if (pg->pg_discovery_filter != PG_FILTER_UNKNOWN && pg->pg_discovery_filter != filter) { log_warnx("cannot set discovery-filter to \"%s\" for " "portal-group \"%s\"; already has a different " "value", str, pg->pg_name); return (1); } pg->pg_discovery_filter = filter; return (0); } int portal_group_set_offload(struct portal_group *pg, const char *offload) { if (pg->pg_offload != NULL) { log_warnx("cannot set offload to \"%s\" for " "portal-group \"%s\"; already defined", offload, pg->pg_name); return (1); } pg->pg_offload = checked_strdup(offload); return (0); } int portal_group_set_redirection(struct portal_group *pg, const char *addr) { if (pg->pg_redirection != NULL) { log_warnx("cannot set redirection to \"%s\" for " "portal-group \"%s\"; already defined", addr, pg->pg_name); return (1); } pg->pg_redirection = checked_strdup(addr); return (0); } static bool valid_hex(const char ch) { switch (ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'a': case 'A': case 'b': case 'B': case 'c': case 'C': case 'd': case 'D': case 'e': case 'E': case 'f': case 'F': return (true); default: return (false); } } bool valid_iscsi_name(const char *name) { int i; if (strlen(name) >= MAX_NAME_LEN) { log_warnx("overlong name for target \"%s\"; max length allowed " "by iSCSI specification is %d characters", name, MAX_NAME_LEN); return (false); } /* * In the cases below, we don't return an error, just in case the admin * was right, and we're wrong. */ if (strncasecmp(name, "iqn.", strlen("iqn.")) == 0) { for (i = strlen("iqn."); name[i] != '\0'; i++) { /* * XXX: We should verify UTF-8 normalisation, as defined * by 3.2.6.2: iSCSI Name Encoding. */ if (isalnum(name[i])) continue; if (name[i] == '-' || name[i] == '.' || name[i] == ':') continue; log_warnx("invalid character \"%c\" in target name " "\"%s\"; allowed characters are letters, digits, " "'-', '.', and ':'", name[i], name); break; } /* * XXX: Check more stuff: valid date and a valid reversed domain. */ } else if (strncasecmp(name, "eui.", strlen("eui.")) == 0) { if (strlen(name) != strlen("eui.") + 16) log_warnx("invalid target name \"%s\"; the \"eui.\" " "should be followed by exactly 16 hexadecimal " "digits", name); for (i = strlen("eui."); name[i] != '\0'; i++) { if (!valid_hex(name[i])) { log_warnx("invalid character \"%c\" in target " "name \"%s\"; allowed characters are 1-9 " "and A-F", name[i], name); break; } } } else if (strncasecmp(name, "naa.", strlen("naa.")) == 0) { if (strlen(name) > strlen("naa.") + 32) log_warnx("invalid target name \"%s\"; the \"naa.\" " "should be followed by at most 32 hexadecimal " "digits", name); for (i = strlen("naa."); name[i] != '\0'; i++) { if (!valid_hex(name[i])) { log_warnx("invalid character \"%c\" in target " "name \"%s\"; allowed characters are 1-9 " "and A-F", name[i], name); break; } } } else { log_warnx("invalid target name \"%s\"; should start with " "either \"iqn.\", \"eui.\", or \"naa.\"", name); } return (true); } struct pport * pport_new(struct conf *conf, const char *name, uint32_t ctl_port) { struct pport *pp; pp = calloc(1, sizeof(*pp)); if (pp == NULL) log_err(1, "calloc"); pp->pp_conf = conf; pp->pp_name = checked_strdup(name); pp->pp_ctl_port = ctl_port; TAILQ_INIT(&pp->pp_ports); TAILQ_INSERT_TAIL(&conf->conf_pports, pp, pp_next); return (pp); } struct pport * pport_find(const struct conf *conf, const char *name) { struct pport *pp; TAILQ_FOREACH(pp, &conf->conf_pports, pp_next) { if (strcasecmp(pp->pp_name, name) == 0) return (pp); } return (NULL); } struct pport * pport_copy(struct pport *pp, struct conf *conf) { struct pport *ppnew; ppnew = pport_new(conf, pp->pp_name, pp->pp_ctl_port); return (ppnew); } void pport_delete(struct pport *pp) { struct port *port, *tport; TAILQ_FOREACH_SAFE(port, &pp->pp_ports, p_ts, tport) port_delete(port); TAILQ_REMOVE(&pp->pp_conf->conf_pports, pp, pp_next); free(pp->pp_name); free(pp); } struct port * port_new(struct conf *conf, struct target *target, struct portal_group *pg) { struct port *port; char *name; int ret; ret = asprintf(&name, "%s-%s", pg->pg_name, target->t_name); if (ret <= 0) log_err(1, "asprintf"); if (port_find(conf, name) != NULL) { log_warnx("duplicate port \"%s\"", name); free(name); return (NULL); } port = calloc(1, sizeof(*port)); if (port == NULL) log_err(1, "calloc"); port->p_conf = conf; port->p_name = name; TAILQ_INSERT_TAIL(&conf->conf_ports, port, p_next); TAILQ_INSERT_TAIL(&target->t_ports, port, p_ts); port->p_target = target; TAILQ_INSERT_TAIL(&pg->pg_ports, port, p_pgs); port->p_portal_group = pg; port->p_foreign = pg->pg_foreign; return (port); } struct port * port_new_pp(struct conf *conf, struct target *target, struct pport *pp) { struct port *port; char *name; int ret; ret = asprintf(&name, "%s-%s", pp->pp_name, target->t_name); if (ret <= 0) log_err(1, "asprintf"); if (port_find(conf, name) != NULL) { log_warnx("duplicate port \"%s\"", name); free(name); return (NULL); } port = calloc(1, sizeof(*port)); if (port == NULL) log_err(1, "calloc"); port->p_conf = conf; port->p_name = name; TAILQ_INSERT_TAIL(&conf->conf_ports, port, p_next); TAILQ_INSERT_TAIL(&target->t_ports, port, p_ts); port->p_target = target; TAILQ_INSERT_TAIL(&pp->pp_ports, port, p_pps); port->p_pport = pp; return (port); } struct port * port_find(const struct conf *conf, const char *name) { struct port *port; TAILQ_FOREACH(port, &conf->conf_ports, p_next) { if (strcasecmp(port->p_name, name) == 0) return (port); } return (NULL); } struct port * port_find_in_pg(const struct portal_group *pg, const char *target) { struct port *port; TAILQ_FOREACH(port, &pg->pg_ports, p_pgs) { if (strcasecmp(port->p_target->t_name, target) == 0) return (port); } return (NULL); } void port_delete(struct port *port) { if (port->p_portal_group) TAILQ_REMOVE(&port->p_portal_group->pg_ports, port, p_pgs); if (port->p_pport) TAILQ_REMOVE(&port->p_pport->pp_ports, port, p_pps); if (port->p_target) TAILQ_REMOVE(&port->p_target->t_ports, port, p_ts); TAILQ_REMOVE(&port->p_conf->conf_ports, port, p_next); free(port->p_name); free(port); } struct target * target_new(struct conf *conf, const char *name) { struct target *targ; int i, len; targ = target_find(conf, name); if (targ != NULL) { log_warnx("duplicated target \"%s\"", name); return (NULL); } if (valid_iscsi_name(name) == false) { log_warnx("target name \"%s\" is invalid", name); return (NULL); } targ = calloc(1, sizeof(*targ)); if (targ == NULL) log_err(1, "calloc"); targ->t_name = checked_strdup(name); /* * RFC 3722 requires us to normalize the name to lowercase. */ len = strlen(name); for (i = 0; i < len; i++) targ->t_name[i] = tolower(targ->t_name[i]); targ->t_conf = conf; TAILQ_INIT(&targ->t_ports); TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next); return (targ); } void target_delete(struct target *targ) { struct port *port, *tport; TAILQ_FOREACH_SAFE(port, &targ->t_ports, p_ts, tport) port_delete(port); TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next); free(targ->t_name); free(targ->t_redirection); free(targ); } struct target * target_find(struct conf *conf, const char *name) { struct target *targ; TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { if (strcasecmp(targ->t_name, name) == 0) return (targ); } return (NULL); } int target_set_redirection(struct target *target, const char *addr) { if (target->t_redirection != NULL) { log_warnx("cannot set redirection to \"%s\" for " "target \"%s\"; already defined", addr, target->t_name); return (1); } target->t_redirection = checked_strdup(addr); return (0); } struct lun * lun_new(struct conf *conf, const char *name) { struct lun *lun; lun = lun_find(conf, name); if (lun != NULL) { log_warnx("duplicated lun \"%s\"", name); return (NULL); } lun = calloc(1, sizeof(*lun)); if (lun == NULL) log_err(1, "calloc"); lun->l_conf = conf; lun->l_name = checked_strdup(name); TAILQ_INIT(&lun->l_options); TAILQ_INSERT_TAIL(&conf->conf_luns, lun, l_next); lun->l_ctl_lun = -1; return (lun); } void lun_delete(struct lun *lun) { struct target *targ; struct lun_option *lo, *tmp; int i; TAILQ_FOREACH(targ, &lun->l_conf->conf_targets, t_next) { for (i = 0; i < MAX_LUNS; i++) { if (targ->t_luns[i] == lun) targ->t_luns[i] = NULL; } } TAILQ_REMOVE(&lun->l_conf->conf_luns, lun, l_next); TAILQ_FOREACH_SAFE(lo, &lun->l_options, lo_next, tmp) lun_option_delete(lo); free(lun->l_name); free(lun->l_backend); free(lun->l_device_id); free(lun->l_path); free(lun->l_scsiname); free(lun->l_serial); free(lun); } struct lun * lun_find(const struct conf *conf, const char *name) { struct lun *lun; TAILQ_FOREACH(lun, &conf->conf_luns, l_next) { if (strcmp(lun->l_name, name) == 0) return (lun); } return (NULL); } void lun_set_backend(struct lun *lun, const char *value) { free(lun->l_backend); lun->l_backend = checked_strdup(value); } void lun_set_blocksize(struct lun *lun, size_t value) { lun->l_blocksize = value; } void lun_set_device_type(struct lun *lun, uint8_t value) { lun->l_device_type = value; } void lun_set_device_id(struct lun *lun, const char *value) { free(lun->l_device_id); lun->l_device_id = checked_strdup(value); } void lun_set_path(struct lun *lun, const char *value) { free(lun->l_path); lun->l_path = checked_strdup(value); } void lun_set_scsiname(struct lun *lun, const char *value) { free(lun->l_scsiname); lun->l_scsiname = checked_strdup(value); } void lun_set_serial(struct lun *lun, const char *value) { free(lun->l_serial); lun->l_serial = checked_strdup(value); } void lun_set_size(struct lun *lun, size_t value) { lun->l_size = value; } void lun_set_ctl_lun(struct lun *lun, uint32_t value) { lun->l_ctl_lun = value; } struct lun_option * lun_option_new(struct lun *lun, const char *name, const char *value) { struct lun_option *lo; lo = lun_option_find(lun, name); if (lo != NULL) { log_warnx("duplicated lun option \"%s\" for lun \"%s\"", name, lun->l_name); return (NULL); } lo = calloc(1, sizeof(*lo)); if (lo == NULL) log_err(1, "calloc"); lo->lo_name = checked_strdup(name); lo->lo_value = checked_strdup(value); lo->lo_lun = lun; TAILQ_INSERT_TAIL(&lun->l_options, lo, lo_next); return (lo); } void lun_option_delete(struct lun_option *lo) { TAILQ_REMOVE(&lo->lo_lun->l_options, lo, lo_next); free(lo->lo_name); free(lo->lo_value); free(lo); } struct lun_option * lun_option_find(const struct lun *lun, const char *name) { struct lun_option *lo; TAILQ_FOREACH(lo, &lun->l_options, lo_next) { if (strcmp(lo->lo_name, name) == 0) return (lo); } return (NULL); } void lun_option_set(struct lun_option *lo, const char *value) { free(lo->lo_value); lo->lo_value = checked_strdup(value); } static struct connection * connection_new(struct portal *portal, int fd, const char *host, const struct sockaddr *client_sa) { struct connection *conn; conn = calloc(1, sizeof(*conn)); if (conn == NULL) log_err(1, "calloc"); conn->conn_portal = portal; conn->conn_socket = fd; conn->conn_initiator_addr = checked_strdup(host); memcpy(&conn->conn_initiator_sa, client_sa, client_sa->sa_len); /* * Default values, from RFC 3720, section 12. */ conn->conn_max_data_segment_length = 8192; conn->conn_max_burst_length = 262144; conn->conn_immediate_data = true; return (conn); } #if 0 static void conf_print(struct conf *conf) { struct auth_group *ag; struct auth *auth; struct auth_name *auth_name; struct auth_portal *auth_portal; struct portal_group *pg; struct portal *portal; struct target *targ; struct lun *lun; struct lun_option *lo; TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { fprintf(stderr, "auth-group %s {\n", ag->ag_name); TAILQ_FOREACH(auth, &ag->ag_auths, a_next) fprintf(stderr, "\t chap-mutual %s %s %s %s\n", auth->a_user, auth->a_secret, auth->a_mutual_user, auth->a_mutual_secret); TAILQ_FOREACH(auth_name, &ag->ag_names, an_next) fprintf(stderr, "\t initiator-name %s\n", auth_name->an_initator_name); TAILQ_FOREACH(auth_portal, &ag->ag_portals, an_next) fprintf(stderr, "\t initiator-portal %s\n", auth_portal->an_initator_portal); fprintf(stderr, "}\n"); } TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { fprintf(stderr, "portal-group %s {\n", pg->pg_name); TAILQ_FOREACH(portal, &pg->pg_portals, p_next) fprintf(stderr, "\t listen %s\n", portal->p_listen); fprintf(stderr, "}\n"); } TAILQ_FOREACH(lun, &conf->conf_luns, l_next) { fprintf(stderr, "\tlun %s {\n", lun->l_name); fprintf(stderr, "\t\tpath %s\n", lun->l_path); TAILQ_FOREACH(lo, &lun->l_options, lo_next) fprintf(stderr, "\t\toption %s %s\n", lo->lo_name, lo->lo_value); fprintf(stderr, "\t}\n"); } TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { fprintf(stderr, "target %s {\n", targ->t_name); if (targ->t_alias != NULL) fprintf(stderr, "\t alias %s\n", targ->t_alias); fprintf(stderr, "}\n"); } } #endif static int conf_verify_lun(struct lun *lun) { const struct lun *lun2; if (lun->l_backend == NULL) lun_set_backend(lun, "block"); if (strcmp(lun->l_backend, "block") == 0) { if (lun->l_path == NULL) { log_warnx("missing path for lun \"%s\"", lun->l_name); return (1); } } else if (strcmp(lun->l_backend, "ramdisk") == 0) { if (lun->l_size == 0) { log_warnx("missing size for ramdisk-backed lun \"%s\"", lun->l_name); return (1); } if (lun->l_path != NULL) { log_warnx("path must not be specified " "for ramdisk-backed lun \"%s\"", lun->l_name); return (1); } } if (lun->l_blocksize == 0) { if (lun->l_device_type == 5) lun_set_blocksize(lun, DEFAULT_CD_BLOCKSIZE); else lun_set_blocksize(lun, DEFAULT_BLOCKSIZE); } else if (lun->l_blocksize < 0) { log_warnx("invalid blocksize for lun \"%s\"; " "must be larger than 0", lun->l_name); return (1); } if (lun->l_size != 0 && lun->l_size % lun->l_blocksize != 0) { log_warnx("invalid size for lun \"%s\"; " "must be multiple of blocksize", lun->l_name); return (1); } TAILQ_FOREACH(lun2, &lun->l_conf->conf_luns, l_next) { if (lun == lun2) continue; if (lun->l_path != NULL && lun2->l_path != NULL && strcmp(lun->l_path, lun2->l_path) == 0) { log_debugx("WARNING: path \"%s\" duplicated " "between lun \"%s\", and " "lun \"%s\"", lun->l_path, lun->l_name, lun2->l_name); } } return (0); } int conf_verify(struct conf *conf) { struct auth_group *ag; struct portal_group *pg; struct port *port; struct target *targ; struct lun *lun; bool found; int error, i; if (conf->conf_pidfile_path == NULL) conf->conf_pidfile_path = checked_strdup(DEFAULT_PIDFILE); TAILQ_FOREACH(lun, &conf->conf_luns, l_next) { error = conf_verify_lun(lun); if (error != 0) return (error); } TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { if (targ->t_auth_group == NULL) { targ->t_auth_group = auth_group_find(conf, "default"); assert(targ->t_auth_group != NULL); } if (TAILQ_EMPTY(&targ->t_ports)) { pg = portal_group_find(conf, "default"); assert(pg != NULL); port_new(conf, targ, pg); } found = false; for (i = 0; i < MAX_LUNS; i++) { if (targ->t_luns[i] != NULL) found = true; } if (!found && targ->t_redirection == NULL) { log_warnx("no LUNs defined for target \"%s\"", targ->t_name); } if (found && targ->t_redirection != NULL) { log_debugx("target \"%s\" contains luns, " " but configured for redirection", targ->t_name); } } TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { assert(pg->pg_name != NULL); if (pg->pg_discovery_auth_group == NULL) { pg->pg_discovery_auth_group = auth_group_find(conf, "default"); assert(pg->pg_discovery_auth_group != NULL); } if (pg->pg_discovery_filter == PG_FILTER_UNKNOWN) pg->pg_discovery_filter = PG_FILTER_NONE; if (pg->pg_redirection != NULL) { if (!TAILQ_EMPTY(&pg->pg_ports)) { log_debugx("portal-group \"%s\" assigned " "to target, but configured " "for redirection", pg->pg_name); } pg->pg_unassigned = false; } else if (!TAILQ_EMPTY(&pg->pg_ports)) { pg->pg_unassigned = false; } else { if (strcmp(pg->pg_name, "default") != 0) log_warnx("portal-group \"%s\" not assigned " "to any target", pg->pg_name); pg->pg_unassigned = true; } } TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { if (ag->ag_name == NULL) assert(ag->ag_target != NULL); else assert(ag->ag_target == NULL); found = false; TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { if (targ->t_auth_group == ag) { found = true; break; } } TAILQ_FOREACH(port, &conf->conf_ports, p_next) { if (port->p_auth_group == ag) { found = true; break; } } TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { if (pg->pg_discovery_auth_group == ag) { found = true; break; } } if (!found && ag->ag_name != NULL && strcmp(ag->ag_name, "default") != 0 && strcmp(ag->ag_name, "no-authentication") != 0 && strcmp(ag->ag_name, "no-access") != 0) { log_warnx("auth-group \"%s\" not assigned " "to any target", ag->ag_name); } } return (0); } static int conf_apply(struct conf *oldconf, struct conf *newconf) { struct lun *oldlun, *newlun, *tmplun; struct portal_group *oldpg, *newpg; struct portal *oldp, *newp; struct port *oldport, *newport, *tmpport; struct isns *oldns, *newns; pid_t otherpid; int changed, cumulated_error = 0, error, sockbuf; int one = 1; if (oldconf->conf_debug != newconf->conf_debug) { log_debugx("changing debug level to %d", newconf->conf_debug); log_init(newconf->conf_debug); } if (oldconf->conf_pidfh != NULL) { assert(oldconf->conf_pidfile_path != NULL); if (newconf->conf_pidfile_path != NULL && strcmp(oldconf->conf_pidfile_path, newconf->conf_pidfile_path) == 0) { newconf->conf_pidfh = oldconf->conf_pidfh; oldconf->conf_pidfh = NULL; } else { log_debugx("removing pidfile %s", oldconf->conf_pidfile_path); pidfile_remove(oldconf->conf_pidfh); oldconf->conf_pidfh = NULL; } } if (newconf->conf_pidfh == NULL && newconf->conf_pidfile_path != NULL) { log_debugx("opening pidfile %s", newconf->conf_pidfile_path); newconf->conf_pidfh = pidfile_open(newconf->conf_pidfile_path, 0600, &otherpid); if (newconf->conf_pidfh == NULL) { if (errno == EEXIST) log_errx(1, "daemon already running, pid: %jd.", (intmax_t)otherpid); log_err(1, "cannot open or create pidfile \"%s\"", newconf->conf_pidfile_path); } } /* * Go through the new portal groups, assigning tags or preserving old. */ TAILQ_FOREACH(newpg, &newconf->conf_portal_groups, pg_next) { if (newpg->pg_tag != 0) continue; oldpg = portal_group_find(oldconf, newpg->pg_name); if (oldpg != NULL) newpg->pg_tag = oldpg->pg_tag; else newpg->pg_tag = ++last_portal_group_tag; } /* Deregister on removed iSNS servers. */ TAILQ_FOREACH(oldns, &oldconf->conf_isns, i_next) { TAILQ_FOREACH(newns, &newconf->conf_isns, i_next) { if (strcmp(oldns->i_addr, newns->i_addr) == 0) break; } if (newns == NULL) isns_deregister(oldns); } /* * XXX: If target or lun removal fails, we should somehow "move" * the old lun or target into newconf, so that subsequent * conf_apply() would try to remove them again. That would * be somewhat hairy, though, and lun deletion failures don't * really happen, so leave it as it is for now. */ /* * First, remove any ports present in the old configuration * and missing in the new one. */ TAILQ_FOREACH_SAFE(oldport, &oldconf->conf_ports, p_next, tmpport) { if (oldport->p_foreign) continue; newport = port_find(newconf, oldport->p_name); if (newport != NULL && !newport->p_foreign) continue; log_debugx("removing port \"%s\"", oldport->p_name); error = kernel_port_remove(oldport); if (error != 0) { log_warnx("failed to remove port %s", oldport->p_name); /* * XXX: Uncomment after fixing the root cause. * * cumulated_error++; */ } } /* * Second, remove any LUNs present in the old configuration * and missing in the new one. */ TAILQ_FOREACH_SAFE(oldlun, &oldconf->conf_luns, l_next, tmplun) { newlun = lun_find(newconf, oldlun->l_name); if (newlun == NULL) { log_debugx("lun \"%s\", CTL lun %d " "not found in new configuration; " "removing", oldlun->l_name, oldlun->l_ctl_lun); error = kernel_lun_remove(oldlun); if (error != 0) { log_warnx("failed to remove lun \"%s\", " "CTL lun %d", oldlun->l_name, oldlun->l_ctl_lun); cumulated_error++; } continue; } /* * Also remove the LUNs changed by more than size. */ changed = 0; assert(oldlun->l_backend != NULL); assert(newlun->l_backend != NULL); if (strcmp(newlun->l_backend, oldlun->l_backend) != 0) { log_debugx("backend for lun \"%s\", " "CTL lun %d changed; removing", oldlun->l_name, oldlun->l_ctl_lun); changed = 1; } if (oldlun->l_blocksize != newlun->l_blocksize) { log_debugx("blocksize for lun \"%s\", " "CTL lun %d changed; removing", oldlun->l_name, oldlun->l_ctl_lun); changed = 1; } if (newlun->l_device_id != NULL && (oldlun->l_device_id == NULL || strcmp(oldlun->l_device_id, newlun->l_device_id) != 0)) { log_debugx("device-id for lun \"%s\", " "CTL lun %d changed; removing", oldlun->l_name, oldlun->l_ctl_lun); changed = 1; } if (newlun->l_path != NULL && (oldlun->l_path == NULL || strcmp(oldlun->l_path, newlun->l_path) != 0)) { log_debugx("path for lun \"%s\", " "CTL lun %d, changed; removing", oldlun->l_name, oldlun->l_ctl_lun); changed = 1; } if (newlun->l_serial != NULL && (oldlun->l_serial == NULL || strcmp(oldlun->l_serial, newlun->l_serial) != 0)) { log_debugx("serial for lun \"%s\", " "CTL lun %d changed; removing", oldlun->l_name, oldlun->l_ctl_lun); changed = 1; } if (changed) { error = kernel_lun_remove(oldlun); if (error != 0) { log_warnx("failed to remove lun \"%s\", " "CTL lun %d", oldlun->l_name, oldlun->l_ctl_lun); cumulated_error++; } lun_delete(oldlun); continue; } lun_set_ctl_lun(newlun, oldlun->l_ctl_lun); } TAILQ_FOREACH_SAFE(newlun, &newconf->conf_luns, l_next, tmplun) { oldlun = lun_find(oldconf, newlun->l_name); if (oldlun != NULL) { log_debugx("modifying lun \"%s\", CTL lun %d", newlun->l_name, newlun->l_ctl_lun); error = kernel_lun_modify(newlun); if (error != 0) { log_warnx("failed to " "modify lun \"%s\", CTL lun %d", newlun->l_name, newlun->l_ctl_lun); cumulated_error++; } continue; } log_debugx("adding lun \"%s\"", newlun->l_name); error = kernel_lun_add(newlun); if (error != 0) { log_warnx("failed to add lun \"%s\"", newlun->l_name); lun_delete(newlun); cumulated_error++; } } /* * Now add new ports or modify existing ones. */ TAILQ_FOREACH(newport, &newconf->conf_ports, p_next) { if (newport->p_foreign) continue; oldport = port_find(oldconf, newport->p_name); if (oldport == NULL || oldport->p_foreign) { log_debugx("adding port \"%s\"", newport->p_name); error = kernel_port_add(newport); } else { log_debugx("updating port \"%s\"", newport->p_name); newport->p_ctl_port = oldport->p_ctl_port; error = kernel_port_update(newport, oldport); } if (error != 0) { log_warnx("failed to %s port %s", (oldport == NULL) ? "add" : "update", newport->p_name); /* * XXX: Uncomment after fixing the root cause. * * cumulated_error++; */ } } /* - * Go through the new portals, opening the sockets as neccessary. + * Go through the new portals, opening the sockets as necessary. */ TAILQ_FOREACH(newpg, &newconf->conf_portal_groups, pg_next) { if (newpg->pg_foreign) continue; if (newpg->pg_unassigned) { log_debugx("not listening on portal-group \"%s\", " "not assigned to any target", newpg->pg_name); continue; } TAILQ_FOREACH(newp, &newpg->pg_portals, p_next) { /* * Try to find already open portal and reuse * the listening socket. We don't care about * what portal or portal group that was, what * matters is the listening address. */ TAILQ_FOREACH(oldpg, &oldconf->conf_portal_groups, pg_next) { TAILQ_FOREACH(oldp, &oldpg->pg_portals, p_next) { if (strcmp(newp->p_listen, oldp->p_listen) == 0 && oldp->p_socket > 0) { newp->p_socket = oldp->p_socket; oldp->p_socket = 0; break; } } } if (newp->p_socket > 0) { /* * We're done with this portal. */ continue; } #ifdef ICL_KERNEL_PROXY if (proxy_mode) { newpg->pg_conf->conf_portal_id++; newp->p_id = newpg->pg_conf->conf_portal_id; log_debugx("listening on %s, portal-group " "\"%s\", portal id %d, using ICL proxy", newp->p_listen, newpg->pg_name, newp->p_id); kernel_listen(newp->p_ai, newp->p_iser, newp->p_id); continue; } #endif assert(proxy_mode == false); assert(newp->p_iser == false); log_debugx("listening on %s, portal-group \"%s\"", newp->p_listen, newpg->pg_name); newp->p_socket = socket(newp->p_ai->ai_family, newp->p_ai->ai_socktype, newp->p_ai->ai_protocol); if (newp->p_socket < 0) { log_warn("socket(2) failed for %s", newp->p_listen); cumulated_error++; continue; } sockbuf = SOCKBUF_SIZE; if (setsockopt(newp->p_socket, SOL_SOCKET, SO_RCVBUF, &sockbuf, sizeof(sockbuf)) == -1) log_warn("setsockopt(SO_RCVBUF) failed " "for %s", newp->p_listen); sockbuf = SOCKBUF_SIZE; if (setsockopt(newp->p_socket, SOL_SOCKET, SO_SNDBUF, &sockbuf, sizeof(sockbuf)) == -1) log_warn("setsockopt(SO_SNDBUF) failed " "for %s", newp->p_listen); error = setsockopt(newp->p_socket, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); if (error != 0) { log_warn("setsockopt(SO_REUSEADDR) failed " "for %s", newp->p_listen); close(newp->p_socket); newp->p_socket = 0; cumulated_error++; continue; } error = bind(newp->p_socket, newp->p_ai->ai_addr, newp->p_ai->ai_addrlen); if (error != 0) { log_warn("bind(2) failed for %s", newp->p_listen); close(newp->p_socket); newp->p_socket = 0; cumulated_error++; continue; } error = listen(newp->p_socket, -1); if (error != 0) { log_warn("listen(2) failed for %s", newp->p_listen); close(newp->p_socket); newp->p_socket = 0; cumulated_error++; continue; } } } /* * Go through the no longer used sockets, closing them. */ TAILQ_FOREACH(oldpg, &oldconf->conf_portal_groups, pg_next) { TAILQ_FOREACH(oldp, &oldpg->pg_portals, p_next) { if (oldp->p_socket <= 0) continue; log_debugx("closing socket for %s, portal-group \"%s\"", oldp->p_listen, oldpg->pg_name); close(oldp->p_socket); oldp->p_socket = 0; } } /* (Re-)Register on remaining/new iSNS servers. */ TAILQ_FOREACH(newns, &newconf->conf_isns, i_next) { TAILQ_FOREACH(oldns, &oldconf->conf_isns, i_next) { if (strcmp(oldns->i_addr, newns->i_addr) == 0) break; } isns_register(newns, oldns); } /* Schedule iSNS update */ if (!TAILQ_EMPTY(&newconf->conf_isns)) set_timeout((newconf->conf_isns_period + 2) / 3, false); return (cumulated_error); } bool timed_out(void) { return (sigalrm_received); } static void sigalrm_handler_fatal(int dummy __unused) { /* * It would be easiest to just log an error and exit. We can't * do this, though, because log_errx() is not signal safe, since * it calls syslog(3). Instead, set a flag checked by pdu_send() * and pdu_receive(), to call log_errx() there. Should they fail * to notice, we'll exit here one second later. */ if (sigalrm_received) { /* * Oh well. Just give up and quit. */ _exit(2); } sigalrm_received = true; } static void sigalrm_handler(int dummy __unused) { sigalrm_received = true; } void set_timeout(int timeout, int fatal) { struct sigaction sa; struct itimerval itv; int error; if (timeout <= 0) { log_debugx("session timeout disabled"); bzero(&itv, sizeof(itv)); error = setitimer(ITIMER_REAL, &itv, NULL); if (error != 0) log_err(1, "setitimer"); sigalrm_received = false; return; } sigalrm_received = false; bzero(&sa, sizeof(sa)); if (fatal) sa.sa_handler = sigalrm_handler_fatal; else sa.sa_handler = sigalrm_handler; sigfillset(&sa.sa_mask); error = sigaction(SIGALRM, &sa, NULL); if (error != 0) log_err(1, "sigaction"); /* * First SIGALRM will arive after conf_timeout seconds. * If we do nothing, another one will arrive a second later. */ log_debugx("setting session timeout to %d seconds", timeout); bzero(&itv, sizeof(itv)); itv.it_interval.tv_sec = 1; itv.it_value.tv_sec = timeout; error = setitimer(ITIMER_REAL, &itv, NULL); if (error != 0) log_err(1, "setitimer"); } static int wait_for_children(bool block) { pid_t pid; int status; int num = 0; for (;;) { /* * If "block" is true, wait for at least one process. */ if (block && num == 0) pid = wait4(-1, &status, 0, NULL); else pid = wait4(-1, &status, WNOHANG, NULL); if (pid <= 0) break; if (WIFSIGNALED(status)) { log_warnx("child process %d terminated with signal %d", pid, WTERMSIG(status)); } else if (WEXITSTATUS(status) != 0) { log_warnx("child process %d terminated with exit status %d", pid, WEXITSTATUS(status)); } else { log_debugx("child process %d terminated gracefully", pid); } num++; } return (num); } static void handle_connection(struct portal *portal, int fd, const struct sockaddr *client_sa, bool dont_fork) { struct connection *conn; int error; pid_t pid; char host[NI_MAXHOST + 1]; struct conf *conf; conf = portal->p_portal_group->pg_conf; if (dont_fork) { log_debugx("incoming connection; not forking due to -d flag"); } else { nchildren -= wait_for_children(false); assert(nchildren >= 0); while (conf->conf_maxproc > 0 && nchildren >= conf->conf_maxproc) { log_debugx("maxproc limit of %d child processes hit; " "waiting for child process to exit", conf->conf_maxproc); nchildren -= wait_for_children(true); assert(nchildren >= 0); } log_debugx("incoming connection; forking child process #%d", nchildren); nchildren++; pid = fork(); if (pid < 0) log_err(1, "fork"); if (pid > 0) { close(fd); return; } } pidfile_close(conf->conf_pidfh); error = getnameinfo(client_sa, client_sa->sa_len, host, sizeof(host), NULL, 0, NI_NUMERICHOST); if (error != 0) log_errx(1, "getnameinfo: %s", gai_strerror(error)); log_debugx("accepted connection from %s; portal group \"%s\"", host, portal->p_portal_group->pg_name); log_set_peer_addr(host); setproctitle("%s", host); conn = connection_new(portal, fd, host, client_sa); set_timeout(conf->conf_timeout, true); kernel_capsicate(); login(conn); if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { kernel_handoff(conn); log_debugx("connection handed off to the kernel"); } else { assert(conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY); discovery(conn); } log_debugx("nothing more to do; exiting"); exit(0); } static int fd_add(int fd, fd_set *fdset, int nfds) { /* * Skip sockets which we failed to bind. */ if (fd <= 0) return (nfds); FD_SET(fd, fdset); if (fd > nfds) nfds = fd; return (nfds); } static void main_loop(struct conf *conf, bool dont_fork) { struct portal_group *pg; struct portal *portal; struct sockaddr_storage client_sa; socklen_t client_salen; #ifdef ICL_KERNEL_PROXY int connection_id; int portal_id; #endif fd_set fdset; int error, nfds, client_fd; pidfile_write(conf->conf_pidfh); for (;;) { if (sighup_received || sigterm_received || timed_out()) return; #ifdef ICL_KERNEL_PROXY if (proxy_mode) { client_salen = sizeof(client_sa); kernel_accept(&connection_id, &portal_id, (struct sockaddr *)&client_sa, &client_salen); assert(client_salen >= client_sa.ss_len); log_debugx("incoming connection, id %d, portal id %d", connection_id, portal_id); TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { if (portal->p_id == portal_id) { goto found; } } } log_errx(1, "kernel returned invalid portal_id %d", portal_id); found: handle_connection(portal, connection_id, (struct sockaddr *)&client_sa, dont_fork); } else { #endif assert(proxy_mode == false); FD_ZERO(&fdset); nfds = 0; TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { TAILQ_FOREACH(portal, &pg->pg_portals, p_next) nfds = fd_add(portal->p_socket, &fdset, nfds); } error = select(nfds + 1, &fdset, NULL, NULL, NULL); if (error <= 0) { if (errno == EINTR) return; log_err(1, "select"); } TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { if (!FD_ISSET(portal->p_socket, &fdset)) continue; client_salen = sizeof(client_sa); client_fd = accept(portal->p_socket, (struct sockaddr *)&client_sa, &client_salen); if (client_fd < 0) { if (errno == ECONNABORTED) continue; log_err(1, "accept"); } assert(client_salen >= client_sa.ss_len); handle_connection(portal, client_fd, (struct sockaddr *)&client_sa, dont_fork); break; } } #ifdef ICL_KERNEL_PROXY } #endif } } static void sighup_handler(int dummy __unused) { sighup_received = true; } static void sigterm_handler(int dummy __unused) { sigterm_received = true; } static void sigchld_handler(int dummy __unused) { /* * The only purpose of this handler is to make SIGCHLD * interrupt the ISCSIDWAIT ioctl(2), so we can call * wait_for_children(). */ } static void register_signals(void) { struct sigaction sa; int error; bzero(&sa, sizeof(sa)); sa.sa_handler = sighup_handler; sigfillset(&sa.sa_mask); error = sigaction(SIGHUP, &sa, NULL); if (error != 0) log_err(1, "sigaction"); sa.sa_handler = sigterm_handler; error = sigaction(SIGTERM, &sa, NULL); if (error != 0) log_err(1, "sigaction"); sa.sa_handler = sigterm_handler; error = sigaction(SIGINT, &sa, NULL); if (error != 0) log_err(1, "sigaction"); sa.sa_handler = sigchld_handler; error = sigaction(SIGCHLD, &sa, NULL); if (error != 0) log_err(1, "sigaction"); } int main(int argc, char **argv) { struct conf *oldconf, *newconf, *tmpconf; struct isns *newns; const char *config_path = DEFAULT_CONFIG_PATH; int debug = 0, ch, error; bool dont_daemonize = false; while ((ch = getopt(argc, argv, "df:R")) != -1) { switch (ch) { case 'd': dont_daemonize = true; debug++; break; case 'f': config_path = optarg; break; case 'R': #ifndef ICL_KERNEL_PROXY log_errx(1, "ctld(8) compiled without ICL_KERNEL_PROXY " "does not support iSER protocol"); #endif proxy_mode = true; break; case '?': default: usage(); } } argc -= optind; if (argc != 0) usage(); log_init(debug); kernel_init(); oldconf = conf_new_from_kernel(); newconf = conf_new_from_file(config_path, oldconf); if (newconf == NULL) log_errx(1, "configuration error; exiting"); if (debug > 0) { oldconf->conf_debug = debug; newconf->conf_debug = debug; } error = conf_apply(oldconf, newconf); if (error != 0) log_errx(1, "failed to apply configuration; exiting"); conf_delete(oldconf); oldconf = NULL; register_signals(); if (dont_daemonize == false) { log_debugx("daemonizing"); if (daemon(0, 0) == -1) { log_warn("cannot daemonize"); pidfile_remove(newconf->conf_pidfh); exit(1); } } /* Schedule iSNS update */ if (!TAILQ_EMPTY(&newconf->conf_isns)) set_timeout((newconf->conf_isns_period + 2) / 3, false); for (;;) { main_loop(newconf, dont_daemonize); if (sighup_received) { sighup_received = false; log_debugx("received SIGHUP, reloading configuration"); tmpconf = conf_new_from_file(config_path, newconf); if (tmpconf == NULL) { log_warnx("configuration error, " "continuing with old configuration"); } else { if (debug > 0) tmpconf->conf_debug = debug; oldconf = newconf; newconf = tmpconf; error = conf_apply(oldconf, newconf); if (error != 0) log_warnx("failed to reload " "configuration"); conf_delete(oldconf); oldconf = NULL; } } else if (sigterm_received) { log_debugx("exiting on signal; " "reloading empty configuration"); log_debugx("removing CTL iSCSI ports " "and terminating all connections"); oldconf = newconf; newconf = conf_new(); if (debug > 0) newconf->conf_debug = debug; error = conf_apply(oldconf, newconf); if (error != 0) log_warnx("failed to apply configuration"); conf_delete(oldconf); oldconf = NULL; log_warnx("exiting on signal"); exit(0); } else { nchildren -= wait_for_children(false); assert(nchildren >= 0); if (timed_out()) { set_timeout(0, false); TAILQ_FOREACH(newns, &newconf->conf_isns, i_next) isns_check(newns); /* Schedule iSNS update */ if (!TAILQ_EMPTY(&newconf->conf_isns)) { set_timeout((newconf->conf_isns_period + 2) / 3, false); } } } } /* NOTREACHED */ } Index: head/usr.sbin/fwcontrol/fwmpegts.c =================================================================== --- head/usr.sbin/fwcontrol/fwmpegts.c (revision 289676) +++ head/usr.sbin/fwcontrol/fwmpegts.c (revision 289677) @@ -1,274 +1,274 @@ /* * Copyright (C) 2005 * Petr Holub, 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. * * $FreeBSD$ */ #include #include #include #include #include #if __FreeBSD_version >= 500000 #include #endif #include #include #include #include #include #include #include #include #if defined(__FreeBSD__) #include #include #elif defined(__NetBSD__) #include #include #else #warning "You need to add support for your OS" #endif #include "fwmethods.h" #define DEBUG 0 /***************************************************************************** MPEG-2 Transport Stream (MPEG TS) packet format according to IEC 61883: 31 15 0 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -------- | len |tag| channel | tcode | sy | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1394 | header_CRC | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -------- |0|0| sid | dbs |fn | qpc |S|RSV| dbc | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ CIP |1|0| fmt | fdf | fdf/syt | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -------- | reserved | cycle_count | cycle_offset | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | N x . . MPEG . MPEG TS payload 188 bytes . . . | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -------- | data_CRC | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ N.b. that CRCs are removed by firewire layer! The following fields are fixed for IEEE-1394: tag = 01b tcode = 1010b The length is payload length, i.e. includes CIP header and data size. The following fields are constant for MPEG TS: sph = 1 (denoted as S in CIP header above) dbs = 6 fmt = (1<<5) fdf = reserved In the supported streams we also require qpc = 0 fn = 3 and thus the payload is divided in 8 blocks as follows: +-----+-----+-----+-----+-----+-----+-----+-----+ | db0 | db1 | db2 | db3 | db4 | db5 | db6 | db7 | +-----+-----+-----+-----+-----+-----+-----+-----+ We have several cases of payload distribution based on stream bandwidth (R): 1) R < 1.5 Mbps: any of db0..db7 may be payload, 2) 1.5 < R < 3 Mbps: db0/db1 or db2/db3 or db4/db5 or db6/db7 is payload, 3) 3 < R < 6 Mbps: db0/db1/db2/db3 or db4/db5/db6/db7 is payload, 4) R > 6 Mbps: all db0..db7 contain the payload. -Curently, only case (4) is supported in fwmpegts.c +Currently, only case (4) is supported in fwmpegts.c Each packet may contain N MPEG TS data blocks with timestamp header, which are (4+188)B long. Experimentally, the N ranges from 0 through 3. *****************************************************************************/ typedef uint8_t mpeg_ts_pld[188]; struct mpeg_pldt { #if BYTE_ORDER == BIG_ENDIAN uint32_t :7, c_count:13, c_offset:12; #else /* BYTE_ORDER != BIG_ENDIAN */ uint32_t c_offset:12, c_count:13, :7; #endif /* BYTE_ORDER == BIG_ENDIAN */ mpeg_ts_pld payload; }; #define NCHUNK 8 #define PSIZE 596 #define NPACKET_R 4096 #define RBUFSIZE (PSIZE * NPACKET_R) void mpegtsrecv(int d, const char *filename, char ich, int count) { struct ciphdr *ciph; struct fw_isochreq isoreq; struct fw_isobufreq bufreq; struct fw_pkt *pkt; struct mpeg_pldt *pld; uint32_t *ptr; int fd, k, len, m, pkt_size, startwr, tlen; char *buf; startwr = 0; if (strcmp(filename, "-") == 0) fd = STDOUT_FILENO; else { fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0660); if (fd == -1) err(EX_NOINPUT, "%s", filename); } buf = malloc(RBUFSIZE); bufreq.rx.nchunk = NCHUNK; bufreq.rx.npacket = NPACKET_R; bufreq.rx.psize = PSIZE; bufreq.tx.nchunk = 0; bufreq.tx.npacket = 0; bufreq.tx.psize = 0; if (ioctl(d, FW_SSTBUF, &bufreq) < 0) err(1, "ioctl"); isoreq.ch = ich & 0x3f; isoreq.tag = (ich >> 6) & 3; if (ioctl(d, FW_SRSTREAM, &isoreq) < 0) err(1, "ioctl"); k = m = 0; while (count <= 0 || k <= count) { len = tlen = read(d, buf, RBUFSIZE); #if DEBUG fprintf(stderr, "Read %d bytes.\n", len); #endif /* DEBUG */ if (len < 0) { if (errno == EAGAIN) { fprintf(stderr, "(EAGAIN) - push 'Play'?\n"); continue; } err(1, "read failed"); } ptr = (uint32_t *) buf; do { pkt = (struct fw_pkt *) ptr; #if DEBUG fprintf(stderr, "\nReading new packet.\n"); fprintf(stderr, "%08x %08x %08x %08x\n", htonl(ptr[0]), htonl(ptr[1]), htonl(ptr[2]), htonl(ptr[3])); #endif /* DEBUG */ /* there is no CRC in the 1394 header */ ciph = (struct ciphdr *)(ptr + 1); /* skip iso header */ if (ciph->fmt != CIP_FMT_MPEG) errx(1, "unknown format 0x%x", ciph->fmt); if (ciph->fn != 3) { errx(1, "unsupported MPEG TS stream, fn=%d (only fn=3 is supported)", ciph->fn); } ptr = (uint32_t *) (ciph + 1); /* skip cip header */ if (pkt->mode.stream.len <= sizeof(struct ciphdr)) { /* no payload */ /* tlen needs to be decremented before end of the loop */ goto next; } #if DEBUG else { fprintf(stderr, "Packet net payload length (IEEE1394 header): %d\n", pkt->mode.stream.len - sizeof(struct ciphdr)); fprintf(stderr, "Data block size (CIP header): %d [q], %d [B]\n", ciph->len, ciph->len * 4); fprintf(stderr, "Data fraction number (CIP header): %d => DBC increments with %d\n", ciph->fn, (1<fn) ); fprintf(stderr, "QCP (CIP header): %d\n", ciph->qpc ); fprintf(stderr, "DBC counter (CIP header): %d\n", ciph->dbc ); fprintf(stderr, "MPEG payload type size: %d\n", sizeof(struct mpeg_pldt)); } #endif /* DEBUG */ /* This is a condition that needs to be satisfied to start writing the data */ if (ciph->dbc % (1<fn) == 0) startwr = 1; /* Read out all the MPEG TS data blocks from current packet */ for (pld = (struct mpeg_pldt *)ptr; (intptr_t)pld < (intptr_t)((char *)ptr + pkt->mode.stream.len - sizeof(struct ciphdr)); pld++) { if (startwr == 1) write(fd, pld->payload, sizeof(pld->payload)); } next: /* CRCs are removed from both header and trailer so that only 4 bytes of 1394 header remains */ pkt_size = pkt->mode.stream.len + 4; ptr = (uint32_t *)((intptr_t)pkt + pkt_size); tlen -= pkt_size; } while (tlen > 0); #if DEBUG fprintf(stderr, "\nReading a data from firewire.\n"); #endif /* DEBUG */ } if (fd != STDOUT_FILENO) close(fd); fprintf(stderr, "\n"); } Index: head/usr.sbin/jail/command.c =================================================================== --- head/usr.sbin/jail/command.c (revision 289676) +++ head/usr.sbin/jail/command.c (revision 289677) @@ -1,979 +1,979 @@ /*- * Copyright (c) 2011 James Gritton * 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 #include #include #include #include #include #include #include #include #include "jailp.h" #define DEFAULT_STOP_TIMEOUT 10 #define PHASH_SIZE 256 LIST_HEAD(phhead, phash); struct phash { LIST_ENTRY(phash) le; struct cfjail *j; pid_t pid; }; int paralimit = -1; extern char **environ; static int run_command(struct cfjail *j); static int add_proc(struct cfjail *j, pid_t pid); static void clear_procs(struct cfjail *j); static struct cfjail *find_proc(pid_t pid); static int term_procs(struct cfjail *j); static int get_user_info(struct cfjail *j, const char *username, const struct passwd **pwdp, login_cap_t **lcapp); static int check_path(struct cfjail *j, const char *pname, const char *path, int isfile, const char *umount_type); static struct cfjails sleeping = TAILQ_HEAD_INITIALIZER(sleeping); static struct cfjails runnable = TAILQ_HEAD_INITIALIZER(runnable); static struct cfstring dummystring = { .len = 1 }; static struct phhead phash[PHASH_SIZE]; static int kq; /* * Run the next command associated with a jail. */ int next_command(struct cfjail *j) { enum intparam comparam; int create_failed, stopping; if (paralimit == 0) { requeue(j, &runnable); return 1; } create_failed = (j->flags & (JF_STOP | JF_FAILED)) == JF_FAILED; stopping = (j->flags & JF_STOP) != 0; comparam = *j->comparam; for (;;) { if (j->comstring == NULL) { j->comparam += create_failed ? -1 : 1; switch ((comparam = *j->comparam)) { case IP__NULL: return 0; case IP_MOUNT_DEVFS: if (!bool_param(j->intparams[IP_MOUNT_DEVFS])) continue; j->comstring = &dummystring; break; case IP_MOUNT_FDESCFS: if (!bool_param(j->intparams[IP_MOUNT_FDESCFS])) continue; j->comstring = &dummystring; break; case IP_MOUNT_PROCFS: if (!bool_param(j->intparams[IP_MOUNT_PROCFS])) continue; j->comstring = &dummystring; break; case IP__OP: case IP_STOP_TIMEOUT: j->comstring = &dummystring; break; default: if (j->intparams[comparam] == NULL) continue; j->comstring = create_failed || (stopping && (j->intparams[comparam]->flags & PF_REV)) ? TAILQ_LAST(&j->intparams[comparam]->val, cfstrings) : TAILQ_FIRST(&j->intparams[comparam]->val); } } else { j->comstring = j->comstring == &dummystring ? NULL : create_failed || (stopping && (j->intparams[comparam]->flags & PF_REV)) ? TAILQ_PREV(j->comstring, cfstrings, tq) : TAILQ_NEXT(j->comstring, tq); } if (j->comstring == NULL || j->comstring->len == 0 || (create_failed && (comparam == IP_EXEC_PRESTART || comparam == IP_EXEC_START || comparam == IP_COMMAND || comparam == IP_EXEC_POSTSTART))) continue; switch (run_command(j)) { case -1: failed(j); /* FALLTHROUGH */ case 1: return 1; } } } /* * Check command exit status */ int finish_command(struct cfjail *j) { int error; if (!(j->flags & JF_SLEEPQ)) return 0; j->flags &= ~JF_SLEEPQ; if (*j->comparam == IP_STOP_TIMEOUT) { j->flags &= ~JF_TIMEOUT; j->pstatus = 0; return 0; } paralimit++; if (!TAILQ_EMPTY(&runnable)) requeue(TAILQ_FIRST(&runnable), &ready); error = 0; if (j->flags & JF_TIMEOUT) { j->flags &= ~JF_TIMEOUT; if (*j->comparam != IP_STOP_TIMEOUT) { jail_warnx(j, "%s: timed out", j->comline); failed(j); error = -1; } else if (verbose > 0) jail_note(j, "timed out\n"); } else if (j->pstatus != 0) { if (WIFSIGNALED(j->pstatus)) jail_warnx(j, "%s: exited on signal %d", j->comline, WTERMSIG(j->pstatus)); else jail_warnx(j, "%s: failed", j->comline); j->pstatus = 0; failed(j); error = -1; } free(j->comline); j->comline = NULL; return error; } /* * Check for finished processes or timeouts. */ struct cfjail * next_proc(int nonblock) { struct kevent ke; struct timespec ts; struct timespec *tsp; struct cfjail *j; if (!TAILQ_EMPTY(&sleeping)) { again: tsp = NULL; if ((j = TAILQ_FIRST(&sleeping)) && j->timeout.tv_sec) { clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec = j->timeout.tv_sec - ts.tv_sec; ts.tv_nsec = j->timeout.tv_nsec - ts.tv_nsec; if (ts.tv_nsec < 0) { ts.tv_sec--; ts.tv_nsec += 1000000000; } if (ts.tv_sec < 0 || (ts.tv_sec == 0 && ts.tv_nsec == 0)) { j->flags |= JF_TIMEOUT; clear_procs(j); return j; } tsp = &ts; } if (nonblock) { ts.tv_sec = 0; ts.tv_nsec = 0; tsp = &ts; } switch (kevent(kq, NULL, 0, &ke, 1, tsp)) { case -1: if (errno != EINTR) err(1, "kevent"); goto again; case 0: if (!nonblock) { j = TAILQ_FIRST(&sleeping); j->flags |= JF_TIMEOUT; clear_procs(j); return j; } break; case 1: (void)waitpid(ke.ident, NULL, WNOHANG); if ((j = find_proc(ke.ident))) { j->pstatus = ke.data; return j; } goto again; } } return NULL; } /* * Run a single command for a jail, possible inside the jail. */ static int run_command(struct cfjail *j) { const struct passwd *pwd; const struct cfstring *comstring, *s; login_cap_t *lcap; const char **argv; char *acs, *cs, *comcs, *devpath; const char *jidstr, *conslog, *path, *ruleset, *term, *username; enum intparam comparam; size_t comlen; pid_t pid; int argc, bg, clean, consfd, down, fib, i, injail, sjuser, timeout; #if defined(INET) || defined(INET6) char *addr, *extrap, *p, *val; #endif static char *cleanenv; /* Perform some operations that aren't actually commands */ comparam = *j->comparam; down = j->flags & (JF_STOP | JF_FAILED); switch (comparam) { case IP_STOP_TIMEOUT: return term_procs(j); case IP__OP: if (down) { if (jail_remove(j->jid) < 0 && errno == EPERM) { jail_warnx(j, "jail_remove: %s", strerror(errno)); return -1; } if (verbose > 0 || (verbose == 0 && (j->flags & JF_STOP ? note_remove : j->name != NULL))) jail_note(j, "removed\n"); j->jid = -1; if (j->flags & JF_STOP) dep_done(j, DF_LIGHT); else j->flags &= ~JF_PERSIST; } else { if (create_jail(j) < 0) return -1; if (iflag) printf("%d\n", j->jid); if (verbose >= 0 && (j->name || verbose > 0)) jail_note(j, "created\n"); dep_done(j, DF_LIGHT); } return 0; default: ; } /* * Collect exec arguments. Internal commands for network and * mounting build their own argument lists. */ comstring = j->comstring; bg = 0; switch (comparam) { #ifdef INET case IP__IP4_IFADDR: argc = 0; val = alloca(strlen(comstring->s) + 1); strcpy(val, comstring->s); cs = val; extrap = NULL; while ((p = strchr(cs, ' ')) != NULL && strlen(p) > 1) { if (extrap == NULL) { *p = '\0'; extrap = p + 1; } cs = p + 1; argc++; } argv = alloca((8 + argc) * sizeof(char *)); argv[0] = _PATH_IFCONFIG; if ((cs = strchr(val, '|'))) { argv[1] = acs = alloca(cs - val + 1); strlcpy(acs, val, cs - val + 1); addr = cs + 1; } else { argv[1] = string_param(j->intparams[IP_INTERFACE]); addr = val; } argv[2] = "inet"; if (!(cs = strchr(addr, '/'))) { argv[3] = addr; argv[4] = "netmask"; argv[5] = "255.255.255.255"; argc = 6; } else if (strchr(cs + 1, '.')) { argv[3] = acs = alloca(cs - addr + 1); strlcpy(acs, addr, cs - addr + 1); argv[4] = "netmask"; argv[5] = cs + 1; argc = 6; } else { argv[3] = addr; argc = 4; } if (!down) { for (cs = strtok(extrap, " "); cs; cs = strtok(NULL, " ")) { size_t len = strlen(cs) + 1; argv[argc++] = acs = alloca(len); strlcpy(acs, cs, len); } } argv[argc] = down ? "-alias" : "alias"; argv[argc + 1] = NULL; break; #endif #ifdef INET6 case IP__IP6_IFADDR: argc = 0; val = alloca(strlen(comstring->s) + 1); strcpy(val, comstring->s); cs = val; extrap = NULL; while ((p = strchr(cs, ' ')) != NULL && strlen(p) > 1) { if (extrap == NULL) { *p = '\0'; extrap = p + 1; } cs = p + 1; argc++; } argv = alloca((8 + argc) * sizeof(char *)); argv[0] = _PATH_IFCONFIG; if ((cs = strchr(val, '|'))) { argv[1] = acs = alloca(cs - val + 1); strlcpy(acs, val, cs - val + 1); addr = cs + 1; } else { argv[1] = string_param(j->intparams[IP_INTERFACE]); addr = val; } argv[2] = "inet6"; argv[3] = addr; if (!(cs = strchr(addr, '/'))) { argv[4] = "prefixlen"; argv[5] = "128"; argc = 6; } else argc = 4; if (!down) { for (cs = strtok(extrap, " "); cs; cs = strtok(NULL, " ")) { size_t len = strlen(cs) + 1; argv[argc++] = acs = alloca(len); strlcpy(acs, cs, len); } } argv[argc] = down ? "-alias" : "alias"; argv[argc + 1] = NULL; break; #endif case IP_VNET_INTERFACE: argv = alloca(5 * sizeof(char *)); argv[0] = _PATH_IFCONFIG; argv[1] = comstring->s; argv[2] = down ? "-vnet" : "vnet"; jidstr = string_param(j->intparams[KP_JID]); argv[3] = jidstr ? jidstr : string_param(j->intparams[KP_NAME]); argv[4] = NULL; break; case IP_MOUNT: case IP__MOUNT_FROM_FSTAB: argv = alloca(8 * sizeof(char *)); comcs = alloca(comstring->len + 1); strcpy(comcs, comstring->s); argc = 0; for (cs = strtok(comcs, " \t\f\v\r\n"); cs && argc < 4; cs = strtok(NULL, " \t\f\v\r\n")) argv[argc++] = cs; if (argc == 0) return 0; if (argc < 3) { jail_warnx(j, "%s: %s: missing information", j->intparams[comparam]->name, comstring->s); return -1; } if (check_path(j, j->intparams[comparam]->name, argv[1], 0, down ? argv[2] : NULL) < 0) return -1; if (down) { argv[4] = NULL; argv[3] = argv[1]; argv[0] = "/sbin/umount"; } else { if (argc == 4) { argv[7] = NULL; argv[6] = argv[1]; argv[5] = argv[0]; argv[4] = argv[3]; argv[3] = "-o"; } else { argv[5] = NULL; argv[4] = argv[1]; argv[3] = argv[0]; } argv[0] = _PATH_MOUNT; } argv[1] = "-t"; break; case IP_MOUNT_DEVFS: argv = alloca(7 * sizeof(char *)); path = string_param(j->intparams[KP_PATH]); if (path == NULL) { jail_warnx(j, "mount.devfs: no path"); return -1; } devpath = alloca(strlen(path) + 5); sprintf(devpath, "%s/dev", path); if (check_path(j, "mount.devfs", devpath, 0, down ? "devfs" : NULL) < 0) return -1; if (down) { argv[0] = "/sbin/umount"; argv[1] = devpath; argv[2] = NULL; } else { argv[0] = _PATH_MOUNT; argv[1] = "-t"; argv[2] = "devfs"; ruleset = string_param(j->intparams[KP_DEVFS_RULESET]); if (!ruleset) ruleset = "4"; /* devfsrules_jail */ argv[3] = acs = alloca(11 + strlen(ruleset)); sprintf(acs, "-oruleset=%s", ruleset); argv[4] = "."; argv[5] = devpath; argv[6] = NULL; } break; case IP_MOUNT_FDESCFS: argv = alloca(7 * sizeof(char *)); path = string_param(j->intparams[KP_PATH]); if (path == NULL) { jail_warnx(j, "mount.fdescfs: no path"); return -1; } devpath = alloca(strlen(path) + 8); sprintf(devpath, "%s/dev/fd", path); if (check_path(j, "mount.fdescfs", devpath, 0, down ? "fdescfs" : NULL) < 0) return -1; if (down) { argv[0] = "/sbin/umount"; argv[1] = devpath; argv[2] = NULL; } else { argv[0] = _PATH_MOUNT; argv[1] = "-t"; argv[2] = "fdescfs"; argv[3] = "."; argv[4] = devpath; argv[5] = NULL; } break; case IP_MOUNT_PROCFS: argv = alloca(7 * sizeof(char *)); path = string_param(j->intparams[KP_PATH]); if (path == NULL) { jail_warnx(j, "mount.procfs: no path"); return -1; } devpath = alloca(strlen(path) + 6); sprintf(devpath, "%s/proc", path); if (check_path(j, "mount.procfs", devpath, 0, down ? "procfs" : NULL) < 0) return -1; if (down) { argv[0] = "/sbin/umount"; argv[1] = devpath; argv[2] = NULL; } else { argv[0] = _PATH_MOUNT; argv[1] = "-t"; argv[2] = "procfs"; argv[3] = "."; argv[4] = devpath; argv[5] = NULL; } break; case IP_COMMAND: if (j->name != NULL) goto default_command; argc = 0; TAILQ_FOREACH(s, &j->intparams[IP_COMMAND]->val, tq) argc++; argv = alloca((argc + 1) * sizeof(char *)); argc = 0; TAILQ_FOREACH(s, &j->intparams[IP_COMMAND]->val, tq) argv[argc++] = s->s; argv[argc] = NULL; j->comstring = &dummystring; break; default: default_command: if ((cs = strpbrk(comstring->s, "!\"$&'()*;<>?[\\]`{|}~")) && !(cs[0] == '&' && cs[1] == '\0')) { argv = alloca(4 * sizeof(char *)); argv[0] = _PATH_BSHELL; argv[1] = "-c"; argv[2] = comstring->s; argv[3] = NULL; } else { if (cs) { *cs = 0; bg = 1; } comcs = alloca(comstring->len + 1); strcpy(comcs, comstring->s); argc = 0; for (cs = strtok(comcs, " \t\f\v\r\n"); cs; cs = strtok(NULL, " \t\f\v\r\n")) argc++; argv = alloca((argc + 1) * sizeof(char *)); strcpy(comcs, comstring->s); argc = 0; for (cs = strtok(comcs, " \t\f\v\r\n"); cs; cs = strtok(NULL, " \t\f\v\r\n")) argv[argc++] = cs; argv[argc] = NULL; } } if (argv[0] == NULL) return 0; if (int_param(j->intparams[IP_EXEC_TIMEOUT], &timeout) && timeout != 0) { clock_gettime(CLOCK_REALTIME, &j->timeout); j->timeout.tv_sec += timeout; } else j->timeout.tv_sec = 0; injail = comparam == IP_EXEC_START || comparam == IP_COMMAND || comparam == IP_EXEC_STOP; clean = bool_param(j->intparams[IP_EXEC_CLEAN]); username = string_param(j->intparams[injail ? IP_EXEC_JAIL_USER : IP_EXEC_SYSTEM_USER]); sjuser = bool_param(j->intparams[IP_EXEC_SYSTEM_JAIL_USER]); consfd = 0; if (injail && (conslog = string_param(j->intparams[IP_EXEC_CONSOLELOG]))) { if (check_path(j, "exec.consolelog", conslog, 1, NULL) < 0) return -1; consfd = open(conslog, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE); if (consfd < 0) { jail_warnx(j, "open %s: %s", conslog, strerror(errno)); return -1; } } comlen = 0; for (i = 0; argv[i]; i++) comlen += strlen(argv[i]) + 1; j->comline = cs = emalloc(comlen); for (i = 0; argv[i]; i++) { strcpy(cs, argv[i]); if (argv[i + 1]) { cs += strlen(argv[i]) + 1; cs[-1] = ' '; } } if (verbose > 0) jail_note(j, "run command%s%s%s: %s\n", injail ? " in jail" : "", username ? " as " : "", username ? username : "", j->comline); pid = fork(); if (pid < 0) err(1, "fork"); if (pid > 0) { if (bg || !add_proc(j, pid)) { free(j->comline); j->comline = NULL; return 0; } else { paralimit--; return 1; } } if (bg) setsid(); /* Set up the environment and run the command */ pwd = NULL; lcap = NULL; if ((clean || username) && injail && sjuser && get_user_info(j, username, &pwd, &lcap) < 0) exit(1); if (injail) { /* jail_attach won't chdir along with its chroot. */ path = string_param(j->intparams[KP_PATH]); if (path && chdir(path) < 0) { jail_warnx(j, "chdir %s: %s", path, strerror(errno)); exit(1); } if (int_param(j->intparams[IP_EXEC_FIB], &fib) && setfib(fib) < 0) { jail_warnx(j, "setfib: %s", strerror(errno)); exit(1); } if (jail_attach(j->jid) < 0) { jail_warnx(j, "jail_attach: %s", strerror(errno)); exit(1); } } if (clean || username) { if (!(injail && sjuser) && get_user_info(j, username, &pwd, &lcap) < 0) exit(1); if (clean) { term = getenv("TERM"); environ = &cleanenv; setenv("PATH", "/bin:/usr/bin", 0); if (term != NULL) setenv("TERM", term, 1); } if (setgid(pwd->pw_gid) < 0) { jail_warnx(j, "setgid %d: %s", pwd->pw_gid, strerror(errno)); exit(1); } if (setusercontext(lcap, pwd, pwd->pw_uid, username ? LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN : LOGIN_SETPATH | LOGIN_SETENV) < 0) { jail_warnx(j, "setusercontext %s: %s", pwd->pw_name, strerror(errno)); exit(1); } login_close(lcap); setenv("USER", pwd->pw_name, 1); setenv("HOME", pwd->pw_dir, 1); setenv("SHELL", *pwd->pw_shell ? pwd->pw_shell : _PATH_BSHELL, 1); if (clean && chdir(pwd->pw_dir) < 0) { jail_warnx(j, "chdir %s: %s", pwd->pw_dir, strerror(errno)); exit(1); } endpwent(); } if (consfd != 0 && (dup2(consfd, 1) < 0 || dup2(consfd, 2) < 0)) { jail_warnx(j, "exec.consolelog: %s", strerror(errno)); exit(1); } closefrom(3); execvp(argv[0], __DECONST(char *const*, argv)); jail_warnx(j, "exec %s: %s", argv[0], strerror(errno)); exit(1); } /* * Add a process to the hash, tied to a jail. */ static int add_proc(struct cfjail *j, pid_t pid) { struct kevent ke; struct cfjail *tj; struct phash *ph; if (!kq && (kq = kqueue()) < 0) err(1, "kqueue"); EV_SET(&ke, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) { if (errno == ESRCH) return 0; err(1, "kevent"); } ph = emalloc(sizeof(struct phash)); ph->j = j; ph->pid = pid; LIST_INSERT_HEAD(&phash[pid % PHASH_SIZE], ph, le); j->nprocs++; j->flags |= JF_SLEEPQ; if (j->timeout.tv_sec == 0) requeue(j, &sleeping); else { - /* File the jail in the sleep queue acording to its timeout. */ + /* File the jail in the sleep queue according to its timeout. */ TAILQ_REMOVE(j->queue, j, tq); TAILQ_FOREACH(tj, &sleeping, tq) { if (!tj->timeout.tv_sec || j->timeout.tv_sec < tj->timeout.tv_sec || (j->timeout.tv_sec == tj->timeout.tv_sec && j->timeout.tv_nsec <= tj->timeout.tv_nsec)) { TAILQ_INSERT_BEFORE(tj, j, tq); break; } } if (tj == NULL) TAILQ_INSERT_TAIL(&sleeping, j, tq); j->queue = &sleeping; } return 1; } /* * Remove any processes from the hash that correspond to a jail. */ static void clear_procs(struct cfjail *j) { struct kevent ke; struct phash *ph, *tph; int i; j->nprocs = 0; for (i = 0; i < PHASH_SIZE; i++) LIST_FOREACH_SAFE(ph, &phash[i], le, tph) if (ph->j == j) { EV_SET(&ke, ph->pid, EVFILT_PROC, EV_DELETE, NOTE_EXIT, 0, NULL); (void)kevent(kq, &ke, 1, NULL, 0, NULL); LIST_REMOVE(ph, le); free(ph); } } /* * Find the jail that corresponds to an exited process. */ static struct cfjail * find_proc(pid_t pid) { struct cfjail *j; struct phash *ph; LIST_FOREACH(ph, &phash[pid % PHASH_SIZE], le) if (ph->pid == pid) { j = ph->j; LIST_REMOVE(ph, le); free(ph); return --j->nprocs ? NULL : j; } return NULL; } /* * Send SIGTERM to all processes in a jail and wait for them to die. */ static int term_procs(struct cfjail *j) { struct kinfo_proc *ki; int i, noted, pcnt, timeout; static kvm_t *kd; if (!int_param(j->intparams[IP_STOP_TIMEOUT], &timeout)) timeout = DEFAULT_STOP_TIMEOUT; else if (timeout == 0) return 0; if (kd == NULL) { kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL); if (kd == NULL) return 0; } ki = kvm_getprocs(kd, KERN_PROC_PROC, 0, &pcnt); if (ki == NULL) return 0; noted = 0; for (i = 0; i < pcnt; i++) if (ki[i].ki_jid == j->jid && kill(ki[i].ki_pid, SIGTERM) == 0) { (void)add_proc(j, ki[i].ki_pid); if (verbose > 0) { if (!noted) { noted = 1; jail_note(j, "sent SIGTERM to:"); } printf(" %d", ki[i].ki_pid); } } if (noted) printf("\n"); if (j->nprocs > 0) { clock_gettime(CLOCK_REALTIME, &j->timeout); j->timeout.tv_sec += timeout; return 1; } return 0; } /* * Look up a user in the passwd and login.conf files. */ static int get_user_info(struct cfjail *j, const char *username, const struct passwd **pwdp, login_cap_t **lcapp) { const struct passwd *pwd; *pwdp = pwd = username ? getpwnam(username) : getpwuid(getuid()); if (pwd == NULL) { if (errno) jail_warnx(j, "getpwnam%s%s: %s", username ? " " : "", username ? username : "", strerror(errno)); else if (username) jail_warnx(j, "%s: no such user", username); else jail_warnx(j, "unknown uid %d", getuid()); return -1; } *lcapp = login_getpwclass(pwd); if (*lcapp == NULL) { jail_warnx(j, "getpwclass %s: %s", pwd->pw_name, strerror(errno)); return -1; } /* Set the groups while the group file is still available */ if (initgroups(pwd->pw_name, pwd->pw_gid) < 0) { jail_warnx(j, "initgroups %s: %s", pwd->pw_name, strerror(errno)); return -1; } return 0; } /* * Make sure a mount or consolelog path is a valid absolute pathname * with no symlinks. */ static int check_path(struct cfjail *j, const char *pname, const char *path, int isfile, const char *umount_type) { struct stat st, mpst; struct statfs stfs; char *tpath, *p; const char *jailpath; size_t jplen; if (path[0] != '/') { jail_warnx(j, "%s: %s: not an absolute pathname", pname, path); return -1; } /* * Only check for symlinks in components below the jail's path, * since that's where the security risk lies. */ jailpath = string_param(j->intparams[KP_PATH]); if (jailpath == NULL) jailpath = ""; jplen = strlen(jailpath); if (!strncmp(path, jailpath, jplen) && path[jplen] == '/') { tpath = alloca(strlen(path) + 1); strcpy(tpath, path); for (p = tpath + jplen; p != NULL; ) { p = strchr(p + 1, '/'); if (p) *p = '\0'; if (lstat(tpath, &st) < 0) { if (errno == ENOENT && isfile && !p) break; jail_warnx(j, "%s: %s: %s", pname, tpath, strerror(errno)); return -1; } if (S_ISLNK(st.st_mode)) { jail_warnx(j, "%s: %s is a symbolic link", pname, tpath); return -1; } if (p) *p = '/'; } } if (umount_type != NULL) { if (stat(path, &st) < 0 || statfs(path, &stfs) < 0) { jail_warnx(j, "%s: %s: %s", pname, path, strerror(errno)); return -1; } if (stat(stfs.f_mntonname, &mpst) < 0) { jail_warnx(j, "%s: %s: %s", pname, stfs.f_mntonname, strerror(errno)); return -1; } if (st.st_ino != mpst.st_ino) { jail_warnx(j, "%s: %s: not a mount point", pname, path); return -1; } if (strcmp(stfs.f_fstypename, umount_type)) { jail_warnx(j, "%s: %s: not a %s mount", pname, path, umount_type); return -1; } } return 0; } Index: head/usr.sbin/jail/jailp.h =================================================================== --- head/usr.sbin/jail/jailp.h (revision 289676) +++ head/usr.sbin/jail/jailp.h (revision 289677) @@ -1,237 +1,237 @@ /*- * Copyright (c) 2011 James Gritton. * 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. * * $FreeBSD$ */ #include #include #include #include #include #include #define CONF_FILE "/etc/jail.conf" #define DEP_FROM 0 #define DEP_TO 1 #define DF_SEEN 0x01 /* Dependency has been followed */ #define DF_LIGHT 0x02 /* Implied dependency on jail existence only */ -#define DF_NOFAIL 0x04 /* Don't propigate failed jails */ +#define DF_NOFAIL 0x04 /* Don't propagate failed jails */ #define PF_VAR 0x01 /* This is a variable, not a true parameter */ #define PF_APPEND 0x02 /* Append to existing parameter list */ #define PF_BAD 0x04 /* Unable to resolve parameter value */ #define PF_INTERNAL 0x08 /* Internal parameter, not passed to kernel */ #define PF_BOOL 0x10 /* Boolean parameter */ #define PF_INT 0x20 /* Integer parameter */ #define PF_CONV 0x40 /* Parameter duplicated in converted form */ #define PF_REV 0x80 /* Run commands in reverse order on stopping */ #define PF_IMMUTABLE 0x100 /* Immutable parameter */ #define JF_START 0x0001 /* -c */ #define JF_SET 0x0002 /* -m */ #define JF_STOP 0x0004 /* -r */ #define JF_DEPEND 0x0008 /* Operation required by dependency */ #define JF_WILD 0x0010 /* Not specified on the command line */ #define JF_FAILED 0x0020 /* Operation failed */ #define JF_PARAMS 0x0040 /* Parameters checked and imported */ #define JF_RDTUN 0x0080 /* Create-only parameter check has been done */ #define JF_PERSIST 0x0100 /* Jail is temporarily persistent */ #define JF_TIMEOUT 0x0200 /* A command (or process kill) timed out */ #define JF_SLEEPQ 0x0400 /* Waiting on a command and/or timeout */ #define JF_OP_MASK (JF_START | JF_SET | JF_STOP) #define JF_RESTART (JF_START | JF_STOP) #define JF_START_SET (JF_START | JF_SET) #define JF_SET_RESTART (JF_SET | JF_STOP) #define JF_START_SET_RESTART (JF_START | JF_SET | JF_STOP) #define JF_DO_STOP(js) (((js) & (JF_SET | JF_STOP)) == JF_STOP) enum intparam { IP__NULL = 0, /* Null command */ IP_ALLOW_DYING, /* Allow making changes to a dying jail */ IP_COMMAND, /* Command run inside jail at creation */ IP_DEPEND, /* Jail starts after (stops before) another */ IP_EXEC_CLEAN, /* Run commands in a clean environment */ IP_EXEC_CONSOLELOG, /* Redirect optput for commands run in jail */ IP_EXEC_FIB, /* Run jailed commands with this FIB */ IP_EXEC_JAIL_USER, /* Run jailed commands as this user */ IP_EXEC_POSTSTART, /* Commands run outside jail after creating */ IP_EXEC_POSTSTOP, /* Commands run outside jail after removing */ IP_EXEC_PRESTART, /* Commands run outside jail before creating */ IP_EXEC_PRESTOP, /* Commands run outside jail before removing */ IP_EXEC_START, /* Commands run inside jail on creation */ IP_EXEC_STOP, /* Commands run inside jail on removal */ IP_EXEC_SYSTEM_JAIL_USER,/* Get jail_user from system passwd file */ IP_EXEC_SYSTEM_USER, /* Run non-jailed commands as this user */ IP_EXEC_TIMEOUT, /* Time to wait for a command to complete */ #if defined(INET) || defined(INET6) IP_INTERFACE, /* Add IP addresses to this interface */ IP_IP_HOSTNAME, /* Get jail IP address(es) from hostname */ #endif IP_MOUNT, /* Mount points in fstab(5) form */ IP_MOUNT_DEVFS, /* Mount /dev under prison root */ IP_MOUNT_FDESCFS, /* Mount /dev/fd under prison root */ IP_MOUNT_PROCFS, /* Mount /proc under prison root */ IP_MOUNT_FSTAB, /* A standard fstab(5) file */ IP_STOP_TIMEOUT, /* Time to wait after sending SIGTERM */ IP_VNET_INTERFACE, /* Assign interface(s) to vnet jail */ #ifdef INET IP__IP4_IFADDR, /* Copy of ip4.addr with interface/netmask */ #endif #ifdef INET6 IP__IP6_IFADDR, /* Copy of ip6.addr with interface/prefixlen */ #endif IP__MOUNT_FROM_FSTAB, /* Line from mount.fstab file */ IP__OP, /* Placeholder for requested operation */ KP_ALLOW_CHFLAGS, KP_ALLOW_MOUNT, KP_ALLOW_RAW_SOCKETS, KP_ALLOW_SET_HOSTNAME, KP_ALLOW_SOCKET_AF, KP_ALLOW_SYSVIPC, KP_DEVFS_RULESET, KP_ENFORCE_STATFS, KP_HOST_HOSTNAME, #ifdef INET KP_IP4_ADDR, #endif #ifdef INET6 KP_IP6_ADDR, #endif KP_JID, KP_NAME, KP_PATH, KP_PERSIST, KP_SECURELEVEL, KP_VNET, IP_NPARAM }; STAILQ_HEAD(cfvars, cfvar); struct cfvar { STAILQ_ENTRY(cfvar) tq; char *name; size_t pos; }; TAILQ_HEAD(cfstrings, cfstring); struct cfstring { TAILQ_ENTRY(cfstring) tq; char *s; size_t len; struct cfvars vars; }; TAILQ_HEAD(cfparams, cfparam); struct cfparam { TAILQ_ENTRY(cfparam) tq; char *name; struct cfstrings val; unsigned flags; int gen; }; TAILQ_HEAD(cfjails, cfjail); STAILQ_HEAD(cfdepends, cfdepend); struct cfjail { TAILQ_ENTRY(cfjail) tq; char *name; char *comline; struct cfparams params; struct cfdepends dep[2]; struct cfjails *queue; struct cfparam *intparams[IP_NPARAM]; struct cfstring *comstring; struct jailparam *jp; struct timespec timeout; const enum intparam *comparam; unsigned flags; int jid; int seq; int pstatus; int ndeps; int njp; int nprocs; }; struct cfdepend { STAILQ_ENTRY(cfdepend) tq[2]; struct cfjail *j[2]; unsigned flags; }; extern void *emalloc(size_t); extern void *erealloc(void *, size_t); extern char *estrdup(const char *); extern int create_jail(struct cfjail *j); extern void failed(struct cfjail *j); extern void jail_note(const struct cfjail *j, const char *fmt, ...); extern void jail_warnx(const struct cfjail *j, const char *fmt, ...); extern int next_command(struct cfjail *j); extern int finish_command(struct cfjail *j); extern struct cfjail *next_proc(int nonblock); extern void load_config(void); extern struct cfjail *add_jail(void); extern void add_param(struct cfjail *j, const struct cfparam *p, enum intparam ipnum, const char *value); extern int bool_param(const struct cfparam *p); extern int int_param(const struct cfparam *p, int *ip); extern const char *string_param(const struct cfparam *p); extern int check_intparams(struct cfjail *j); extern int import_params(struct cfjail *j); extern int equalopts(const char *opt1, const char *opt2); extern int wild_jail_name(const char *wname); extern int wild_jail_match(const char *jname, const char *wname); extern void dep_setup(int docf); extern int dep_check(struct cfjail *j); extern void dep_done(struct cfjail *j, unsigned flags); extern void dep_reset(struct cfjail *j); extern struct cfjail *next_jail(void); extern int start_state(const char *target, int docf, unsigned state, int running); extern void requeue(struct cfjail *j, struct cfjails *queue); extern void yyerror(const char *); extern int yylex(void); extern struct cfjails cfjails; extern struct cfjails ready; extern struct cfjails depend; extern const char *cfname; extern int iflag; extern int note_remove; extern int paralimit; extern int verbose; Index: head/usr.sbin/jail/jailparse.y =================================================================== --- head/usr.sbin/jail/jailparse.y (revision 289676) +++ head/usr.sbin/jail/jailparse.y (revision 289677) @@ -1,216 +1,216 @@ %{ /*- * Copyright (c) 2011 James Gritton * 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 "jailp.h" #ifdef DEBUG #define YYDEBUG 1 #endif %} %union { struct cfjail *j; struct cfparams *pp; struct cfparam *p; struct cfstrings *ss; struct cfstring *s; char *cs; } %token PLEQ %token STR STR1 VAR VAR1 %type jail %type param_l %type

param name %type value %type string %% /* * A config file is a series of jails (containing parameters) and jail-less * parameters which realy belong to a global pseudo-jail. */ conf : ; | conf jail ; | conf param ';' { struct cfjail *j; j = TAILQ_LAST(&cfjails, cfjails); if (!j || strcmp(j->name, "*")) { j = add_jail(); j->name = estrdup("*"); } TAILQ_INSERT_TAIL(&j->params, $2, tq); } | conf ';' jail : STR '{' param_l '}' { $$ = add_jail(); $$->name = $1; TAILQ_CONCAT(&$$->params, $3, tq); free($3); } ; param_l : { $$ = emalloc(sizeof(struct cfparams)); TAILQ_INIT($$); } | param_l param ';' { $$ = $1; TAILQ_INSERT_TAIL($$, $2, tq); } | param_l ';' ; /* * Parameters have a name and an optional list of value strings, - * which may have "+=" or "=" preceeding them. + * which may have "+=" or "=" preceding them. */ param : name { $$ = $1; } | name '=' value { $$ = $1; TAILQ_CONCAT(&$$->val, $3, tq); free($3); } | name PLEQ value { $$ = $1; TAILQ_CONCAT(&$$->val, $3, tq); $$->flags |= PF_APPEND; free($3); } | name value { $$ = $1; TAILQ_CONCAT(&$$->val, $2, tq); free($2); } | error { } ; /* * A parameter has a fixed name. A variable definition looks just like a * parameter except that the name is a variable. */ name : STR { $$ = emalloc(sizeof(struct cfparam)); $$->name = $1; TAILQ_INIT(&$$->val); $$->flags = 0; } | VAR { $$ = emalloc(sizeof(struct cfparam)); $$->name = $1; TAILQ_INIT(&$$->val); $$->flags = PF_VAR; } ; value : string { $$ = emalloc(sizeof(struct cfstrings)); TAILQ_INIT($$); TAILQ_INSERT_TAIL($$, $1, tq); } | value ',' string { $$ = $1; TAILQ_INSERT_TAIL($$, $3, tq); } ; /* * Strings may be passed in pieces, because of quoting and/or variable * interpolation. Reassemble them into a single string. */ string : STR { $$ = emalloc(sizeof(struct cfstring)); $$->s = $1; $$->len = strlen($1); STAILQ_INIT(&$$->vars); } | VAR { struct cfvar *v; $$ = emalloc(sizeof(struct cfstring)); $$->s = estrdup(""); $$->len = 0; STAILQ_INIT(&$$->vars); v = emalloc(sizeof(struct cfvar)); v->name = $1; v->pos = 0; STAILQ_INSERT_TAIL(&$$->vars, v, tq); } | string STR1 { size_t len1; $$ = $1; len1 = strlen($2); $$->s = erealloc($$->s, $$->len + len1 + 1); strcpy($$->s + $$->len, $2); free($2); $$->len += len1; } | string VAR1 { struct cfvar *v; $$ = $1; v = emalloc(sizeof(struct cfvar)); v->name = $2; v->pos = $$->len; STAILQ_INSERT_TAIL(&$$->vars, v, tq); } ; %% Index: head/usr.sbin/jls/jls.c =================================================================== --- head/usr.sbin/jls/jls.c (revision 289676) +++ head/usr.sbin/jls/jls.c (revision 289677) @@ -1,518 +1,518 @@ /*- * Copyright (c) 2003 Mike Barcroft * Copyright (c) 2008 Bjoern A. Zeeb * Copyright (c) 2009 James Gritton * 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 #include #include #include #define JP_USER 0x01000000 #define JP_OPT 0x02000000 #define PRINT_DEFAULT 0x01 #define PRINT_HEADER 0x02 #define PRINT_NAMEVAL 0x04 #define PRINT_QUOTED 0x08 #define PRINT_SKIP 0x10 #define PRINT_VERBOSE 0x20 #define PRINT_JAIL_NAME 0x40 static struct jailparam *params; static int *param_parent; static int nparams; #ifdef INET6 static int ip6_ok; #endif #ifdef INET static int ip4_ok; #endif static int add_param(const char *name, void *value, size_t valuelen, struct jailparam *source, unsigned flags); static int sort_param(const void *a, const void *b); static char *noname(const char *name); static char *nononame(const char *name); static int print_jail(int pflags, int jflags); static void quoted_print(char *str); int main(int argc, char **argv) { char *dot, *ep, *jname, *pname; int c, i, jflags, jid, lastjid, pflags, spc; jname = NULL; pflags = jflags = jid = 0; while ((c = getopt(argc, argv, "adj:hNnqsv")) >= 0) switch (c) { case 'a': case 'd': jflags |= JAIL_DYING; break; case 'j': jid = strtoul(optarg, &ep, 10); if (!jid || *ep) { jid = 0; jname = optarg; } break; case 'h': pflags = (pflags & ~(PRINT_SKIP | PRINT_VERBOSE)) | PRINT_HEADER; break; case 'N': pflags |= PRINT_JAIL_NAME; break; case 'n': pflags = (pflags & ~PRINT_VERBOSE) | PRINT_NAMEVAL; break; case 'q': pflags |= PRINT_QUOTED; break; case 's': pflags = (pflags & ~(PRINT_HEADER | PRINT_VERBOSE)) | PRINT_NAMEVAL | PRINT_QUOTED | PRINT_SKIP; break; case 'v': pflags = (pflags & ~(PRINT_HEADER | PRINT_NAMEVAL | PRINT_SKIP)) | PRINT_VERBOSE; break; default: errx(1, "usage: jls [-dhNnqv] [-j jail] [param ...]"); } #ifdef INET6 ip6_ok = feature_present("inet6"); #endif #ifdef INET ip4_ok = feature_present("inet"); #endif /* Add the parameters to print. */ if (optind == argc) { if (pflags & (PRINT_HEADER | PRINT_NAMEVAL)) add_param("all", NULL, (size_t)0, NULL, JP_USER); else if (pflags & PRINT_VERBOSE) { add_param("jid", NULL, (size_t)0, NULL, JP_USER); add_param("host.hostname", NULL, (size_t)0, NULL, JP_USER); add_param("path", NULL, (size_t)0, NULL, JP_USER); add_param("name", NULL, (size_t)0, NULL, JP_USER); add_param("dying", NULL, (size_t)0, NULL, JP_USER); add_param("cpuset.id", NULL, (size_t)0, NULL, JP_USER); #ifdef INET if (ip4_ok) add_param("ip4.addr", NULL, (size_t)0, NULL, JP_USER); #endif #ifdef INET6 if (ip6_ok) add_param("ip6.addr", NULL, (size_t)0, NULL, JP_USER | JP_OPT); #endif } else { pflags |= PRINT_DEFAULT; if (pflags & PRINT_JAIL_NAME) add_param("name", NULL, (size_t)0, NULL, JP_USER); else add_param("jid", NULL, (size_t)0, NULL, JP_USER); #ifdef INET if (ip4_ok) add_param("ip4.addr", NULL, (size_t)0, NULL, JP_USER); #endif add_param("host.hostname", NULL, (size_t)0, NULL, JP_USER); add_param("path", NULL, (size_t)0, NULL, JP_USER); } } else { pflags &= ~PRINT_VERBOSE; while (optind < argc) add_param(argv[optind++], NULL, (size_t)0, NULL, JP_USER); } if (pflags & PRINT_SKIP) { /* Check for parameters with jailsys parents. */ for (i = 0; i < nparams; i++) { if ((params[i].jp_flags & JP_USER) && (dot = strchr(params[i].jp_name, '.'))) { pname = alloca((dot - params[i].jp_name) + 1); strlcpy(pname, params[i].jp_name, (dot - params[i].jp_name) + 1); param_parent[i] = add_param(pname, NULL, (size_t)0, NULL, JP_OPT); } } } /* Add the index key parameters. */ if (jid != 0) add_param("jid", &jid, sizeof(jid), NULL, 0); else if (jname != NULL) add_param("name", jname, strlen(jname), NULL, 0); else add_param("lastjid", &lastjid, sizeof(lastjid), NULL, 0); /* Print a header line if requested. */ if (pflags & PRINT_VERBOSE) printf(" JID Hostname Path\n" " Name State\n" " CPUSetID\n" " IP Address(es)\n"); else if (pflags & PRINT_DEFAULT) if (pflags & PRINT_JAIL_NAME) printf(" JID IP Address " "Hostname Path\n"); else printf(" JID IP Address " "Hostname Path\n"); else if (pflags & PRINT_HEADER) { for (i = spc = 0; i < nparams; i++) if (params[i].jp_flags & JP_USER) { if (spc) putchar(' '); else spc = 1; fputs(params[i].jp_name, stdout); } putchar('\n'); } - /* Fetch the jail(s) and print the paramters. */ + /* Fetch the jail(s) and print the parameters. */ if (jid != 0 || jname != NULL) { if (print_jail(pflags, jflags) < 0) errx(1, "%s", jail_errmsg); } else { for (lastjid = 0; (lastjid = print_jail(pflags, jflags)) >= 0; ) ; if (errno != 0 && errno != ENOENT) errx(1, "%s", jail_errmsg); } return (0); } static int add_param(const char *name, void *value, size_t valuelen, struct jailparam *source, unsigned flags) { struct jailparam *param, *tparams; int i, tnparams; static int paramlistsize; /* The pseudo-parameter "all" scans the list of available parameters. */ if (!strcmp(name, "all")) { tnparams = jailparam_all(&tparams); if (tnparams < 0) errx(1, "%s", jail_errmsg); qsort(tparams, (size_t)tnparams, sizeof(struct jailparam), sort_param); for (i = 0; i < tnparams; i++) add_param(tparams[i].jp_name, NULL, (size_t)0, tparams + i, flags); free(tparams); return -1; } /* Check for repeat parameters. */ for (i = 0; i < nparams; i++) if (!strcmp(name, params[i].jp_name)) { if (value != NULL && jailparam_import_raw(params + i, value, valuelen) < 0) errx(1, "%s", jail_errmsg); params[i].jp_flags |= flags; if (source != NULL) jailparam_free(source, 1); return i; } /* Make sure there is room for the new param record. */ if (!nparams) { paramlistsize = 32; params = malloc(paramlistsize * sizeof(*params)); param_parent = malloc(paramlistsize * sizeof(*param_parent)); if (params == NULL || param_parent == NULL) err(1, "malloc"); } else if (nparams >= paramlistsize) { paramlistsize *= 2; params = realloc(params, paramlistsize * sizeof(*params)); param_parent = realloc(param_parent, paramlistsize * sizeof(*param_parent)); if (params == NULL || param_parent == NULL) err(1, "realloc"); } /* Look up the parameter. */ param_parent[nparams] = -1; param = params + nparams++; if (source != NULL) { *param = *source; param->jp_flags |= flags; return param - params; } if (jailparam_init(param, name) < 0 || (value != NULL ? jailparam_import_raw(param, value, valuelen) : jailparam_import(param, value)) < 0) { if (flags & JP_OPT) { nparams--; return (-1); } errx(1, "%s", jail_errmsg); } param->jp_flags = flags; return param - params; } static int sort_param(const void *a, const void *b) { const struct jailparam *parama, *paramb; char *ap, *bp; /* Put top-level parameters first. */ parama = a; paramb = b; ap = strchr(parama->jp_name, '.'); bp = strchr(paramb->jp_name, '.'); if (ap && !bp) return (1); if (bp && !ap) return (-1); return (strcmp(parama->jp_name, paramb->jp_name)); } static char * noname(const char *name) { char *nname, *p; nname = malloc(strlen(name) + 3); if (nname == NULL) err(1, "malloc"); p = strrchr(name, '.'); if (p != NULL) sprintf(nname, "%.*s.no%s", (int)(p - name), name, p + 1); else sprintf(nname, "no%s", name); return nname; } static char * nononame(const char *name) { char *nname, *p; p = strrchr(name, '.'); if (strncmp(p ? p + 1 : name, "no", 2)) return NULL; nname = malloc(strlen(name) - 1); if (nname == NULL) err(1, "malloc"); if (p != NULL) sprintf(nname, "%.*s.%s", (int)(p - name), name, p + 3); else strcpy(nname, name + 2); return nname; } static int print_jail(int pflags, int jflags) { char *nname; char **param_values; int i, ai, jid, count, n, spc; char ipbuf[INET6_ADDRSTRLEN]; jid = jailparam_get(params, nparams, jflags); if (jid < 0) return jid; if (pflags & PRINT_VERBOSE) { printf("%6d %-29.29s %.74s\n" "%6s %-29.29s %.74s\n" "%6s %-6d\n", *(int *)params[0].jp_value, (char *)params[1].jp_value, (char *)params[2].jp_value, "", (char *)params[3].jp_value, *(int *)params[4].jp_value ? "DYING" : "ACTIVE", "", *(int *)params[5].jp_value); n = 6; #ifdef INET if (ip4_ok && !strcmp(params[n].jp_name, "ip4.addr")) { count = params[n].jp_valuelen / sizeof(struct in_addr); for (ai = 0; ai < count; ai++) if (inet_ntop(AF_INET, &((struct in_addr *)params[n].jp_value)[ai], ipbuf, sizeof(ipbuf)) == NULL) err(1, "inet_ntop"); else printf("%6s %-15.15s\n", "", ipbuf); n++; } #endif #ifdef INET6 if (ip6_ok && !strcmp(params[n].jp_name, "ip6.addr")) { count = params[n].jp_valuelen / sizeof(struct in6_addr); for (ai = 0; ai < count; ai++) if (inet_ntop(AF_INET6, &((struct in6_addr *) params[n].jp_value)[ai], ipbuf, sizeof(ipbuf)) == NULL) err(1, "inet_ntop"); else printf("%6s %s\n", "", ipbuf); n++; } #endif } else if (pflags & PRINT_DEFAULT) { if (pflags & PRINT_JAIL_NAME) printf(" %-15s ", (char *)params[0].jp_value); else printf("%6d ", *(int *)params[0].jp_value); printf("%-15.15s %-29.29s %.74s\n", #ifdef INET (!ip4_ok || params[1].jp_valuelen == 0) ? "-" : inet_ntoa(*(struct in_addr *)params[1].jp_value), (char *)params[2-!ip4_ok].jp_value, (char *)params[3-!ip4_ok].jp_value); #else "-", (char *)params[1].jp_value, (char *)params[2].jp_value); #endif } else { param_values = alloca(nparams * sizeof(*param_values)); for (i = 0; i < nparams; i++) { if (!(params[i].jp_flags & JP_USER)) continue; param_values[i] = jailparam_export(params + i); if (param_values[i] == NULL) errx(1, "%s", jail_errmsg); } for (i = spc = 0; i < nparams; i++) { if (!(params[i].jp_flags & JP_USER)) continue; if ((pflags & PRINT_SKIP) && ((!(params[i].jp_ctltype & (CTLFLAG_WR | CTLFLAG_TUN))) || (param_parent[i] >= 0 && *(int *)params[param_parent[i]].jp_value != JAIL_SYS_NEW))) continue; if (spc) putchar(' '); else spc = 1; if (pflags & PRINT_NAMEVAL) { /* * Generally "name=value", but for booleans * either "name" or "noname". */ if (params[i].jp_flags & (JP_BOOL | JP_NOBOOL)) { if (*(int *)params[i].jp_value) printf("%s", params[i].jp_name); else { nname = (params[i].jp_flags & JP_NOBOOL) ? nononame(params[i].jp_name) : noname(params[i].jp_name); printf("%s", nname); free(nname); } continue; } printf("%s=", params[i].jp_name); } if (params[i].jp_valuelen == 0) { if (pflags & PRINT_QUOTED) printf("\"\""); else if (!(pflags & PRINT_NAMEVAL)) putchar('-'); } else quoted_print(param_values[i]); } putchar('\n'); for (i = 0; i < nparams; i++) if (params[i].jp_flags & JP_USER) free(param_values[i]); } return (jid); } static void quoted_print(char *str) { int c, qc; char *p = str; /* An empty string needs quoting. */ if (!*p) { fputs("\"\"", stdout); return; } /* * The value will be surrounded by quotes if it contains spaces * or quotes. */ qc = strchr(p, '\'') ? '"' : strchr(p, '"') ? '\'' : strchr(p, ' ') || strchr(p, '\t') ? '"' : 0; if (qc) putchar(qc); while ((c = *p++)) { if (c == '\\' || c == qc) putchar('\\'); putchar(c); } if (qc) putchar(qc); } Index: head/usr.sbin/makefs/cd9660.c =================================================================== --- head/usr.sbin/makefs/cd9660.c (revision 289676) +++ head/usr.sbin/makefs/cd9660.c (revision 289677) @@ -1,2127 +1,2127 @@ /* $NetBSD: cd9660.c,v 1.31 2011/08/06 23:25:19 christos Exp $ */ /* * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan * Perez-Rathke and Ram Vedam. All rights reserved. * * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, * Alan Perez-Rathke and Ram Vedam. * * 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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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. */ /* * Copyright (c) 2001 Wasabi Systems, Inc. * All rights reserved. * * Written by Luke Mewburn for Wasabi Systems, Inc. * * 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 for the NetBSD Project by * Wasabi Systems, Inc. * 4. The name of Wasabi Systems, Inc. may not be used to endorse * or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC * 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. */ /* * Copyright (c) 1982, 1986, 1989, 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$"); #include #include #include #include #include "makefs.h" #include "cd9660.h" #include "cd9660/iso9660_rrip.h" #include "cd9660/cd9660_archimedes.h" /* * Global variables */ iso9660_disk diskStructure; static void cd9660_finalize_PVD(void); static cd9660node *cd9660_allocate_cd9660node(void); static void cd9660_set_defaults(void); static int cd9660_arguments_set_string(const char *, const char *, int, char, char *); static void cd9660_populate_iso_dir_record( struct _iso_directory_record_cd9660 *, u_char, u_char, u_char, const char *); static void cd9660_setup_root_node(void); static int cd9660_setup_volume_descriptors(void); #if 0 static int cd9660_fill_extended_attribute_record(cd9660node *); #endif static void cd9660_sort_nodes(cd9660node *); static int cd9660_translate_node_common(cd9660node *); static int cd9660_translate_node(fsnode *, cd9660node *); static int cd9660_compare_filename(const char *, const char *); static void cd9660_sorted_child_insert(cd9660node *, cd9660node *); static int cd9660_handle_collisions(cd9660node *, int); static cd9660node *cd9660_rename_filename(cd9660node *, int, int); static void cd9660_copy_filenames(cd9660node *); static void cd9660_sorting_nodes(cd9660node *); static int cd9660_count_collisions(cd9660node *); static cd9660node *cd9660_rrip_move_directory(cd9660node *); static int cd9660_add_dot_records(cd9660node *); static void cd9660_convert_structure(fsnode *, cd9660node *, int, int *, int *); static void cd9660_free_structure(cd9660node *); static int cd9660_generate_path_table(void); static int cd9660_level1_convert_filename(const char *, char *, int); static int cd9660_level2_convert_filename(const char *, char *, int); #if 0 static int cd9660_joliet_convert_filename(const char *, char *, int); #endif static int cd9660_convert_filename(const char *, char *, int); static void cd9660_populate_dot_records(cd9660node *); static int64_t cd9660_compute_offsets(cd9660node *, int64_t); #if 0 static int cd9660_copy_stat_info(cd9660node *, cd9660node *, int); #endif static cd9660node *cd9660_create_virtual_entry(const char *, cd9660node *, int, int); static cd9660node *cd9660_create_file(const char *, cd9660node *, cd9660node *); static cd9660node *cd9660_create_directory(const char *, cd9660node *, cd9660node *); static cd9660node *cd9660_create_special_directory(u_char, cd9660node *); /* - * Allocate and initalize a cd9660node + * Allocate and initialize a cd9660node * @returns struct cd9660node * Pointer to new node, or NULL on error */ static cd9660node * cd9660_allocate_cd9660node(void) { cd9660node *temp; if ((temp = calloc(1, sizeof(cd9660node))) == NULL) err(EXIT_FAILURE, "%s: calloc", __func__); TAILQ_INIT(&temp->cn_children); temp->parent = temp->dot_record = temp->dot_dot_record = NULL; temp->ptnext = temp->ptprev = temp->ptlast = NULL; temp->node = NULL; temp->isoDirRecord = NULL; temp->isoExtAttributes = NULL; temp->rr_real_parent = temp->rr_relocated = NULL; temp->su_tail_data = NULL; return temp; } int cd9660_defaults_set = 0; /** * Set default values for cd9660 extension to makefs */ static void cd9660_set_defaults(void) { /*Fix the sector size for now, though the spec allows for other sizes*/ diskStructure.sectorSize = 2048; /* Set up defaults in our own structure */ diskStructure.verbose_level = 0; diskStructure.keep_bad_images = 0; diskStructure.follow_sym_links = 0; diskStructure.isoLevel = 2; diskStructure.rock_ridge_enabled = 0; diskStructure.rock_ridge_renamed_dir_name = 0; diskStructure.rock_ridge_move_count = 0; diskStructure.rr_moved_dir = 0; diskStructure.archimedes_enabled = 0; diskStructure.chrp_boot = 0; diskStructure.include_padding_areas = 1; /* Spec breaking functionality */ diskStructure.allow_deep_trees = diskStructure.allow_start_dot = diskStructure.allow_max_name = diskStructure.allow_illegal_chars = diskStructure.allow_lowercase = diskStructure.allow_multidot = diskStructure.omit_trailing_period = 0; /* Make sure the PVD is clear */ memset(&diskStructure.primaryDescriptor, 0, 2048); memset(diskStructure.primaryDescriptor.publisher_id, 0x20,128); memset(diskStructure.primaryDescriptor.preparer_id, 0x20,128); memset(diskStructure.primaryDescriptor.application_id, 0x20,128); memset(diskStructure.primaryDescriptor.copyright_file_id, 0x20,37); memset(diskStructure.primaryDescriptor.abstract_file_id, 0x20,37); memset(diskStructure.primaryDescriptor.bibliographic_file_id, 0x20,37); strcpy(diskStructure.primaryDescriptor.system_id, "FreeBSD"); cd9660_defaults_set = 1; /* Boot support: Initially disabled */ diskStructure.has_generic_bootimage = 0; diskStructure.generic_bootimage = NULL; diskStructure.boot_image_directory = 0; /*memset(diskStructure.boot_descriptor, 0, 2048);*/ diskStructure.is_bootable = 0; TAILQ_INIT(&diskStructure.boot_images); LIST_INIT(&diskStructure.boot_entries); } void cd9660_prep_opts(fsinfo_t *fsopts __unused) { cd9660_set_defaults(); } void cd9660_cleanup_opts(fsinfo_t *fsopts __unused) { } static int cd9660_arguments_set_string(const char *val, const char *fieldtitle, int length, char testmode, char * dest) { int len, test; if (val == NULL) warnx("error: The %s requires a string argument", fieldtitle); else if ((len = strlen(val)) <= length) { if (testmode == 'd') test = cd9660_valid_d_chars(val); else test = cd9660_valid_a_chars(val); if (test) { memcpy(dest, val, len); if (test == 2) cd9660_uppercase_characters(dest, len); return 1; } else warnx("error: The %s must be composed of " "%c-characters", fieldtitle, testmode); } else warnx("error: The %s must be at most 32 characters long", fieldtitle); return 0; } /* * Command-line parsing function */ int cd9660_parse_opts(const char *option, fsinfo_t *fsopts) { char *var, *val; int rv; /* Set up allowed options - integer options ONLY */ option_t cd9660_options[] = { { "l", &diskStructure.isoLevel, 1, 3, "ISO Level" }, { "isolevel", &diskStructure.isoLevel, 1, 3, "ISO Level" }, { "verbose", &diskStructure.verbose_level, 0, 2, "Turns on verbose output" }, { "v", &diskStructure.verbose_level, 0 , 2, "Turns on verbose output"}, { .name = NULL } }; if (cd9660_defaults_set == 0) cd9660_set_defaults(); /* * Todo : finish implementing this, and make a function that * parses them */ /* string_option_t cd9660_string_options[] = { { "L", "Label", &diskStructure.primaryDescriptor.volume_id, 1, 32, "Disk Label", ISO_STRING_FILTER_DCHARS }, { NULL } } */ assert(option != NULL); if (debug & DEBUG_FS_PARSE_OPTS) printf("cd9660_parse_opts: got `%s'\n", option); if ((var = strdup(option)) == NULL) err(1, "allocating memory for copy of option string"); rv = 1; val = strchr(var, '='); if (val != NULL) *val++ = '\0'; /* First handle options with no parameters */ if (strcmp(var, "h") == 0) { diskStructure.displayHelp = 1; rv = 1; } else if (CD9660_IS_COMMAND_ARG_DUAL(var, "S", "follow-symlinks")) { /* this is not handled yet */ diskStructure.follow_sym_links = 1; rv = 1; } else if (CD9660_IS_COMMAND_ARG_DUAL(var, "L", "label")) { rv = cd9660_arguments_set_string(val, "Disk Label", 32, 'd', diskStructure.primaryDescriptor.volume_id); } else if (CD9660_IS_COMMAND_ARG_DUAL(var, "A", "applicationid")) { rv = cd9660_arguments_set_string(val, "Application Identifier", 128, 'a', diskStructure.primaryDescriptor.application_id); } else if(CD9660_IS_COMMAND_ARG_DUAL(var, "P", "publisher")) { rv = cd9660_arguments_set_string(val, "Publisher Identifier", 128, 'a', diskStructure.primaryDescriptor.publisher_id); } else if (CD9660_IS_COMMAND_ARG_DUAL(var, "p", "preparer")) { rv = cd9660_arguments_set_string(val, "Preparer Identifier", 128, 'a', diskStructure.primaryDescriptor.preparer_id); } else if (CD9660_IS_COMMAND_ARG_DUAL(var, "V", "volumeid")) { rv = cd9660_arguments_set_string(val, "Volume Set Identifier", 128, 'a', diskStructure.primaryDescriptor.volume_set_id); /* Boot options */ } else if (CD9660_IS_COMMAND_ARG_DUAL(var, "B", "bootimage")) { if (val == NULL) warnx("error: The Boot Image parameter requires a valid boot information string"); else rv = cd9660_add_boot_disk(val); } else if (CD9660_IS_COMMAND_ARG(var, "bootimagedir")) { /* * XXXfvdl this is unused. */ if (val == NULL) errx(1, "error: The Boot Image Directory parameter" " requires a directory name\n"); else { if ((diskStructure.boot_image_directory = malloc(strlen(val) + 1)) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_parse_opts"); exit(1); } /* BIG TODO: Add the max length function here */ cd9660_arguments_set_string(val, "Boot Image Directory", 12 , 'd', diskStructure.boot_image_directory); } } else if (CD9660_IS_COMMAND_ARG_DUAL(var, "G", "generic-bootimage")) { if (val == NULL) warnx("error: The Boot Image parameter requires a valid boot information string"); else rv = cd9660_add_generic_bootimage(val); } else if (CD9660_IS_COMMAND_ARG(var, "no-trailing-padding")) diskStructure.include_padding_areas = 0; /* RRIP */ else if (CD9660_IS_COMMAND_ARG_DUAL(var, "R", "rockridge")) diskStructure.rock_ridge_enabled = 1; else if (CD9660_IS_COMMAND_ARG_DUAL(var, "A", "archimedes")) diskStructure.archimedes_enabled = 1; else if (CD9660_IS_COMMAND_ARG(var, "chrp-boot")) diskStructure.chrp_boot = 1; else if (CD9660_IS_COMMAND_ARG_DUAL(var, "K", "keep-bad-images")) diskStructure.keep_bad_images = 1; else if (CD9660_IS_COMMAND_ARG(var, "allow-deep-trees")) diskStructure.allow_deep_trees = 1; else if (CD9660_IS_COMMAND_ARG(var, "allow-max-name")) diskStructure.allow_max_name = 1; else if (CD9660_IS_COMMAND_ARG(var, "allow-illegal-chars")) diskStructure.allow_illegal_chars = 1; else if (CD9660_IS_COMMAND_ARG(var, "allow-lowercase")) diskStructure.allow_lowercase = 1; else if (CD9660_IS_COMMAND_ARG(var,"allow-multidot")) diskStructure.allow_multidot = 1; else if (CD9660_IS_COMMAND_ARG(var, "omit-trailing-period")) diskStructure.omit_trailing_period = 1; else if (CD9660_IS_COMMAND_ARG(var, "no-emul-boot") || CD9660_IS_COMMAND_ARG(var, "no-boot") || CD9660_IS_COMMAND_ARG(var, "hard-disk-boot")) { cd9660_eltorito_add_boot_option(var, 0); /* End of flag variables */ } else if (CD9660_IS_COMMAND_ARG(var, "boot-load-segment")) { if (val == NULL) { warnx("Option `%s' doesn't contain a value", var); rv = 0; } else { cd9660_eltorito_add_boot_option(var, val); } } else { if (val == NULL) { warnx("Option `%s' doesn't contain a value", var); rv = 0; } else rv = set_option(cd9660_options, var, val); } free(var); return (rv); } /* * Main function for cd9660_makefs * Builds the ISO image file * @param const char *image The image filename to create * @param const char *dir The directory that is being read * @param struct fsnode *root The root node of the filesystem tree * @param struct fsinfo_t *fsopts Any options */ void cd9660_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts) { int64_t startoffset; int numDirectories; uint64_t pathTableSectors; int64_t firstAvailableSector; int64_t totalSpace; int error; cd9660node *real_root; if (diskStructure.verbose_level > 0) printf("cd9660_makefs: ISO level is %i\n", diskStructure.isoLevel); if (diskStructure.isoLevel < 2 && diskStructure.allow_multidot) errx(1, "allow-multidot requires iso level of 2\n"); assert(image != NULL); assert(dir != NULL); assert(root != NULL); if (diskStructure.displayHelp) { /* * Display help here - probably want to put it in * a separate function */ return; } if (diskStructure.verbose_level > 0) printf("cd9660_makefs: image %s directory %s root %p\n", image, dir, root); /* Set up some constants. Later, these will be defined with options */ /* Counter needed for path tables */ numDirectories = 0; /* Convert tree to our own format */ /* Actually, we now need to add the REAL root node, at level 0 */ real_root = cd9660_allocate_cd9660node(); if ((real_root->isoDirRecord = malloc( sizeof(iso_directory_record_cd9660) )) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_makefs"); exit(1); } /* Leave filename blank for root */ memset(real_root->isoDirRecord->name, 0, ISO_FILENAME_MAXLENGTH_WITH_PADDING); real_root->level = 0; diskStructure.rootNode = real_root; real_root->type = CD9660_TYPE_DIR; error = 0; real_root->node = root; cd9660_convert_structure(root, real_root, 1, &numDirectories, &error); if (TAILQ_EMPTY(&real_root->cn_children)) { errx(1, "cd9660_makefs: converted directory is empty. " "Tree conversion failed\n"); } else if (error != 0) { errx(1, "cd9660_makefs: tree conversion failed\n"); } else { if (diskStructure.verbose_level > 0) printf("cd9660_makefs: tree converted\n"); } /* Add the dot and dot dot records */ cd9660_add_dot_records(real_root); cd9660_setup_root_node(); if (diskStructure.verbose_level > 0) printf("cd9660_makefs: done converting tree\n"); /* non-SUSP extensions */ if (diskStructure.archimedes_enabled) archimedes_convert_tree(diskStructure.rootNode); /* Rock ridge / SUSP init pass */ if (diskStructure.rock_ridge_enabled) { cd9660_susp_initialize(diskStructure.rootNode, diskStructure.rootNode, NULL); } /* Build path table structure */ diskStructure.pathTableLength = cd9660_generate_path_table(); pathTableSectors = CD9660_BLOCKS(diskStructure.sectorSize, diskStructure.pathTableLength); firstAvailableSector = cd9660_setup_volume_descriptors(); if (diskStructure.is_bootable) { firstAvailableSector = cd9660_setup_boot(firstAvailableSector); if (firstAvailableSector < 0) errx(1, "setup_boot failed"); } /* LE first, then BE */ diskStructure.primaryLittleEndianTableSector = firstAvailableSector; diskStructure.primaryBigEndianTableSector = diskStructure.primaryLittleEndianTableSector + pathTableSectors; /* Set the secondary ones to -1, not going to use them for now */ diskStructure.secondaryBigEndianTableSector = -1; diskStructure.secondaryLittleEndianTableSector = -1; diskStructure.dataFirstSector = diskStructure.primaryBigEndianTableSector + pathTableSectors; if (diskStructure.verbose_level > 0) printf("cd9660_makefs: Path table conversion complete. " "Each table is %i bytes, or %" PRIu64 " sectors.\n", diskStructure.pathTableLength, pathTableSectors); startoffset = diskStructure.sectorSize*diskStructure.dataFirstSector; totalSpace = cd9660_compute_offsets(real_root, startoffset); diskStructure.totalSectors = diskStructure.dataFirstSector + CD9660_BLOCKS(diskStructure.sectorSize, totalSpace); /* Disabled until pass 1 is done */ if (diskStructure.rock_ridge_enabled) { diskStructure.susp_continuation_area_start_sector = diskStructure.totalSectors; diskStructure.totalSectors += CD9660_BLOCKS(diskStructure.sectorSize, diskStructure.susp_continuation_area_size); cd9660_susp_finalize(diskStructure.rootNode); } cd9660_finalize_PVD(); /* Add padding sectors, just for testing purposes right now */ /* diskStructure.totalSectors+=150; */ /* Debugging output */ if (diskStructure.verbose_level > 0) { printf("cd9660_makefs: Sectors 0-15 reserved\n"); printf("cd9660_makefs: Primary path tables starts in sector %" PRId64 "\n", diskStructure.primaryLittleEndianTableSector); printf("cd9660_makefs: File data starts in sector %" PRId64 "\n", diskStructure.dataFirstSector); printf("cd9660_makefs: Total sectors: %" PRId64 "\n", diskStructure.totalSectors); } /* * Add padding sectors at the end * TODO: Clean this up and separate padding */ if (diskStructure.include_padding_areas) diskStructure.totalSectors += 150; cd9660_write_image(image); if (diskStructure.verbose_level > 1) { debug_print_volume_descriptor_information(); debug_print_tree(real_root,0); debug_print_path_tree(real_root); } /* Clean up data structures */ cd9660_free_structure(real_root); if (diskStructure.verbose_level > 0) printf("cd9660_makefs: done\n"); } /* Generic function pointer - implement later */ typedef int (*cd9660node_func)(cd9660node *); static void cd9660_finalize_PVD(void) { time_t tim; /* root should be a fixed size of 34 bytes since it has no name */ memcpy(diskStructure.primaryDescriptor.root_directory_record, diskStructure.rootNode->dot_record->isoDirRecord, 34); /* In RRIP, this might be longer than 34 */ diskStructure.primaryDescriptor.root_directory_record[0] = 34; /* Set up all the important numbers in the PVD */ cd9660_bothendian_dword(diskStructure.totalSectors, (unsigned char *)diskStructure.primaryDescriptor.volume_space_size); cd9660_bothendian_word(1, (unsigned char *)diskStructure.primaryDescriptor.volume_set_size); cd9660_bothendian_word(1, (unsigned char *) diskStructure.primaryDescriptor.volume_sequence_number); cd9660_bothendian_word(diskStructure.sectorSize, (unsigned char *) diskStructure.primaryDescriptor.logical_block_size); cd9660_bothendian_dword(diskStructure.pathTableLength, (unsigned char *)diskStructure.primaryDescriptor.path_table_size); cd9660_731(diskStructure.primaryLittleEndianTableSector, (u_char *)diskStructure.primaryDescriptor.type_l_path_table); cd9660_732(diskStructure.primaryBigEndianTableSector, (u_char *)diskStructure.primaryDescriptor.type_m_path_table); diskStructure.primaryDescriptor.file_structure_version[0] = 1; /* Pad all strings with spaces instead of nulls */ cd9660_pad_string_spaces(diskStructure.primaryDescriptor.volume_id, 32); cd9660_pad_string_spaces(diskStructure.primaryDescriptor.system_id, 32); cd9660_pad_string_spaces(diskStructure.primaryDescriptor.volume_set_id, 128); cd9660_pad_string_spaces(diskStructure.primaryDescriptor.publisher_id, 128); cd9660_pad_string_spaces(diskStructure.primaryDescriptor.preparer_id, 128); cd9660_pad_string_spaces(diskStructure.primaryDescriptor.application_id, 128); cd9660_pad_string_spaces( diskStructure.primaryDescriptor.copyright_file_id, 37); cd9660_pad_string_spaces( diskStructure.primaryDescriptor.abstract_file_id, 37); cd9660_pad_string_spaces( diskStructure.primaryDescriptor.bibliographic_file_id, 37); /* Setup dates */ time(&tim); cd9660_time_8426( (unsigned char *)diskStructure.primaryDescriptor.creation_date, tim); cd9660_time_8426( (unsigned char *)diskStructure.primaryDescriptor.modification_date, tim); /* cd9660_set_date(diskStructure.primaryDescriptor.expiration_date, now); */ memset(diskStructure.primaryDescriptor.expiration_date, '0', 16); diskStructure.primaryDescriptor.expiration_date[16] = 0; cd9660_time_8426( (unsigned char *)diskStructure.primaryDescriptor.effective_date, tim); } static void cd9660_populate_iso_dir_record(struct _iso_directory_record_cd9660 *record, u_char ext_attr_length, u_char flags, u_char name_len, const char * name) { record->ext_attr_length[0] = ext_attr_length; record->flags[0] = ISO_FLAG_CLEAR | flags; record->file_unit_size[0] = 0; record->interleave[0] = 0; cd9660_bothendian_word(1, record->volume_sequence_number); record->name_len[0] = name_len; memset(record->name, '\0', sizeof (record->name)); memcpy(record->name, name, name_len); record->length[0] = 33 + name_len; /* Todo : better rounding */ record->length[0] += (record->length[0] & 1) ? 1 : 0; } static void cd9660_setup_root_node(void) { cd9660_populate_iso_dir_record(diskStructure.rootNode->isoDirRecord, 0, ISO_FLAG_DIRECTORY, 1, "\0"); } /*********** SUPPORT FUNCTIONS ***********/ static int cd9660_setup_volume_descriptors(void) { /* Boot volume descriptor should come second */ int sector = 16; /* For now, a fixed 2 : PVD and terminator */ volume_descriptor *temp, *t; /* Set up the PVD */ if ((temp = malloc(sizeof(volume_descriptor))) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_setup_volume_descriptors"); exit(1); } temp->volumeDescriptorData = (unsigned char *)&diskStructure.primaryDescriptor; temp->volumeDescriptorData[0] = ISO_VOLUME_DESCRIPTOR_PVD; temp->volumeDescriptorData[6] = 1; temp->sector = sector; memcpy(temp->volumeDescriptorData + 1, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5); diskStructure.firstVolumeDescriptor = temp; sector++; /* Set up boot support if enabled. BVD must reside in sector 17 */ if (diskStructure.is_bootable) { if ((t = malloc(sizeof(volume_descriptor))) == NULL) { CD9660_MEM_ALLOC_ERROR( "cd9660_setup_volume_descriptors"); exit(1); } if ((t->volumeDescriptorData = malloc(2048)) == NULL) { CD9660_MEM_ALLOC_ERROR( "cd9660_setup_volume_descriptors"); exit(1); } temp->next = t; temp = t; memset(t->volumeDescriptorData, 0, 2048); t->sector = 17; if (diskStructure.verbose_level > 0) printf("Setting up boot volume descriptor\n"); cd9660_setup_boot_volume_descriptor(t); sector++; } /* Set up the terminator */ if ((t = malloc(sizeof(volume_descriptor))) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_setup_volume_descriptors"); exit(1); } if ((t->volumeDescriptorData = malloc(2048)) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_setup_volume_descriptors"); exit(1); } temp->next = t; memset(t->volumeDescriptorData, 0, 2048); t->volumeDescriptorData[0] = ISO_VOLUME_DESCRIPTOR_TERMINATOR; t->next = 0; t->volumeDescriptorData[6] = 1; t->sector = sector; memcpy(t->volumeDescriptorData + 1, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5); sector++; return sector; } #if 0 /* * Populate EAR at some point. Not required, but is used by NetBSD's * cd9660 support */ static int cd9660_fill_extended_attribute_record(cd9660node *node) { if ((node->isoExtAttributes = malloc(sizeof(struct iso_extended_attributes))) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_fill_extended_attribute_record"); exit(1); }; return 1; } #endif static int cd9660_translate_node_common(cd9660node *newnode) { time_t tim; int test; u_char flag; char temp[ISO_FILENAME_MAXLENGTH_WITH_PADDING]; /* Now populate the isoDirRecord structure */ memset(temp, 0, ISO_FILENAME_MAXLENGTH_WITH_PADDING); test = cd9660_convert_filename(newnode->node->name, temp, !(S_ISDIR(newnode->node->type))); flag = ISO_FLAG_CLEAR; if (S_ISDIR(newnode->node->type)) flag |= ISO_FLAG_DIRECTORY; cd9660_populate_iso_dir_record(newnode->isoDirRecord, 0, flag, strlen(temp), temp); /* Set the various dates */ /* If we want to use the current date and time */ time(&tim); cd9660_time_915(newnode->isoDirRecord->date, tim); cd9660_bothendian_dword(newnode->fileDataLength, newnode->isoDirRecord->size); /* If the file is a link, we want to set the size to 0 */ if (S_ISLNK(newnode->node->type)) newnode->fileDataLength = 0; return 1; } /* * Translate fsnode to cd9660node * Translate filenames and other metadata, including dates, sizes, * permissions, etc * @param struct fsnode * The node generated by makefs * @param struct cd9660node * The intermediate node to be written to * @returns int 0 on failure, 1 on success */ static int cd9660_translate_node(fsnode *node, cd9660node *newnode) { if (node == NULL) { if (diskStructure.verbose_level > 0) printf("cd9660_translate_node: NULL node passed, " "returning\n"); return 0; } if ((newnode->isoDirRecord = malloc(sizeof(iso_directory_record_cd9660))) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_translate_node"); return 0; } /* Set the node pointer */ newnode->node = node; /* Set the size */ if (!(S_ISDIR(node->type))) newnode->fileDataLength = node->inode->st.st_size; if (cd9660_translate_node_common(newnode) == 0) return 0; /* Finally, overwrite some of the values that are set by default */ cd9660_time_915(newnode->isoDirRecord->date, node->inode->st.st_mtime); return 1; } /* * Compares two ISO filenames * @param const char * The first file name * @param const char * The second file name * @returns : -1 if first is less than second, 0 if they are the same, 1 if * the second is greater than the first */ static int cd9660_compare_filename(const char *first, const char *second) { /* * This can be made more optimal once it has been tested * (the extra character, for example, is for testing) */ int p1 = 0; int p2 = 0; char c1, c2; /* First, on the filename */ while (p1 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION-1 && p2 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION-1) { c1 = first[p1]; c2 = second[p2]; if (c1 == '.' && c2 =='.') break; else if (c1 == '.') { p2++; c1 = ' '; } else if (c2 == '.') { p1++; c2 = ' '; } else { p1++; p2++; } if (c1 < c2) return -1; else if (c1 > c2) { return 1; } } if (first[p1] == '.' && second[p2] == '.') { p1++; p2++; while (p1 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION - 1 && p2 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION - 1) { c1 = first[p1]; c2 = second[p2]; if (c1 == ';' && c2 == ';') break; else if (c1 == ';') { p2++; c1 = ' '; } else if (c2 == ';') { p1++; c2 = ' '; } else { p1++; p2++; } if (c1 < c2) return -1; else if (c1 > c2) return 1; } } return 0; } /* * Insert a node into list with ISO sorting rules * @param cd9660node * The head node of the list * @param cd9660node * The node to be inserted */ static void cd9660_sorted_child_insert(cd9660node *parent, cd9660node *cn_new) { int compare; cd9660node *cn; struct cd9660_children_head *head = &parent->cn_children; /* TODO: Optimize? */ cn_new->parent = parent; /* * first will either be 0, the . or the .. * if . or .., this means no other entry may be written before first * if 0, the new node may be inserted at the head */ TAILQ_FOREACH(cn, head, cn_next_child) { /* * Dont insert a node twice - * that would cause an infinite loop */ if (cn_new == cn) return; compare = cd9660_compare_filename(cn_new->isoDirRecord->name, cn->isoDirRecord->name); if (compare == 0) compare = cd9660_compare_filename(cn_new->node->name, cn->node->name); if (compare < 0) break; } if (cn == NULL) TAILQ_INSERT_TAIL(head, cn_new, cn_next_child); else TAILQ_INSERT_BEFORE(cn, cn_new, cn_next_child); } /* * Called After cd9660_sorted_child_insert * handles file collisions by suffixing each filname with ~n * where n represents the files respective place in the ordering */ static int cd9660_handle_collisions(cd9660node *colliding, int past) { cd9660node *iter, *next, *prev; int skip; int delete_chars = 0; int temp_past = past; int temp_skip; int flag = 0; cd9660node *end_of_range; for (iter = TAILQ_FIRST(&colliding->cn_children); iter != NULL && (next = TAILQ_NEXT(iter, cn_next_child)) != NULL;) { if (strcmp(iter->isoDirRecord->name, next->isoDirRecord->name) != 0) { iter = TAILQ_NEXT(iter, cn_next_child); continue; } flag = 1; temp_skip = skip = cd9660_count_collisions(iter); end_of_range = iter; while (temp_skip > 0) { temp_skip--; end_of_range = TAILQ_NEXT(end_of_range, cn_next_child); } temp_past = past; while (temp_past > 0) { if ((next = TAILQ_NEXT(end_of_range, cn_next_child)) != NULL) end_of_range = next; else if ((prev = TAILQ_PREV(iter, cd9660_children_head, cn_next_child)) != NULL) iter = prev; else delete_chars++; temp_past--; } skip += past; iter = cd9660_rename_filename(iter, skip, delete_chars); } return flag; } static cd9660node * cd9660_rename_filename(cd9660node *iter, int num, int delete_chars) { int i = 0; int numbts, digit, digits, temp, powers, count; char *naming; int maxlength; char *tmp; if (diskStructure.verbose_level > 0) printf("Rename_filename called\n"); /* TODO : A LOT of chanes regarding 8.3 filenames */ if (diskStructure.isoLevel == 1) maxlength = 8; else if (diskStructure.isoLevel == 2) maxlength = 31; else maxlength = ISO_FILENAME_MAXLENGTH_BEFORE_VERSION; tmp = malloc(ISO_FILENAME_MAXLENGTH_WITH_PADDING); while (i < num) { powers = 1; count = 0; digits = 1; while (((int)(i / powers) ) >= 10) { digits++; powers = powers * 10; } naming = iter->o_name; /* while ((*naming != '.') && (*naming != ';')) { naming++; count++; } */ while (count < maxlength) { if (*naming == ';') break; naming++; count++; } if ((count + digits) < maxlength) numbts = count; else numbts = maxlength - (digits); numbts -= delete_chars; /* 8.3 rules - keep the extension, add before the dot */ /* * This code makes a bunch of assumptions. * See if you can spot them all :) */ /* if (diskStructure.isoLevel == 1) { numbts = 8 - digits - delete_chars; if (dot < 0) { } else { if (dot < 8) { memmove(&tmp[numbts],&tmp[dot],4); } } } */ /* (copying just the filename before the '.' */ memcpy(tmp, (iter->o_name), numbts); /* adding the appropriate number following the name */ temp = i; while (digits > 0) { digit = (int)(temp / powers); temp = temp - digit * powers; sprintf(&tmp[numbts] , "%d", digit); digits--; numbts++; powers = powers / 10; } while ((*naming != ';') && (numbts < maxlength)) { tmp[numbts] = (*naming); naming++; numbts++; } tmp[numbts] = ';'; tmp[numbts+1] = '1'; tmp[numbts+2] = '\0'; /* * now tmp has exactly the identifier * we want so we'll copy it back to record */ memcpy((iter->isoDirRecord->name), tmp, numbts + 3); iter = TAILQ_NEXT(iter, cn_next_child); i++; } free(tmp); return iter; } /* Todo: Figure out why these functions are nec. */ static void cd9660_copy_filenames(cd9660node *node) { cd9660node *cn; if (TAILQ_EMPTY(&node->cn_children)) return; if (TAILQ_FIRST(&node->cn_children)->isoDirRecord == NULL) { debug_print_tree(diskStructure.rootNode, 0); exit(1); } TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) { cd9660_copy_filenames(cn); memcpy(cn->o_name, cn->isoDirRecord->name, ISO_FILENAME_MAXLENGTH_WITH_PADDING); } } static void cd9660_sorting_nodes(cd9660node *node) { cd9660node *cn; TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) cd9660_sorting_nodes(cn); cd9660_sort_nodes(node); } /* XXX Bubble sort. */ static void cd9660_sort_nodes(cd9660node *node) { cd9660node *cn, *next; do { TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) { if ((next = TAILQ_NEXT(cn, cn_next_child)) == NULL) return; else if (strcmp(next->isoDirRecord->name, cn->isoDirRecord->name) >= 0) continue; TAILQ_REMOVE(&node->cn_children, next, cn_next_child); TAILQ_INSERT_BEFORE(cn, next, cn_next_child); break; } } while (cn != NULL); } static int cd9660_count_collisions(cd9660node *copy) { int count = 0; cd9660node *iter, *next; for (iter = copy; (next = TAILQ_NEXT(iter, cn_next_child)) != NULL; iter = next) { if (cd9660_compare_filename(iter->isoDirRecord->name, next->isoDirRecord->name) == 0) count++; else return count; } #if 0 if ((next = TAILQ_NEXT(iter, cn_next_child)) != NULL) { printf("cd9660_recurse_on_collision: count is %i \n", count); compare = cd9660_compare_filename(iter->isoDirRecord->name, next->isoDirRecord->name); if (compare == 0) { count++; return cd9660_recurse_on_collision(next, count); } else return count; } #endif return count; } static cd9660node * cd9660_rrip_move_directory(cd9660node *dir) { char newname[9]; cd9660node *tfile; /* * This function needs to: * 1) Create an empty virtual file in place of the old directory * 2) Point the virtual file to the new directory * 3) Point the relocated directory to its old parent * 4) Move the directory specified by dir into rr_moved_dir, * and rename it to "diskStructure.rock_ridge_move_count" (as a string) */ /* First see if the moved directory even exists */ if (diskStructure.rr_moved_dir == NULL) { diskStructure.rr_moved_dir = cd9660_create_directory(ISO_RRIP_DEFAULT_MOVE_DIR_NAME, diskStructure.rootNode, dir); if (diskStructure.rr_moved_dir == NULL) return 0; } /* Create a file with the same ORIGINAL name */ tfile = cd9660_create_file(dir->node->name, dir->parent, dir); if (tfile == NULL) return NULL; diskStructure.rock_ridge_move_count++; snprintf(newname, sizeof(newname), "%08i", diskStructure.rock_ridge_move_count); /* Point to old parent */ dir->rr_real_parent = dir->parent; /* Place the placeholder file */ if (TAILQ_EMPTY(&dir->rr_real_parent->cn_children)) { TAILQ_INSERT_HEAD(&dir->rr_real_parent->cn_children, tfile, cn_next_child); } else { cd9660_sorted_child_insert(dir->rr_real_parent, tfile); } /* Point to new parent */ dir->parent = diskStructure.rr_moved_dir; /* Point the file to the moved directory */ tfile->rr_relocated = dir; /* Actually move the directory */ cd9660_sorted_child_insert(diskStructure.rr_moved_dir, dir); /* TODO: Inherit permissions / ownership (basically the entire inode) */ /* Set the new name */ memset(dir->isoDirRecord->name, 0, ISO_FILENAME_MAXLENGTH_WITH_PADDING); strncpy(dir->isoDirRecord->name, newname, 8); dir->isoDirRecord->length[0] = 34 + 8; dir->isoDirRecord->name_len[0] = 8; return dir; } static int cd9660_add_dot_records(cd9660node *root) { struct cd9660_children_head *head = &root->cn_children; cd9660node *cn; TAILQ_FOREACH(cn, head, cn_next_child) { if ((cn->type & CD9660_TYPE_DIR) == 0) continue; /* Recursion first */ cd9660_add_dot_records(cn); } cd9660_create_special_directory(CD9660_TYPE_DOT, root); cd9660_create_special_directory(CD9660_TYPE_DOTDOT, root); return 1; } /* * Convert node to cd9660 structure * This function is designed to be called recursively on the root node of * the filesystem * Lots of recursion going on here, want to make sure it is efficient * @param struct fsnode * The root node to be converted * @param struct cd9660* The parent node (should not be NULL) * @param int Current directory depth * @param int* Running count of the number of directories that are being created */ static void cd9660_convert_structure(fsnode *root, cd9660node *parent_node, int level, int *numDirectories, int *error) { fsnode *iterator = root; cd9660node *this_node; int working_level; int add; int flag = 0; int counter = 0; /* * Newer, more efficient method, reduces recursion depth */ if (root == NULL) { warnx("%s: root is null\n", __func__); return; } /* Test for an empty directory - makefs still gives us the . record */ if ((S_ISDIR(root->type)) && (root->name[0] == '.') && (root->name[1] == '\0')) { root = root->next; if (root == NULL) return; } if ((this_node = cd9660_allocate_cd9660node()) == NULL) { CD9660_MEM_ALLOC_ERROR(__func__); } /* * To reduce the number of recursive calls, we will iterate over * the next pointers to the right. */ while (iterator != NULL) { add = 1; /* * Increment the directory count if this is a directory * Ignore "." entries. We will generate them later */ if (!S_ISDIR(iterator->type) || strcmp(iterator->name, ".") != 0) { /* Translate the node, including its filename */ this_node->parent = parent_node; cd9660_translate_node(iterator, this_node); this_node->level = level; if (S_ISDIR(iterator->type)) { (*numDirectories)++; this_node->type = CD9660_TYPE_DIR; working_level = level + 1; /* * If at level 8, directory would be at 8 * and have children at 9 which is not * allowed as per ISO spec */ if (level == 8) { if ((!diskStructure.allow_deep_trees) && (!diskStructure.rock_ridge_enabled)) { warnx("error: found entry " "with depth greater " "than 8."); (*error) = 1; return; } else if (diskStructure. rock_ridge_enabled) { working_level = 3; /* * Moved directory is actually * at level 2. */ this_node->level = working_level - 1; if (cd9660_rrip_move_directory( this_node) == 0) { warnx("Failure in " "cd9660_rrip_" "move_directory" ); (*error) = 1; return; } add = 0; } } /* Do the recursive call on the children */ if (iterator->child != 0) { cd9660_convert_structure( iterator->child, this_node, working_level, numDirectories, error); if ((*error) == 1) { warnx("%s: Error on recursive " "call", __func__); return; } } } else { /* Only directories should have children */ assert(iterator->child == NULL); this_node->type = CD9660_TYPE_FILE; } /* * Finally, do a sorted insert */ if (add) { cd9660_sorted_child_insert( parent_node, this_node); } /*Allocate new temp_node */ if (iterator->next != 0) { this_node = cd9660_allocate_cd9660node(); if (this_node == NULL) CD9660_MEM_ALLOC_ERROR(__func__); } } iterator = iterator->next; } /* cd9660_handle_collisions(first_node); */ /* TODO: need cleanup */ cd9660_copy_filenames(parent_node); do { flag = cd9660_handle_collisions(parent_node, counter); counter++; cd9660_sorting_nodes(parent_node); } while ((flag == 1) && (counter < 100)); } /* * Clean up the cd9660node tree * This is designed to be called recursively on the root node * @param struct cd9660node *root The node to free * @returns void */ static void cd9660_free_structure(cd9660node *root) { cd9660node *cn; while ((cn = TAILQ_FIRST(&root->cn_children)) != NULL) { TAILQ_REMOVE(&root->cn_children, cn, cn_next_child); cd9660_free_structure(cn); } free(root); } /* * Be a little more memory conservative: * instead of having the TAILQ_ENTRY as part of the cd9660node, * just create a temporary structure */ struct ptq_entry { TAILQ_ENTRY(ptq_entry) ptq; cd9660node *node; } *n; #define PTQUEUE_NEW(n,s,r,t){\ n = malloc(sizeof(struct s)); \ if (n == NULL) \ return r; \ n->node = t;\ } /* * Generate the path tables * The specific implementation of this function is left as an exercise to the * programmer. It could be done recursively. Make sure you read how the path * table has to be laid out, it has levels. * @param struct iso9660_disk *disk The disk image * @returns int The number of built path tables (between 1 and 4), 0 on failure */ static int cd9660_generate_path_table(void) { cd9660node *cn, *dirNode = diskStructure.rootNode; cd9660node *last = dirNode; int pathTableSize = 0; /* computed as we go */ int counter = 1; /* root gets a count of 0 */ TAILQ_HEAD(cd9660_pt_head, ptq_entry) pt_head; TAILQ_INIT(&pt_head); PTQUEUE_NEW(n, ptq_entry, -1, diskStructure.rootNode); /* Push the root node */ TAILQ_INSERT_HEAD(&pt_head, n, ptq); /* Breadth-first traversal of file structure */ while (pt_head.tqh_first != 0) { n = pt_head.tqh_first; dirNode = n->node; TAILQ_REMOVE(&pt_head, pt_head.tqh_first, ptq); free(n); /* Update the size */ pathTableSize += ISO_PATHTABLE_ENTRY_BASESIZE + dirNode->isoDirRecord->name_len[0]+ (dirNode->isoDirRecord->name_len[0] % 2 == 0 ? 0 : 1); /* includes the padding bit */ dirNode->ptnumber=counter; if (dirNode != last) { last->ptnext = dirNode; dirNode->ptprev = last; } last = dirNode; /* Push children onto queue */ TAILQ_FOREACH(cn, &dirNode->cn_children, cn_next_child) { /* * Dont add the DOT and DOTDOT types to the path * table. */ if ((cn->type != CD9660_TYPE_DOT) && (cn->type != CD9660_TYPE_DOTDOT)) { if (S_ISDIR(cn->node->type)) { PTQUEUE_NEW(n, ptq_entry, -1, cn); TAILQ_INSERT_TAIL(&pt_head, n, ptq); } } } counter++; } return pathTableSize; } void cd9660_compute_full_filename(cd9660node *node, char *buf) { int len; len = CD9660MAXPATH + 1; len = snprintf(buf, len, "%s/%s/%s", node->node->root, node->node->path, node->node->name); if (len > CD9660MAXPATH) errx(1, "Pathname too long."); } /* NEW filename conversion method */ typedef int(*cd9660_filename_conversion_functor)(const char *, char *, int); /* * TODO: These two functions are almost identical. * Some code cleanup is possible here * * XXX bounds checking! */ static int cd9660_level1_convert_filename(const char *oldname, char *newname, int is_file) { /* * ISO 9660 : 10.1 * File Name shall not contain more than 8 d or d1 characters * File Name Extension shall not contain more than 3 d or d1 characters * Directory Identifier shall not contain more than 8 d or d1 characters */ int namelen = 0; int extlen = 0; int found_ext = 0; while (*oldname != '\0' && extlen < 3) { /* Handle period first, as it is special */ if (*oldname == '.') { if (found_ext) { *newname++ = '_'; extlen ++; } else { *newname++ = '.'; found_ext = 1; } } else { /* cut RISC OS file type off ISO name */ if (diskStructure.archimedes_enabled && *oldname == ',' && strlen(oldname) == 4) break; /* Enforce 12.3 / 8 */ if (namelen == 8 && !found_ext) break; if (islower((unsigned char)*oldname)) *newname++ = toupper((unsigned char)*oldname); else if (isupper((unsigned char)*oldname) || isdigit((unsigned char)*oldname)) *newname++ = *oldname; else *newname++ = '_'; if (found_ext) extlen++; else namelen++; } oldname ++; } if (is_file) { if (!found_ext && !diskStructure.omit_trailing_period) *newname++ = '.'; /* Add version */ sprintf(newname, ";%i", 1); } return namelen + extlen + found_ext; } /* XXX bounds checking! */ static int cd9660_level2_convert_filename(const char *oldname, char *newname, int is_file) { /* * ISO 9660 : 7.5.1 * File name : 0+ d or d1 characters * separator 1 (.) * File name extension : 0+ d or d1 characters * separator 2 (;) * File version number (5 characters, 1-32767) * 1 <= Sum of File name and File name extension <= 30 */ int namelen = 0; int extlen = 0; int found_ext = 0; while (*oldname != '\0' && namelen + extlen < 30) { /* Handle period first, as it is special */ if (*oldname == '.') { if (found_ext) { if (diskStructure.allow_multidot) { *newname++ = '.'; } else { *newname++ = '_'; } extlen ++; } else { *newname++ = '.'; found_ext = 1; } } else { /* cut RISC OS file type off ISO name */ if (diskStructure.archimedes_enabled && *oldname == ',' && strlen(oldname) == 4) break; if (islower((unsigned char)*oldname)) *newname++ = toupper((unsigned char)*oldname); else if (isupper((unsigned char)*oldname) || isdigit((unsigned char)*oldname)) *newname++ = *oldname; else if (diskStructure.allow_multidot && *oldname == '.') { *newname++ = '.'; } else { *newname++ = '_'; } if (found_ext) extlen++; else namelen++; } oldname ++; } if (is_file) { if (!found_ext && !diskStructure.omit_trailing_period) *newname++ = '.'; /* Add version */ sprintf(newname, ";%i", 1); } return namelen + extlen + found_ext; } #if 0 static int cd9660_joliet_convert_filename(const char *oldname, char *newname, int is_file) { /* TODO: implement later, move to cd9660_joliet.c ?? */ } #endif /* * Convert a file name to ISO compliant file name * @param char * oldname The original filename * @param char ** newname The new file name, in the appropriate character * set and of appropriate length * @param int 1 if file, 0 if directory * @returns int The length of the new string */ static int cd9660_convert_filename(const char *oldname, char *newname, int is_file) { /* NEW */ cd9660_filename_conversion_functor conversion_function = 0; if (diskStructure.isoLevel == 1) conversion_function = &cd9660_level1_convert_filename; else if (diskStructure.isoLevel == 2) conversion_function = &cd9660_level2_convert_filename; return (*conversion_function)(oldname, newname, is_file); } int cd9660_compute_record_size(cd9660node *node) { int size = node->isoDirRecord->length[0]; if (diskStructure.rock_ridge_enabled) size += node->susp_entry_size; size += node->su_tail_size; size += size & 1; /* Ensure length of record is even. */ assert(size <= 254); return size; } static void cd9660_populate_dot_records(cd9660node *node) { node->dot_record->fileDataSector = node->fileDataSector; memcpy(node->dot_record->isoDirRecord,node->isoDirRecord, 34); node->dot_record->isoDirRecord->name_len[0] = 1; node->dot_record->isoDirRecord->name[0] = 0; node->dot_record->isoDirRecord->name[1] = 0; node->dot_record->isoDirRecord->length[0] = 34; node->dot_record->fileRecordSize = cd9660_compute_record_size(node->dot_record); if (node == diskStructure.rootNode) { node->dot_dot_record->fileDataSector = node->fileDataSector; memcpy(node->dot_dot_record->isoDirRecord,node->isoDirRecord, 34); } else { node->dot_dot_record->fileDataSector = node->parent->fileDataSector; memcpy(node->dot_dot_record->isoDirRecord, node->parent->isoDirRecord,34); } node->dot_dot_record->isoDirRecord->name_len[0] = 1; node->dot_dot_record->isoDirRecord->name[0] = 1; node->dot_dot_record->isoDirRecord->name[1] = 0; node->dot_dot_record->isoDirRecord->length[0] = 34; node->dot_dot_record->fileRecordSize = cd9660_compute_record_size(node->dot_dot_record); } /* * @param struct cd9660node *node The node * @param int The offset (in bytes) - SHOULD align to the beginning of a sector * @returns int The total size of files and directory entries (should be * a multiple of sector size) */ static int64_t cd9660_compute_offsets(cd9660node *node, int64_t startOffset) { /* * This function needs to compute the size of directory records and * runs, file lengths, and set the appropriate variables both in * cd9660node and isoDirEntry */ int64_t used_bytes = 0; int64_t current_sector_usage = 0; cd9660node *child; fsinode *inode; int64_t r; assert(node != NULL); /* * NOTE : There needs to be some special case detection for * the "real root" node, since for it, node->node is undefined */ node->fileDataSector = -1; if (node->type & CD9660_TYPE_DIR) { node->fileRecordSize = cd9660_compute_record_size(node); /*Set what sector this directory starts in*/ node->fileDataSector = CD9660_BLOCKS(diskStructure.sectorSize,startOffset); cd9660_bothendian_dword(node->fileDataSector, node->isoDirRecord->extent); /* * First loop over children, need to know the size of * their directory records */ node->fileSectorsUsed = 1; TAILQ_FOREACH(child, &node->cn_children, cn_next_child) { node->fileDataLength += cd9660_compute_record_size(child); if ((cd9660_compute_record_size(child) + current_sector_usage) >= diskStructure.sectorSize) { current_sector_usage = 0; node->fileSectorsUsed++; } current_sector_usage += cd9660_compute_record_size(child); } cd9660_bothendian_dword(node->fileSectorsUsed * diskStructure.sectorSize,node->isoDirRecord->size); /* * This should point to the sector after the directory * record (or, the first byte in that sector) */ used_bytes += node->fileSectorsUsed * diskStructure.sectorSize; for (child = TAILQ_NEXT(node->dot_dot_record, cn_next_child); child != NULL; child = TAILQ_NEXT(child, cn_next_child)) { /* Directories need recursive call */ if (S_ISDIR(child->node->type)) { r = cd9660_compute_offsets(child, used_bytes + startOffset); if (r != -1) used_bytes += r; else return -1; } } /* Explicitly set the . and .. records */ cd9660_populate_dot_records(node); /* Finally, do another iteration to write the file data*/ for (child = TAILQ_NEXT(node->dot_dot_record, cn_next_child); child != NULL; child = TAILQ_NEXT(child, cn_next_child)) { /* Files need extent set */ if (S_ISDIR(child->node->type)) continue; child->fileRecordSize = cd9660_compute_record_size(child); child->fileSectorsUsed = CD9660_BLOCKS(diskStructure.sectorSize, child->fileDataLength); inode = child->node->inode; if ((inode->flags & FI_ALLOCATED) == 0) { inode->ino = CD9660_BLOCKS(diskStructure.sectorSize, used_bytes + startOffset); inode->flags |= FI_ALLOCATED; used_bytes += child->fileSectorsUsed * diskStructure.sectorSize; } else { INODE_WARNX(("%s: already allocated inode %d " "data sectors at %" PRIu32, __func__, (int)inode->st.st_ino, inode->ino)); } child->fileDataSector = inode->ino; cd9660_bothendian_dword(child->fileDataSector, child->isoDirRecord->extent); } } return used_bytes; } #if 0 /* Might get rid of this func */ static int cd9660_copy_stat_info(cd9660node *from, cd9660node *to, int file) { to->node->inode->st.st_dev = 0; to->node->inode->st.st_ino = 0; to->node->inode->st.st_size = 0; to->node->inode->st.st_blksize = from->node->inode->st.st_blksize; to->node->inode->st.st_atime = from->node->inode->st.st_atime; to->node->inode->st.st_mtime = from->node->inode->st.st_mtime; to->node->inode->st.st_ctime = from->node->inode->st.st_ctime; to->node->inode->st.st_uid = from->node->inode->st.st_uid; to->node->inode->st.st_gid = from->node->inode->st.st_gid; to->node->inode->st.st_mode = from->node->inode->st.st_mode; /* Clear out type */ to->node->inode->st.st_mode = to->node->inode->st.st_mode & ~(S_IFMT); if (file) to->node->inode->st.st_mode |= S_IFREG; else to->node->inode->st.st_mode |= S_IFDIR; return 1; } #endif static cd9660node * cd9660_create_virtual_entry(const char *name, cd9660node *parent, int file, int insert) { cd9660node *temp; fsnode * tfsnode; assert(parent != NULL); temp = cd9660_allocate_cd9660node(); if (temp == NULL) return NULL; if ((tfsnode = malloc(sizeof(fsnode))) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_create_virtual_entry"); return NULL; } /* Assume for now name is a valid length */ if ((tfsnode->name = malloc(strlen(name) + 1)) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_create_virtual_entry"); return NULL; } if ((temp->isoDirRecord = malloc(sizeof(iso_directory_record_cd9660))) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_create_virtual_entry"); return NULL; } strcpy(tfsnode->name, name); cd9660_convert_filename(tfsnode->name, temp->isoDirRecord->name, file); temp->node = tfsnode; temp->parent = parent; if (insert) { if (temp->parent != NULL) { temp->level = temp->parent->level + 1; if (!TAILQ_EMPTY(&temp->parent->cn_children)) cd9660_sorted_child_insert(temp->parent, temp); else TAILQ_INSERT_HEAD(&temp->parent->cn_children, temp, cn_next_child); } } if (parent->node != NULL) { tfsnode->type = parent->node->type; } /* Clear out file type bits */ tfsnode->type &= ~(S_IFMT); if (file) tfsnode->type |= S_IFREG; else tfsnode->type |= S_IFDIR; /* Indicate that there is no spec entry (inode) */ tfsnode->flags &= ~(FSNODE_F_HASSPEC); #if 0 cd9660_copy_stat_info(parent, temp, file); #endif return temp; } static cd9660node * cd9660_create_file(const char * name, cd9660node *parent, cd9660node *me) { cd9660node *temp; temp = cd9660_create_virtual_entry(name,parent,1,1); if (temp == NULL) return NULL; temp->fileDataLength = 0; temp->type = CD9660_TYPE_FILE | CD9660_TYPE_VIRTUAL; if ((temp->node->inode = calloc(1, sizeof(fsinode))) == NULL) return NULL; *temp->node->inode = *me->node->inode; if (cd9660_translate_node_common(temp) == 0) return NULL; return temp; } /* * Create a new directory which does not exist on disk * @param const char * name The name to assign to the directory * @param const char * parent Pointer to the parent directory * @returns cd9660node * Pointer to the new directory */ static cd9660node * cd9660_create_directory(const char *name, cd9660node *parent, cd9660node *me) { cd9660node *temp; temp = cd9660_create_virtual_entry(name,parent,0,1); if (temp == NULL) return NULL; temp->node->type |= S_IFDIR; temp->type = CD9660_TYPE_DIR | CD9660_TYPE_VIRTUAL; if ((temp->node->inode = calloc(1, sizeof(fsinode))) == NULL) return NULL; *temp->node->inode = *me->node->inode; if (cd9660_translate_node_common(temp) == 0) return NULL; return temp; } static cd9660node * cd9660_create_special_directory(u_char type, cd9660node *parent) { cd9660node *temp, *first; char na[2]; assert(parent != NULL); if (type == CD9660_TYPE_DOT) na[0] = 0; else if (type == CD9660_TYPE_DOTDOT) na[0] = 1; else return 0; na[1] = 0; if ((temp = cd9660_create_virtual_entry(na, parent, 0, 0)) == NULL) return NULL; temp->parent = parent; temp->type = type; temp->isoDirRecord->length[0] = 34; /* Dot record is always first */ if (type == CD9660_TYPE_DOT) { parent->dot_record = temp; TAILQ_INSERT_HEAD(&parent->cn_children, temp, cn_next_child); /* DotDot should be second */ } else if (type == CD9660_TYPE_DOTDOT) { parent->dot_dot_record = temp; /* * If the first child is the dot record, insert * this second. Otherwise, insert it at the head. */ if ((first = TAILQ_FIRST(&parent->cn_children)) == NULL || (first->type & CD9660_TYPE_DOT) == 0) { TAILQ_INSERT_HEAD(&parent->cn_children, temp, cn_next_child); } else { TAILQ_INSERT_AFTER(&parent->cn_children, first, temp, cn_next_child); } } return temp; } int cd9660_add_generic_bootimage(const char *bootimage) { struct stat stbuf; assert(bootimage != NULL); if (*bootimage == '\0') { warnx("Error: Boot image must be a filename"); return 0; } if ((diskStructure.generic_bootimage = strdup(bootimage)) == NULL) { warn("%s: strdup", __func__); return 0; } /* Get information about the file */ if (lstat(diskStructure.generic_bootimage, &stbuf) == -1) err(EXIT_FAILURE, "%s: lstat(\"%s\")", __func__, diskStructure.generic_bootimage); if (stbuf.st_size > 32768) { warnx("Error: Boot image must be no greater than 32768 bytes"); return 0; } if (diskStructure.verbose_level > 0) { printf("Generic boot image image has size %lld\n", (long long)stbuf.st_size); } diskStructure.has_generic_bootimage = 1; return 1; } Index: head/usr.sbin/mfiutil/mfiutil.8 =================================================================== --- head/usr.sbin/mfiutil/mfiutil.8 (revision 289676) +++ head/usr.sbin/mfiutil/mfiutil.8 (revision 289677) @@ -1,723 +1,723 @@ .\" Copyright (c) 2008, 2009 Yahoo!, Inc. .\" 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. The names of the authors may not be used to endorse or promote .\" products derived from this software without specific prior written .\" permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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. .\" .\" $FreeBSD$ .\" .Dd September 2, 2011 .Dt MFIUTIL 8 .Os .Sh NAME .Nm mfiutil .Nd Utility for managing LSI MegaRAID SAS controllers .Sh SYNOPSIS .Nm .Cm version .Nm .Op Fl u Ar unit .Cm show adapter .Nm .Op Fl u Ar unit .Cm show battery .Nm .Op Fl d .Op Fl e .Op Fl u Ar unit .Cm show config .Nm .Op Fl u Ar unit .Cm show drives .Nm .Op Fl u Ar unit .Cm show events .Op Fl c Ar class .Op Fl l Ar locale .Op Fl n Ar count .Op Fl v .Op Ar start Op Ar stop .Nm .Op Fl u Ar unit .Cm show firmware .Nm .Op Fl u Ar unit .Cm show foreign Op Ar volume .Nm .Op Fl u Ar unit .Cm show logstate .Nm .Op Fl d .Op Fl e .Op Fl u Ar unit .Cm show patrol .Nm .Op Fl d .Op Fl e .Op Fl u Ar unit .Cm show progress .Nm .Op Fl u Ar unit .Cm show volumes .Nm .Op Fl u Ar unit .Cm fail Ar drive .Nm .Op Fl u Ar unit .Cm good Ar drive .Nm .Op Fl u Ar unit .Cm rebuild Ar drive .Nm .Op Fl u Ar unit .Cm syspd Ar drive .Nm .Op Fl u Ar unit .Cm drive progress Ar drive .Nm .Op Fl u Ar unit .Cm drive clear Ar drive Brq "start | stop" .Nm .Op Fl u Ar unit .Cm start rebuild Ar drive .Nm .Op Fl u Ar unit .Cm abort rebuild Ar drive .Nm .Op Fl u Ar unit .Cm locate Ar drive Brq "on | off" .Nm .Op Fl u Ar unit .Cm cache Ar volume Op Ar setting Oo Ar value Oc Op ... .Nm .Op Fl u Ar unit .Cm name Ar volume Ar name .Nm .Op Fl u Ar unit .Cm volume progress Ar volume .Nm .Op Fl u Ar unit .Cm clear .Nm .Op Fl u Ar unit .Cm create Ar type .Op Fl v .Op Fl s Ar stripe_size .Ar drive Ns Op \&, Ns Ar drive Ns Op ",..." .Op Ar drive Ns Op \&, Ns Ar drive Ns Op ",..." .Nm .Op Fl u Ar unit .Cm delete Ar volume .Nm .Op Fl u Ar unit .Cm add Ar drive Op Ar volume .Nm .Op Fl u Ar unit .Cm remove Ar drive .Nm .Op Fl u Ar unit .Cm start patrol .Nm .Op Fl u Ar unit .Cm stop patrol .Nm .Op Fl u Ar unit .Cm patrol Ar command Op Ar interval Op Ar start .Nm .Op Fl u Ar unit .Cm foreign scan .Nm .Op Fl u Ar unit .Cm foreign clear Op Ar config .Nm .Op Fl u Ar unit .Cm foreign diag Op Ar config .Nm .Op Fl u Ar unit .Cm foreign preview Op Ar config .Nm .Op Fl u Ar unit .Cm foreign import Op Ar config .Nm .Op Fl u Ar unit .Cm flash Ar file .Nm .Op Fl u Ar unit .Cm start learn .Nm .Op Fl u Ar unit .Cm bbu Ar setting Ar value .Nm .Op Fl u Ar unit .Cm ctrlprop Ar rebuild Op Ar rate .Nm .Op Fl u Ar unit .Cm ctrlprop Ar alarm Op Ar 0/1 .Sh DESCRIPTION The .Nm utility can be used to display or modify various parameters on LSI MegaRAID SAS RAID controllers. Each invocation of .Nm consists of zero or more global options followed by a command. Commands may support additional optional or required arguments after the command. .Pp Currently one global option is supported: .Bl -tag -width indent .It Fl u Ar unit .Ar unit specifies the unit of the controller to work with. If no unit is specified, then unit 0 is used. .El .Pp Various commands accept either or both of the two options: .Bl -tag -width indent .It Fl d Print numeric device IDs as drive identifier. This is the default. Useful in combination with .Fl e to print both, numeric device IDs and enclosure:slot information. .It Fl e Print drive identifiers in enclosure:slot form. See next paragraph on format details in context of input rather than output. .El .Pp Drives may be specified in two forms. First, a drive may be identified by its device ID. The device ID for configured drives can be found in .Cm show config . Second, a drive may be identified by its location as .Sm off .Op E Ar xx Ns \&: .Li S Ns Ar yy .Sm on where .Ar xx is the enclosure and .Ar yy is the slot for each drive as displayed in .Cm show drives . .Pp Volumes may be specified in two forms. First, a volume may be identified by its target ID. Second, on the volume may be specified by the corresponding .Em mfidX device, such as .Em mfid0 . .Pp The .Nm utility supports several different groups of commands. The first group of commands provide information about the controller, the volumes it manages, and the drives it controls. The second group of commands are used to manage the physical drives attached to the controller. The third group of commands are used to manage the logical volumes managed by the controller. The fourth group of commands are used to manage the drive configuration for the controller. The fifth group of commands are used to manage controller-wide operations. .Pp The informational commands include: .Bl -tag -width indent .It Cm version Displays the version of .Nm . .It Cm show adapter Displays information about the RAID controller such as the model number. .It Cm show battery Displays information about the battery from the battery backup unit. .It Cm show config Displays the volume and drive configuration for the controller. Each array is listed along with the physical drives the array is built from. Each volume is listed along with the arrays that the volume spans. If any hot spare drives are configured, then they are listed as well. .It Cm show drives Lists all of the physical drives attached to the controller. .It Xo Cm show events .Op Fl c Ar class .Op Fl l Ar locale .Op Fl n Ar count .Op Fl v .Op Ar start Op Ar stop .Xc Display entries from the controller's event log. The controller maintains a circular buffer of events. Each event is tagged with a class and locale. .Pp The .Ar class parameter limits the output to entries at the specified class or higher. The default class is .Dq warn . The available classes from lowest priority to highest are: .Bl -tag -width indent .It Cm debug Debug messages. .It Cm progress Periodic progress updates for long-running operations such as background initializations, array rebuilds, or patrol reads. .It Cm info Informational messages such as drive insertions and volume creations. .It Cm warn Indicates that some component may be close to failing. .It Cm crit A component has failed, but no data is lost. For example, a volume becoming degraded due to a drive failure. .It Cm fatal A component has failed resulting in data loss. .It Cm dead The controller itself has died. .El .Pp The .Ar locale parameter limits the output to entries for the specified part of the controller. The default locale is .Dq all . The available locales are .Dq volume , .Dq drive , .Dq enclosure , .Dq battery , .Dq sas , .Dq controller , .Dq config , .Dq cluster , and .Dq all . .Pp The .Ar count parameter is a debugging aid that specifies the number of events to fetch from the controller for each low-level request. The default is 15 events. .Pp By default, matching event log entries from the previous shutdown up to the present are displayed. This range can be adjusted via the .Ar start and .Ar stop parameters. Each of these parameters can either be specified as a log entry number or as one of the following aliases: .Bl -tag -width indent .It Cm newest The newest entry in the event log. .It Cm oldest The oldest entry in the event log. .It Cm clear The first entry since the event log was cleared. .It Cm shutdown The entry in the event log corresponding to the last time the controller was cleanly shut down. .It Cm boot The entry in the event log corresponding to the most recent boot. .El .It Cm show firmware Lists all of the firmware images present on the controller. .It Cm show foreign Displays detected foreign configurations on disks for importation or removal. .It Cm show logstate Display the various sequence numbers associated with the event log. .It Cm show patrol Display the status of the controller's patrol read operation. .It Cm show progress Report the current progress and estimated completion time for active operations on all volumes and drives. .It Cm show volumes Lists all of the logical volumes managed by the controller. .El .Pp The physical drive management commands include: .Bl -tag -width indent .It Cm fail Ar drive Mark .Ar drive as failed. .Ar Drive must be an online drive that is part of an array. .It Cm good Ar drive Mark .Ar drive as an unconfigured good drive. .Ar Drive must not be part of an existing array. .It Cm rebuild Ar drive Mark a failed .Ar drive that is still part of an array as a good drive suitable for a rebuild. The firmware should kick off an array rebuild on its own if a failed drive is marked as a rebuild drive. .It Cm syspd Ar drive Present the drive to the host operating system as a disk SYSPD block device in the format /dev/mfisyspdX. Clear this flag with .Cm good .Ar drive .It Cm drive progress Ar drive Report the current progress and estimated completion time of drive operations such as rebuilds or patrol reads. .It Cm drive clear Ar drive Brq "start | stop" Start or stop the writing of all 0x00 characters to a drive. .It Cm start rebuild Ar drive Manually start a rebuild on .Ar drive . .It Cm abort rebuild Ar drive Abort an in-progress rebuild operation on .Ar drive . It can be resumed with the .Cm start rebuild command. .It Cm locate Ar drive Brq "on | off" Change the state of the external LED associated with .Ar drive . .El .Pp The logical volume management commands include: .Bl -tag -width indent .It Cm cache Ar volume Op Ar setting Oo Ar value Oc Op ... If no .Ar setting arguments are supplied, then the current cache policy for .Ar volume is displayed; otherwise, the cache policy for .Ar volume is modified. One or more .Ar setting arguments may be given. Some settings take an additional .Ar value argument as noted below. The valid settings are: .Bl -tag -width indent .It Cm enable Enable caching for both read and write I/O operations. .It Cm disable Disable caching for both read and write I/O operations. .It Cm reads Enable caching only for read I/O operations. .It Cm writes Enable caching only for write I/O operations. .It Cm write-back Use write-back policy for cached writes. .It Cm write-through Use write-through policy for cached writes. .It Cm read-ahead Ar value Set the read ahead policy for cached reads. The .Ar value argument can be set to either .Dq none , .Dq adaptive , or .Dq always . .It Cm bad-bbu-write-cache Ar value Control the behavior of I/O write caching if the battery is dead or missing. The .Ar value argument can be set to either .Dq disable or .Dq enable . In general this setting should be left disabled to avoid data loss when the system loses power. .It Cm write-cache Ar value Control the write caches on the physical drives backing .Ar volume . The .Ar value argument can be set to either .Dq disable , .Dq enable , or .Dq default . .Pp In general this setting should be left disabled to avoid data loss when the physical drives lose power. The battery backup of the RAID controller does not save data in the write caches of the physical drives. .El .It Cm name Ar volume Ar name Sets the name of .Ar volume to .Ar name . .It Cm volume progress Ar volume Report the current progress and estimated completion time of volume operations such as consistency checks and initializations. .El .Pp The configuration commands include: .Bl -tag -width indent .It Cm clear Delete the entire configuration including all volumes, arrays, and spares. .It Xo Cm create Ar type .Op Fl v .Op Fl s Ar stripe_size .Ar drive Ns Op \&, Ns Ar drive Ns Op ",..." .Op Ar drive Ns Op \&, Ns Ar drive Ns Op ",..." .Xc Create a new volume. The .Ar type specifies the type of volume to create. Currently supported types include: .Bl -tag -width indent .It Cm jbod Creates a RAID0 volume for each drive specified. Each drive must be specified as a separate argument. .It Cm raid0 Creates one RAID0 volume spanning the drives listed in the single drive list. .It Cm raid1 Creates one RAID1 volume spanning the drives listed in the single drive list. .It Cm raid5 Creates one RAID5 volume spanning the drives listed in the single drive list. .It Cm raid6 Creates one RAID6 volume spanning the drives listed in the single drive list. .It Cm raid10 Creates one RAID10 volume spanning multiple RAID1 arrays. The drives for each RAID1 array are specified as a single drive list. .It Cm raid50 Creates one RAID50 volume spanning multiple RAID5 arrays. The drives for each RAID5 array are specified as a single drive list. .It Cm raid60 Creates one RAID60 volume spanning multiple RAID6 arrays. The drives for each RAID6 array are specified as a single drive list. .It Cm concat Creates a single volume by concatenating all of the drives in the single drive list. .El .Pp .Sy Note: Not all volume types are supported by all controllers. .Pp If the .Fl v flag is specified after .Ar type , then more verbose output will be enabled. Currently this just provides notification as drives are added to arrays and arrays to volumes when building the configuration. .Pp The .Fl s .Ar stripe_size parameter allows the stripe size of the array to be set. By default a stripe size of 64K is used. Valid values are 512 through 1M, though the MFI firmware may reject some values. .It Cm delete Ar volume Delete the volume .Ar volume . .It Cm add Ar drive Op Ar volume Mark .Ar drive as a hot spare. .Ar Drive must be in the unconfigured good state. If .Ar volume is specified, then the hot spare will be dedicated to arrays backing that volume. Otherwise, .Ar drive will be used as a global hot spare backing all arrays for this controller. Note that .Ar drive must be as large as the smallest drive in all of the arrays it is going to back. .It Cm remove Ar drive Remove the hot spare .Ar drive from service. It will be placed in the unconfigured good state. .El .Pp The controller management commands include: .Bl -tag -width indent .It Cm patrol Ar command Op Ar interval Op Ar start Set the patrol read operation mode. The .Ar command argument can be one of the following values: .Bl -tag -width indent .It Cm disable Disable patrol reads. .It Cm auto Enable periodic patrol reads initiated by the firmware. The optional .Ar interval argument specifies the interval in seconds between patrol reads. If patrol reads should be run continuously, then .Ar interval should consist of the word .Dq continuously . The optional .Ar start argument specifies a non-negative, relative start time for the next patrol read. If an interval or start time is not specified, then the existing setting will be used. .It Cm manual Enable manual patrol reads that are only initiated by the user. .El .It Cm start patrol Start a patrol read operation. .It Cm stop patrol Stop a currently running patrol read operation. .It Cm foreign scan Scan for foreign configurations and display the number found. The .Ar config argument for the commands below takes the form of a number from 0 to the total configurations found. .It Cm foreign clear Op config -Clear the specifed foreign +Clear the specified foreign .Ar config or all if no .Ar config argument is provided. .It Cm foreign diag Op config -Display a diagnostic display of the specifed foreign +Display a diagnostic display of the specified foreign .Ar config or all if no .Ar config argument is provided. .It Cm foreign preview Op config -Preview the specifed foreign +Preview the specified foreign .Ar config after import or all if no .Ar config argument is provided. .It Cm foreign import Op config -Import the specifed foreign +Import the specified foreign .Ar config or all if no .Ar config argument is provided. .It Cm flash Ar file Updates the flash on the controller with the firmware stored in .Ar file . A reboot is required for the new firmware to take effect. .It Cm start learn Start a battery relearn. Note that this seems to always result in the battery being completely drained, regardless of the BBU mode. In particular, the controller write cache will be disabled during the relearn even if transparent learning mode is enabled. .It Cm bbu Ar setting Ar value Update battery backup unit (BBU) properties related to battery relearning. The following settings are configurable: .Bl -tag -width indent .It Cm learn-delay Add a delay to the next scheduled battery relearn event. This setting is given in hours and must lie in the range of 0 to 255. .It Cm autolearn-mode Enable or disable automatic periodic battery relearning. The setting may be set to .Dq enable or .Dq disable to respectively enable or disable the relearn cycle. Alternatively, a mode of 0, 1 or 2 may be given. Mode 0 enables periodic relearning, mode 1 disables it, and mode 2 disables it and logs a warning to the event log when it detects that a battery relearn should be performed. .It Cm bbu-mode Set the BBU's mode of operation. This setting is not supported by all BBUs. Where it is supported, the possible values are the integers between 1 and 5 inclusive. Modes 1, 2 and 3 enable a transparent learn cycle, whereas modes 4 and 5 do not. The BBU's data retention time is greater when transparent learning is not used. .El .It Cm ctrlprop Ar rebuild Op Ar rate With no arguments display the rate of rebuild (percentage)a for volumes. With an integer argument (0-100), set that value as the new rebuild rate for volumes. .It Cm ctrlprop Ar alarm Op Ar 0/1 With no arguments display the current alarm enable/disable status. With a 0, disable alarms. With a 1, enable alarms. .El .Sh EXAMPLES Configure the cache for volume mfid0 to cache only writes: .Pp .Dl Nm Cm cache mfid0 writes .Dl Nm Cm cache mfid0 write-back .Pp Create a RAID5 array spanning the first four disks in the second enclosure: .Pp .Dl Nm Cm create raid5 e1:s0,e1:s1,e1:s2,e1:s4 .Pp Configure the first three disks on a controller as JBOD: .Pp .Dl Nm Cm create jbod 0 1 2 .Pp Create a RAID10 volume that spans two arrays each of which contains two disks from two different enclosures: .Pp .Dl Nm Cm create raid10 e1:s0,e1:s1 e2:s0,e2:s1 .Pp Add drive with the device ID of 4 as a global hot spare: .Pp .Dl Nm Cm add 4 .Pp Add the drive in slot 2 in the main chassis as a hot spare for volume mfid0: .Pp .Dl Nm Cm add s2 mfid0 .Pp Reconfigure a disk as a SYSPD block device with no RAID .Pp .Dl Nm Cm syspd 0 .Pp Configure the adapter to run periodic patrol reads once a week with the first patrol read starting in 5 minutes: .Pp .Dl Nm Cm patrol auto 604800 300 .Pp Display the second detected foreign configuration: .Pp .Dl Nm Cm show foreign 1 .Pp Set the current rebuild rate for volumes to 40%: .Dl Nm Cm ctrlprop rebuild 40 .Sh SEE ALSO .Xr mfi 4 .Sh HISTORY The .Nm utility first appeared in .Fx 8.0 . Index: head/usr.sbin/nandsim/nandsim.8 =================================================================== --- head/usr.sbin/nandsim/nandsim.8 (revision 289676) +++ head/usr.sbin/nandsim/nandsim.8 (revision 289677) @@ -1,229 +1,229 @@ .\" Copyright (c) 2010 Semihalf .\" 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. .\" .\" $FreeBSD$ .\" .Dd August 10, 2010 .Dt NANDSIM 8 .Os .Sh NAME .Nm nandsim .Nd NAND simulator control program .Sh SYNOPSIS .Nm .Ic status .Aq ctrl_no | Fl -all | Fl a .Op Fl v .Nm .Ic conf .Aq filename .Nm .Ic start .Aq ctrl_no .Nm .Ic mod .Aq ctrl_no:cs_no | Fl l Aq loglevel .Op Fl p Aq prog_time .Op Fl e Aq erase_time .Op Fl r Aq read_time .Op Fl E Aq error_ratio .Op Fl h .Nm .Ic stop .Aq ctrl_no .Nm .Ic error .Aq ctrl_no:cs_no .Aq page_num .Aq column .Aq length .Aq pattern .Nm .Ic bb .Aq ctrl_no:cs_no .Op blk_num,blk_num2,... .Op Fl U .Op Fl L .Nm .Ic freeze .Op ctrl_no .Nm .Ic log .Aq ctrl_no | Fl -all | Fl a .Nm .Ic stats .Aq ctrl_no:cs_no .Aq page_num .Nm .Ic dump .Aq ctrl_no:cs_no .Aq filename .Nm .Ic restore .Aq ctrl_no:chip_no .Aq filename .Nm .Ic destroy .Aq ctrl_no[:cs_no] | Fl -all | Fl a .Nm .Ic help .Op Fl v .Sh COMMAND DESCRIPTION Controllers and chips are arranged into a simple hierarchy. There can be up to 4 controllers configured, each with 4 chip select (CS) lines. A given chip is connected to one of the chip selects. .Pp Controllers are specified as .Aq ctrl_no ; chip selects are specified as .Aq cs_no . .Bl -tag -width periphlist .It Ic status Gets controller(s) status. If .Fl a or .Fl -all flag is specified - command will print status of every controller currently available. Optional flag .Fl v causes printing complete information about the controller, and all chips attached to it. .It Ic conf Reads simulator configuration from a specified file (this includes the simulation "layout" i.e. controllers-chips assignments). Configuration changes for an already started simulation require a full stop-start cycle in order to take effect i.e.: .Bl -column .It nandsim stop ... .It nandsim destroy ... .Pp .It << edit config file >> .Pp .It nandsim conf ... .It nandsim start ... .El .It Ic mod Alters simulator parameters on-the-fly. If controller number and CS pair is not specified, the general simulator parameters (not specific to a controller or a chip) will be modified. Changing chip's parameters requires specifying both controller number and CS to which the given chip is connected. Parameters which can be altered: .Pp General simulator related: .Bl -tag -width flag .It Fl l Aq log_level change logging level to .Aq log_level .El .Pp Chip related: .Bl -tag -width flag .It Fl p Aq prog_time change prog time for specified chip to .Aq prog_time .It Fl e Aq erase_time change erase time for specified chip to .Aq erase_time .It Fl r Aq read_time change read time for specified chip to .Aq read_time .It Fl E Aq error_ratio change error ratio for specified chip to .Aq error_ratio . Error ratio is a number of errors per million read/write bytes. .El .Pp Additionally, flag .Fl h will list parameters which can be altered. .El .Bl -tag -width periphlist .It Ic bb Marks/unmarks a specified block as bad. To mark/unmark the bad condition an a block, the following parameters have to be supplied: controller number, CS number, and at least one block number. It is possible to specify multiple blocks, by separating blocks numbers with a comma. The following options can be used for the 'bb' command: .Bl -tag -width flag .It Fl U unmark the bad previously marked block as bad. .It Fl L list all blocks marked as bad on a given chip. .El .It Ic log Prints activity log of the specified controller to stdout; if -controller number is not specifed, logs for all available +controller number is not specified, logs for all available controllers are printed. .It Ic stats Print statistics of the selected controller, chip and page. Statistics includes read count, write count, raw read count, raw write count, ECC stats (succeeded corrections, failed correction). .It Ic dump Dumps a snaphot of a single chip (including data and bad blocks information, wearout level) into the file. .It Ic restore Restores chip state from a dump-file snapshot (produced previously with the 'dump' command). .It Ic start Starts a controller i.e. the simulation. .It Ic stop Stops an already started controller; if the controller number is not supplied, attempts to stop all currently working controllers. .It Ic destroy Removes existing active chip/controller and its configuration from memory and releases the resources. Specifying flag .Fl a or .Fl -all causes removal of every chip and controller. Controller must be stopped in order to be destroyed. .It Ic error Directly overwrites a certain number of bytes in the specified page at a given offset with a supplied pattern (which mimics the corruption of flash contents). .It Ic help Prints synopsis, .Fl v gives more verbose output. .It Ic freeze Stops simulation of given controller (simulates power-loss). All commands issues to any chip on this controller are ignored. .El .Sh SEE ALSO .Xr nand 4 , .Xr nandsim 4 , .Xr nandsim.conf 5 .Sh HISTORY The .Nm utility first appeared in .Fx 10.0 . .Sh AUTHORS This utility was written by .An Lukasz Wojcik . Index: head/usr.sbin/nandsim/nandsim.c =================================================================== --- head/usr.sbin/nandsim/nandsim.c (revision 289676) +++ head/usr.sbin/nandsim/nandsim.c (revision 289677) @@ -1,1397 +1,1397 @@ /*- * Copyright (C) 2009-2012 Semihalf * 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 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. */ /* * Control application for the NAND simulator. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nandsim_cfgparse.h" #define SIMDEVICE "/dev/nandsim.ioctl" #define error(fmt, args...) do { \ printf("ERROR: " fmt "\n", ##args); } while (0) #define warn(fmt, args...) do { \ printf("WARNING: " fmt "\n", ##args); } while (0) #define DEBUG #undef DEBUG #ifdef DEBUG #define debug(fmt, args...) do { \ printf("NANDSIM_CONF:" fmt "\n", ##args); } while (0) #else #define debug(fmt, args...) do {} while(0) #endif #define NANDSIM_RAM_LOG_SIZE 16384 #define MSG_NOTRUNNING "Controller#%d is not running.Please start" \ " it first." #define MSG_RUNNING "Controller#%d is already running!" #define MSG_CTRLCHIPNEEDED "You have to specify ctrl_no:cs_no pair!" #define MSG_STATUSACQCTRLCHIP "Could not acquire status for ctrl#%d chip#%d" #define MSG_STATUSACQCTRL "Could not acquire status for ctrl#%d" #define MSG_NOCHIP "There is no such chip configured (chip#%d "\ "at ctrl#%d)!" #define MSG_NOCTRL "Controller#%d is not configured!" #define MSG_NOTCONFIGDCTRLCHIP "Chip connected to ctrl#%d at cs#%d " \ "is not configured." typedef int (commandfunc_t)(int , char **); static struct nandsim_command *getcommand(char *); static int parse_devstring(char *, int *, int *); static void printchip(struct sim_chip *, uint8_t); static void printctrl(struct sim_ctrl *); static int opendev(int *); static commandfunc_t cmdstatus; static commandfunc_t cmdconf; static commandfunc_t cmdstart; static commandfunc_t cmdstop; static commandfunc_t cmdmod; static commandfunc_t cmderror; static commandfunc_t cmdbb; static commandfunc_t cmdfreeze; static commandfunc_t cmdlog; static commandfunc_t cmdstats; static commandfunc_t cmddump; static commandfunc_t cmdrestore; static commandfunc_t cmddestroy; static commandfunc_t cmdhelp; static int checkusage(int, int, char **); static int is_chip_created(int, int, int *); static int is_ctrl_created(int, int *); static int is_ctrl_running(int, int *); static int assert_chip_connected(int , int); static int printstats(int, int, uint32_t, int); struct nandsim_command { const char *cmd_name; /* Command name */ commandfunc_t *commandfunc; /* Ptr to command function */ uint8_t req_argc; /* Mandatory arguments count */ const char *usagestring; /* Usage string */ }; static struct nandsim_command commands[] = { {"status", cmdstatus, 1, "status [-v]\n" }, {"conf", cmdconf, 1, "conf \n" }, {"start", cmdstart, 1, "start \n" }, {"mod", cmdmod, 2, "mod [-l ] | [-p ]\n" "\t[-e ] [-r ]\n" "\t[-E ] | [-h]\n" }, {"stop", cmdstop, 1, "stop \n" }, {"error", cmderror, 5, "error \n" }, {"bb", cmdbb, 2, "bb [blk_num1,blk_num2,..] [-U] [-L]\n" }, {"freeze", cmdfreeze, 1, "freeze [ctrl_no]\n" }, {"log", cmdlog, 1, "log \n" }, {"stats", cmdstats, 2, "stats \n" }, {"dump", cmddump, 2, "dump \n" }, {"restore", cmdrestore, 2, "restore \n" }, {"destroy", cmddestroy, 1, "destroy \n" }, {"help", cmdhelp, 0, "help [-v]" }, {NULL, NULL, 0, NULL}, }; /* Parse command name, and start appropriate function */ static struct nandsim_command* getcommand(char *arg) { struct nandsim_command *opts; for (opts = commands; (opts != NULL) && (opts->cmd_name != NULL); opts++) { if (strcmp(opts->cmd_name, arg) == 0) return (opts); } return (NULL); } /* * Parse given string in format :, if possible -- set * ctrl and/or cs, and return 0 (success) or 1 (in case of error). * * ctrl == 0xff && chip == 0xff : '--all' flag specified * ctrl != 0xff && chip != 0xff : both ctrl & chip were specified * ctrl != 0xff && chip == 0xff : only ctrl was specified */ static int parse_devstring(char *str, int *ctrl, int *cs) { char *tmpstr; unsigned int num = 0; /* Ignore white spaces at the beginning */ while (isspace(*str) && (*str != '\0')) str++; *ctrl = 0xff; *cs = 0xff; if (strcmp(str, "--all") == 0 || strcmp(str, "-a") == 0) { /* If --all or -a is specified, ctl==chip==0xff */ debug("CTRL=%d CHIP=%d\n", *ctrl, *cs); return (0); } /* Separate token and try to convert it to int */ tmpstr = (char *)strtok(str, ":"); if ((tmpstr != NULL) && (*tmpstr != '\0')) { if (convert_arguint(tmpstr, &num) != 0) return (1); if (num > MAX_SIM_DEV - 1) { error("Invalid ctrl_no supplied: %s. Valid ctrl_no " "value must lie between 0 and 3!", tmpstr); return (1); } *ctrl = num; tmpstr = (char *)strtok(NULL, ":"); if ((tmpstr != NULL) && (*tmpstr != '\0')) { if (convert_arguint(tmpstr, &num) != 0) return (1); /* Check if chip_no is valid */ if (num > MAX_CTRL_CS - 1) { error("Invalid chip_no supplied: %s. Valid " "chip_no value must lie between 0 and 3!", tmpstr); return (1); } *cs = num; } } else /* Empty devstring supplied */ return (1); debug("CTRL=%d CHIP=%d\n", *ctrl, *cs); return (0); } static int opendev(int *fd) { *fd = open(SIMDEVICE, O_RDWR); if (*fd == -1) { error("Could not open simulator device file (%s)!", SIMDEVICE); return (EX_OSFILE); } return (EX_OK); } static int opencdev(int *cdevd, int ctrl, int chip) { char fname[255]; sprintf(fname, "/dev/nandsim%d.%d", ctrl, chip); *cdevd = open(fname, O_RDWR); if (*cdevd == -1) return (EX_NOINPUT); return (EX_OK); } /* * Check if given arguments count match requirements. If no, or * --help (-h) flag is specified -- return 1 (print usage) */ static int checkusage(int gargc, int argsreqd, char **gargv) { if (gargc < argsreqd + 2 || (gargc >= (argsreqd + 2) && (strcmp(gargv[1], "--help") == 0 || strcmp(gargv[1], "-h") == 0))) return (1); return (0); } static int cmdstatus(int gargc, char **gargv) { int chip = 0, ctl = 0, err = 0, fd, idx, idx2, start, stop; uint8_t verbose = 0; struct sim_ctrl ctrlconf; struct sim_chip chipconf; err = parse_devstring(gargv[2], &ctl, &chip); if (err) { return (EX_USAGE); } else if (ctl == 0xff) { /* Every controller */ start = 0; stop = MAX_SIM_DEV-1; } else { /* Specified controller only */ start = ctl; stop = ctl; } if (opendev(&fd) != EX_OK) return (EX_OSFILE); for (idx = 0; idx < gargc; idx ++) if (strcmp(gargv[idx], "-v") == 0 || strcmp(gargv[idx], "--verbose") == 0) verbose = 1; for (idx = start; idx <= stop; idx++) { ctrlconf.num = idx; err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrlconf); if (err) { err = EX_SOFTWARE; error(MSG_STATUSACQCTRL, idx); continue; } printctrl(&ctrlconf); for (idx2 = 0; idx2 < MAX_CTRL_CS; idx2++) { chipconf.num = idx2; chipconf.ctrl_num = idx; err = ioctl(fd, NANDSIM_STATUS_CHIP, &chipconf); if (err) { err = EX_SOFTWARE; error(MSG_STATUSACQCTRL, idx); continue; } printchip(&chipconf, verbose); } } close(fd); return (err); } static int cmdconf(int gargc __unused, char **gargv) { int err; err = parse_config(gargv[2], SIMDEVICE); if (err) return (EX_DATAERR); return (EX_OK); } static int cmdstart(int gargc __unused, char **gargv) { int chip = 0, ctl = 0, err = 0, fd, running, state; err = parse_devstring(gargv[2], &ctl, &chip); if (err) return (EX_USAGE); err = is_ctrl_created(ctl, &state); if (err) { return (EX_SOFTWARE); } else if (state == 0) { error(MSG_NOCTRL, ctl); return (EX_SOFTWARE); } err = is_ctrl_running(ctl, &running); if (err) return (EX_SOFTWARE); if (running) { warn(MSG_RUNNING, ctl); } else { if (opendev(&fd) != EX_OK) return (EX_OSFILE); err = ioctl(fd, NANDSIM_START_CTRL, &ctl); close(fd); if (err) { error("Cannot start controller#%d", ctl); err = EX_SOFTWARE; } } return (err); } static int cmdstop(int gargc __unused, char **gargv) { int chip = 0, ctl = 0, err = 0, fd, running; err = parse_devstring(gargv[2], &ctl, &chip); if (err) return (EX_USAGE); err = is_ctrl_running(ctl, &running); if (err) return (EX_SOFTWARE); if (!running) { error(MSG_NOTRUNNING, ctl); } else { if (opendev(&fd) != EX_OK) return (EX_OSFILE); err = ioctl(fd, NANDSIM_STOP_CTRL, &ctl); close(fd); if (err) { error("Cannot stop controller#%d", ctl); err = EX_SOFTWARE; } } return (err); } static int cmdmod(int gargc __unused, char **gargv) { int chip, ctl, err = 0, fd = -1, i; struct sim_mod mods; if (gargc >= 4) { if (strcmp(gargv[2], "--loglevel") == 0 || strcmp(gargv[2], "-l") == 0) { - /* Set loglevel (ctrl:chip pair independant) */ + /* Set loglevel (ctrl:chip pair independent) */ mods.field = SIM_MOD_LOG_LEVEL; if (convert_arguint(gargv[3], &mods.new_value) != 0) return (EX_SOFTWARE); if (opendev(&fd) != EX_OK) return (EX_OSFILE); err = ioctl(fd, NANDSIM_MODIFY, &mods); if (err) { error("simulator parameter %s could not be " "modified !", gargv[3]); close(fd); return (EX_SOFTWARE); } debug("request : loglevel = %d\n", mods.new_value); close(fd); return (EX_OK); } } err = parse_devstring(gargv[2], &ctl, &chip); if (err) return (EX_USAGE); else if (chip == 0xff) { error(MSG_CTRLCHIPNEEDED); return (EX_USAGE); } if (!assert_chip_connected(ctl, chip)) return (EX_SOFTWARE); if (opendev(&fd) != EX_OK) return (EX_OSFILE); /* Find out which flags were passed */ for (i = 3; i < gargc; i++) { if (convert_arguint(gargv[i + 1], &mods.new_value) != 0) continue; if (strcmp(gargv[i], "--prog-time") == 0 || strcmp(gargv[i], "-p") == 0) { mods.field = SIM_MOD_PROG_TIME; debug("request : progtime = %d\n", mods.new_value); } else if (strcmp(gargv[i], "--erase-time") == 0 || strcmp(gargv[i], "-e") == 0) { mods.field = SIM_MOD_ERASE_TIME; debug("request : eraseime = %d\n", mods.new_value); } else if (strcmp(gargv[i], "--read-time") == 0 || strcmp(gargv[i], "-r") == 0) { mods.field = SIM_MOD_READ_TIME; debug("request : read_time = %d\n", mods.new_value); } else if (strcmp(gargv[i], "--error-ratio") == 0 || strcmp(gargv[i], "-E") == 0) { mods.field = SIM_MOD_ERROR_RATIO; debug("request : error_ratio = %d\n", mods.new_value); } else { /* Flag not recognized, or nothing specified. */ error("Unrecognized flag:%s\n", gargv[i]); if (fd >= 0) close(fd); return (EX_USAGE); } mods.chip_num = chip; mods.ctrl_num = ctl; /* Call appropriate ioctl */ err = ioctl(fd, NANDSIM_MODIFY, &mods); if (err) { error("simulator parameter %s could not be modified! ", gargv[i]); continue; } i++; } close(fd); return (EX_OK); } static int cmderror(int gargc __unused, char **gargv) { uint32_t page, column, len, pattern; int chip = 0, ctl = 0, err = 0, fd; struct sim_error sim_err; err = parse_devstring(gargv[2], &ctl, &chip); if (err) return (EX_USAGE); if (chip == 0xff) { error(MSG_CTRLCHIPNEEDED); return (EX_USAGE); } if (convert_arguint(gargv[3], &page) || convert_arguint(gargv[4], &column) || convert_arguint(gargv[5], &len) || convert_arguint(gargv[6], &pattern)) return (EX_SOFTWARE); if (!assert_chip_connected(ctl, chip)) return (EX_SOFTWARE); sim_err.page_num = page; sim_err.column = column; sim_err.len = len; sim_err.pattern = pattern; sim_err.ctrl_num = ctl; sim_err.chip_num = chip; if (opendev(&fd) != EX_OK) return (EX_OSFILE); err = ioctl(fd, NANDSIM_INJECT_ERROR, &sim_err); close(fd); if (err) { error("Could not inject error !"); return (EX_SOFTWARE); } return (EX_OK); } static int cmdbb(int gargc, char **gargv) { struct sim_block_state bs; struct chip_param_io cparams; uint32_t blkidx; int c, cdevd, chip = 0, ctl = 0, err = 0, fd, idx; uint8_t flagL = 0, flagU = 0; int *badblocks = NULL; /* Check for --list/-L or --unmark/-U flags */ for (idx = 3; idx < gargc; idx++) { if (strcmp(gargv[idx], "--list") == 0 || strcmp(gargv[idx], "-L") == 0) flagL = idx; if (strcmp(gargv[idx], "--unmark") == 0 || strcmp(gargv[idx], "-U") == 0) flagU = idx; } if (flagL == 2 || flagU == 2 || flagU == 3) return (EX_USAGE); err = parse_devstring(gargv[2], &ctl, &chip); if (err) { return (EX_USAGE); } if (chip == 0xff || ctl == 0xff) { error(MSG_CTRLCHIPNEEDED); return (EX_USAGE); } bs.ctrl_num = ctl; bs.chip_num = chip; if (!assert_chip_connected(ctl, chip)) return (EX_SOFTWARE); if (opencdev(&cdevd, ctl, chip) != EX_OK) return (EX_OSFILE); err = ioctl(cdevd, NAND_IO_GET_CHIP_PARAM, &cparams); if (err) return (EX_SOFTWARE); close(cdevd); bs.ctrl_num = ctl; bs.chip_num = chip; if (opendev(&fd) != EX_OK) return (EX_OSFILE); if (flagL != 3) { /* * Flag -L was specified either after blocklist or was not * specified at all. */ c = parse_intarray(gargv[3], &badblocks); for (idx = 0; idx < c; idx++) { bs.block_num = badblocks[idx]; /* Do not change wearout */ bs.wearout = -1; bs.state = (flagU == 0) ? NANDSIM_BAD_BLOCK : NANDSIM_GOOD_BLOCK; err = ioctl(fd, NANDSIM_SET_BLOCK_STATE, &bs); if (err) { error("Could not set bad block(%d) for " "controller (%d)!", badblocks[idx], ctl); err = EX_SOFTWARE; break; } } } if (flagL != 0) { /* If flag -L was specified (anywhere) */ for (blkidx = 0; blkidx < cparams.blocks; blkidx++) { bs.block_num = blkidx; /* Do not change the wearout */ bs.wearout = -1; err = ioctl(fd, NANDSIM_GET_BLOCK_STATE, &bs); if (err) { error("Could not acquire block state"); err = EX_SOFTWARE; continue; } printf("Block#%d: wear count: %d %s\n", blkidx, bs.wearout, (bs.state == NANDSIM_BAD_BLOCK) ? "BAD":"GOOD"); } } close(fd); return (err); } static int cmdfreeze(int gargc __unused, char **gargv) { int chip = 0, ctl = 0, err = 0, fd, i, start = 0, state, stop = 0; struct sim_ctrl_chip ctrlchip; err = parse_devstring(gargv[2], &ctl, &chip); if (err) return (EX_USAGE); if (ctl == 0xff) { error("You have to specify at least controller number"); return (EX_USAGE); } if (ctl != 0xff && chip == 0xff) { start = 0; stop = MAX_CTRL_CS - 1; } else { start = chip; stop = chip; } ctrlchip.ctrl_num = ctl; err = is_ctrl_running(ctl, &state); if (err) return (EX_SOFTWARE); if (state == 0) { error(MSG_NOTRUNNING, ctl); return (EX_SOFTWARE); } if (opendev(&fd) != EX_OK) return (EX_OSFILE); for (i = start; i <= stop; i++) { err = is_chip_created(ctl, i, &state); if (err) return (EX_SOFTWARE); else if (state == 0) { continue; } ctrlchip.chip_num = i; err = ioctl(fd, NANDSIM_FREEZE, &ctrlchip); if (err) { error("Could not freeze ctrl#%d chip#%d", ctl, i); close(fd); return (EX_SOFTWARE); } } close(fd); return (EX_OK); } static int cmdlog(int gargc __unused, char **gargv) { struct sim_log log; int chip = 0, ctl = 0, err = 0, fd, idx, start = 0, stop = 0; char *logbuf; err = parse_devstring(gargv[2], &ctl, &chip); if (err) return (EX_USAGE); logbuf = (char *)malloc(sizeof(char) * NANDSIM_RAM_LOG_SIZE); if (logbuf == NULL) { error("Not enough memory to create log buffer"); return (EX_SOFTWARE); } memset(logbuf, 0, NANDSIM_RAM_LOG_SIZE); log.log = logbuf; log.len = NANDSIM_RAM_LOG_SIZE; if (ctl == 0xff) { start = 0; stop = MAX_SIM_DEV-1; } else { start = ctl; stop = ctl; } if (opendev(&fd) != EX_OK) { free(logbuf); return (EX_OSFILE); } /* Print logs for selected controller(s) */ for (idx = start; idx <= stop; idx++) { log.ctrl_num = idx; err = ioctl(fd, NANDSIM_PRINT_LOG, &log); if (err) { error("Could not get log for controller %d!", idx); continue; } printf("Logs for controller#%d:\n%s\n", idx, logbuf); } free(logbuf); close(fd); return (EX_OK); } static int cmdstats(int gargc __unused, char **gargv) { int cdevd, chip = 0, ctl = 0, err = 0; uint32_t pageno = 0; err = parse_devstring(gargv[2], &ctl, &chip); if (err) return (EX_USAGE); if (chip == 0xff) { error(MSG_CTRLCHIPNEEDED); return (EX_USAGE); } if (convert_arguint(gargv[3], &pageno) != 0) return (EX_USAGE); if (!assert_chip_connected(ctl, chip)) return (EX_SOFTWARE); if (opencdev(&cdevd, ctl, chip) != EX_OK) return (EX_OSFILE); err = printstats(ctl, chip, pageno, cdevd); if (err) { close(cdevd); return (EX_SOFTWARE); } close(cdevd); return (EX_OK); } static int cmddump(int gargc __unused, char **gargv) { struct sim_dump dump; struct sim_block_state bs; struct chip_param_io cparams; int chip = 0, ctl = 0, err = EX_OK, fd, dumpfd; uint32_t blkidx, bwritten = 0, totalwritten = 0; void *buf; err = parse_devstring(gargv[2], &ctl, &chip); if (err) return (EX_USAGE); if (chip == 0xff || ctl == 0xff) { error(MSG_CTRLCHIPNEEDED); return (EX_USAGE); } if (!assert_chip_connected(ctl, chip)) return (EX_SOFTWARE); if (opencdev(&fd, ctl, chip) != EX_OK) return (EX_OSFILE); err = ioctl(fd, NAND_IO_GET_CHIP_PARAM, &cparams); if (err) { error("Cannot get parameters for chip %d:%d", ctl, chip); close(fd); return (EX_SOFTWARE); } close(fd); dump.ctrl_num = ctl; dump.chip_num = chip; dump.len = cparams.pages_per_block * (cparams.page_size + cparams.oob_size); buf = malloc(dump.len); if (buf == NULL) { error("Could not allocate memory!"); return (EX_SOFTWARE); } dump.data = buf; errno = 0; dumpfd = open(gargv[3], O_WRONLY | O_CREAT, 0666); if (dumpfd == -1) { error("Cannot create dump file."); free(buf); return (EX_SOFTWARE); } if (opendev(&fd)) { close(dumpfd); free(buf); return (EX_SOFTWARE); } bs.ctrl_num = ctl; bs.chip_num = chip; /* First uint32_t in file shall contain block count */ if (write(dumpfd, &cparams, sizeof(cparams)) < (int)sizeof(cparams)) { error("Error writing to dumpfile!"); close(fd); close(dumpfd); free(buf); return (EX_SOFTWARE); } /* * First loop acquires blocks states and writes them to * the dump file. */ for (blkidx = 0; blkidx < cparams.blocks; blkidx++) { bs.block_num = blkidx; err = ioctl(fd, NANDSIM_GET_BLOCK_STATE, &bs); if (err) { error("Could not get bad block(%d) for " "controller (%d)!", blkidx, ctl); close(fd); close(dumpfd); free(buf); return (EX_SOFTWARE); } bwritten = write(dumpfd, &bs, sizeof(bs)); if (bwritten != sizeof(bs)) { error("Error writing to dumpfile"); close(fd); close(dumpfd); free(buf); return (EX_SOFTWARE); } } /* Second loop dumps the data */ for (blkidx = 0; blkidx < cparams.blocks; blkidx++) { debug("Block#%d...", blkidx); dump.block_num = blkidx; err = ioctl(fd, NANDSIM_DUMP, &dump); if (err) { error("Could not dump ctrl#%d chip#%d " "block#%d", ctl, chip, blkidx); err = EX_SOFTWARE; break; } bwritten = write(dumpfd, dump.data, dump.len); if (bwritten != dump.len) { error("Error writing to dumpfile"); err = EX_SOFTWARE; break; } debug("OK!\n"); totalwritten += bwritten; } printf("%d out of %d B written.\n", totalwritten, dump.len * blkidx); close(fd); close(dumpfd); free(buf); return (err); } static int cmdrestore(int gargc __unused, char **gargv) { struct sim_dump dump; struct sim_block_state bs; struct stat filestat; int chip = 0, ctl = 0, err = 0, fd, dumpfd = -1; uint32_t blkidx, blksz, fsize = 0, expfilesz; void *buf; struct chip_param_io cparams, dumpcparams; err = parse_devstring(gargv[2], &ctl, &chip); if (err) return (EX_USAGE); else if (ctl == 0xff) { error(MSG_CTRLCHIPNEEDED); return (EX_USAGE); } if (!assert_chip_connected(ctl, chip)) return (EX_SOFTWARE); /* Get chip geometry */ if (opencdev(&fd, ctl, chip) != EX_OK) return (EX_OSFILE); err = ioctl(fd, NAND_IO_GET_CHIP_PARAM, &cparams); if (err) { error("Cannot get parameters for chip %d:%d", ctl, chip); close(fd); return (err); } close(fd); /* Obtain dump file size */ errno = 0; if (stat(gargv[3], &filestat) != 0) { error("Could not acquire file size! : %s", strerror(errno)); return (EX_IOERR); } fsize = filestat.st_size; blksz = cparams.pages_per_block * (cparams.page_size + cparams.oob_size); /* Expected dump file size for chip */ expfilesz = cparams.blocks * (blksz + sizeof(bs)) + sizeof(cparams); if (fsize != expfilesz) { error("File size does not match chip geometry (file size: %d" ", dump size: %d)", fsize, expfilesz); return (EX_SOFTWARE); } dumpfd = open(gargv[3], O_RDONLY); if (dumpfd == -1) { error("Could not open dump file!"); return (EX_IOERR); } /* Read chip params saved in dumpfile */ read(dumpfd, &dumpcparams, sizeof(dumpcparams)); /* XXX */ if (bcmp(&dumpcparams, &cparams, sizeof(cparams)) != 0) { error("Supplied dump is created for a chip with different " "chip configuration!"); close(dumpfd); return (EX_SOFTWARE); } if (opendev(&fd) != EX_OK) { close(dumpfd); return (EX_OSFILE); } buf = malloc(blksz); if (buf == NULL) { error("Could not allocate memory for block buffer"); close(dumpfd); close(fd); return (EX_SOFTWARE); } dump.ctrl_num = ctl; dump.chip_num = chip; dump.data = buf; /* Restore block states and wearouts */ for (blkidx = 0; blkidx < cparams.blocks; blkidx++) { dump.block_num = blkidx; if (read(dumpfd, &bs, sizeof(bs)) != sizeof(bs)) { error("Error reading dumpfile"); close(dumpfd); close(fd); free(buf); return (EX_SOFTWARE); } bs.ctrl_num = ctl; bs.chip_num = chip; debug("BLKIDX=%d BLOCKS=%d CTRL=%d CHIP=%d STATE=%d\n" "WEAROUT=%d BS.CTRL_NUM=%d BS.CHIP_NUM=%d\n", blkidx, cparams.blocks, dump.ctrl_num, dump.chip_num, bs.state, bs.wearout, bs.ctrl_num, bs.chip_num); err = ioctl(fd, NANDSIM_SET_BLOCK_STATE, &bs); if (err) { error("Could not set bad block(%d) for " "controller: %d, chip: %d!", blkidx, ctl, chip); close(dumpfd); close(fd); free(buf); return (EX_SOFTWARE); } } /* Restore data */ for (blkidx = 0; blkidx < cparams.blocks; blkidx++) { errno = 0; dump.len = read(dumpfd, buf, blksz); if (errno) { error("Failed to read block#%d from dumpfile.", blkidx); err = EX_SOFTWARE; break; } dump.block_num = blkidx; err = ioctl(fd, NANDSIM_RESTORE, &dump); if (err) { error("Could not restore block#%d of ctrl#%d chip#%d" ": %s", blkidx, ctl, chip, strerror(errno)); err = EX_SOFTWARE; break; } } free(buf); close(dumpfd); close(fd); return (err); } static int cmddestroy(int gargc __unused, char **gargv) { int chip = 0, ctl = 0, err = 0, fd, idx, idx2, state; int chipstart, chipstop, ctrlstart, ctrlstop; struct sim_chip_destroy chip_destroy; err = parse_devstring(gargv[2], &ctl, &chip); if (err) return (EX_USAGE); if (ctl == 0xff) { /* Every chip at every controller */ ctrlstart = chipstart = 0; ctrlstop = MAX_SIM_DEV - 1; chipstop = MAX_CTRL_CS - 1; } else { ctrlstart = ctrlstop = ctl; if (chip == 0xff) { /* Every chip at selected controller */ chipstart = 0; chipstop = MAX_CTRL_CS - 1; } else /* Selected chip at selected controller */ chipstart = chipstop = chip; } debug("CTRLSTART=%d CTRLSTOP=%d CHIPSTART=%d CHIPSTOP=%d\n", ctrlstart, ctrlstop, chipstart, chipstop); for (idx = ctrlstart; idx <= ctrlstop; idx++) { err = is_ctrl_created(idx, &state); if (err) { error("Could not acquire ctrl#%d state. Cannot " "destroy controller.", idx); return (EX_SOFTWARE); } if (state == 0) { continue; } err = is_ctrl_running(idx, &state); if (err) { error(MSG_STATUSACQCTRL, idx); return (EX_SOFTWARE); } if (state != 0) { error(MSG_RUNNING, ctl); return (EX_SOFTWARE); } if (opendev(&fd) != EX_OK) return (EX_OSFILE); for (idx2 = chipstart; idx2 <= chipstop; idx2++) { err = is_chip_created(idx, idx2, &state); if (err) { error(MSG_STATUSACQCTRLCHIP, idx2, idx); continue; } if (state == 0) /* There is no such chip running */ continue; chip_destroy.ctrl_num = idx; chip_destroy.chip_num = idx2; ioctl(fd, NANDSIM_DESTROY_CHIP, &chip_destroy); } /* If chip isn't explicitly specified -- destroy ctrl */ if (chip == 0xff) { err = ioctl(fd, NANDSIM_DESTROY_CTRL, &idx); if (err) { error("Could not destroy ctrl#%d", idx); continue; } } close(fd); } return (err); } int main(int argc, char **argv) { struct nandsim_command *cmdopts; int retcode = 0; if (argc < 2) { cmdhelp(argc, argv); retcode = EX_USAGE; } else { cmdopts = getcommand(argv[1]); if (cmdopts != NULL && cmdopts->commandfunc != NULL) { if (checkusage(argc, cmdopts->req_argc, argv) == 1) { /* Print command specific usage */ printf("nandsim %s", cmdopts->usagestring); return (EX_USAGE); } retcode = cmdopts->commandfunc(argc, argv); if (retcode == EX_USAGE) { /* Print command-specific usage */ printf("nandsim %s", cmdopts->usagestring); } else if (retcode == EX_OSFILE) { error("Could not open device file"); } } else { error("Unknown command!"); retcode = EX_USAGE; } } return (retcode); } static int cmdhelp(int gargc __unused, char **gargv __unused) { struct nandsim_command *opts; printf("usage: nandsim [command params] [params]\n\n"); for (opts = commands; (opts != NULL) && (opts->cmd_name != NULL); opts++) printf("nandsim %s", opts->usagestring); printf("\n"); return (EX_OK); } static void printchip(struct sim_chip *chip, uint8_t verbose) { if (chip->created == 0) return; if (verbose > 0) { printf("\n[Chip info]\n"); printf("num= %d\nctrl_num=%d\ndevice_id=%02x" "\tmanufacturer_id=%02x\ndevice_model=%s\nmanufacturer=" "%s\ncol_addr_cycles=%d\nrow_addr_cycles=%d" "\npage_size=%d\noob_size=%d\npages_per_block=%d\n" "blocks_per_lun=%d\nluns=%d\n\nprog_time=%d\n" "erase_time=%d\nread_time=%d\n" "error_ratio=%d\nwear_level=%d\nwrite_protect=%c\n" "chip_width=%db\n", chip->num, chip->ctrl_num, chip->device_id, chip->manufact_id,chip->device_model, chip->manufacturer, chip->col_addr_cycles, chip->row_addr_cycles, chip->page_size, chip->oob_size, chip->pgs_per_blk, chip->blks_per_lun, chip->luns,chip->prog_time, chip->erase_time, chip->read_time, chip->error_ratio, chip->wear_level, (chip->is_wp == 0) ? 'N':'Y', chip->width); } else { printf("[Chip info]\n"); printf("\tnum=%d\n\tdevice_model=%s\n\tmanufacturer=%s\n" "\tpage_size=%d\n\twrite_protect=%s\n", chip->num, chip->device_model, chip->manufacturer, chip->page_size, (chip->is_wp == 0) ? "NO":"YES"); } } static void printctrl(struct sim_ctrl *ctrl) { int i; if (ctrl->created == 0) { printf(MSG_NOCTRL "\n", ctrl->num); return; } printf("\n[Controller info]\n"); printf("\trunning: %s\n", ctrl->running ? "yes" : "no"); printf("\tnum cs: %d\n", ctrl->num_cs); printf("\tecc: %d\n", ctrl->ecc); printf("\tlog_filename: %s\n", ctrl->filename); printf("\tecc_layout:"); for (i = 0; i < MAX_ECC_BYTES; i++) { if (ctrl->ecc_layout[i] == 0xffff) break; else printf("%c%d", i%16 ? ' ' : '\n', ctrl->ecc_layout[i]); } printf("\n"); } static int is_ctrl_running(int ctrl_no, int *running) { struct sim_ctrl ctrl; int err, fd; ctrl.num = ctrl_no; if (opendev(&fd) != EX_OK) return (EX_OSFILE); err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrl); if (err) { error(MSG_STATUSACQCTRL, ctrl_no); close(fd); return (err); } *running = ctrl.running; close(fd); return (0); } static int is_ctrl_created(int ctrl_no, int *created) { struct sim_ctrl ctrl; int err, fd; ctrl.num = ctrl_no; if (opendev(&fd) != EX_OK) return (EX_OSFILE); err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrl); if (err) { error("Could not acquire conf for ctrl#%d", ctrl_no); close(fd); return (err); } *created = ctrl.created; close(fd); return (0); } static int is_chip_created(int ctrl_no, int chip_no, int *created) { struct sim_chip chip; int err, fd; chip.ctrl_num = ctrl_no; chip.num = chip_no; if (opendev(&fd) != EX_OK) return (EX_OSFILE); err = ioctl(fd, NANDSIM_STATUS_CHIP, &chip); if (err) { error("Could not acquire conf for chip#%d", chip_no); close(fd); return (err); } *created = chip.created; close(fd); return (0); } static int assert_chip_connected(int ctrl_no, int chip_no) { int created, running; if (is_ctrl_created(ctrl_no, &created)) return (0); if (!created) { error(MSG_NOCTRL, ctrl_no); return (0); } if (is_chip_created(ctrl_no, chip_no, &created)) return (0); if (!created) { error(MSG_NOTCONFIGDCTRLCHIP, ctrl_no, chip_no); return (0); } if (is_ctrl_running(ctrl_no, &running)) return (0); if (!running) { error(MSG_NOTRUNNING, ctrl_no); return (0); } return (1); } static int printstats(int ctrlno, int chipno, uint32_t pageno, int cdevd) { struct page_stat_io pstats; struct block_stat_io bstats; struct chip_param_io cparams; uint32_t blkidx; int err; /* Gather information about chip */ err = ioctl(cdevd, NAND_IO_GET_CHIP_PARAM, &cparams); if (err) { error("Could not acquire chip info for chip attached to cs#" "%d, ctrl#%d", chipno, ctrlno); return (EX_SOFTWARE); } blkidx = (pageno / cparams.pages_per_block); bstats.block_num = blkidx; err = ioctl(cdevd, NAND_IO_BLOCK_STAT, &bstats); if (err) { error("Could not acquire block#%d statistics!", blkidx); return (ENXIO); } printf("Block #%d erased: %d\n", blkidx, bstats.block_erased); pstats.page_num = pageno; err = ioctl(cdevd, NAND_IO_PAGE_STAT, &pstats); if (err) { error("Could not acquire page statistics!"); return (ENXIO); } debug("BLOCKIDX = %d PAGENO (REL. TO BLK) = %d\n", blkidx, pstats.page_num); printf("Page#%d : reads:%d writes:%d \n\traw reads:%d raw writes:%d " "\n\tecc_succeeded:%d ecc_corrected:%d ecc_failed:%d\n", pstats.page_num, pstats.page_read, pstats.page_written, pstats.page_raw_read, pstats.page_raw_written, pstats.ecc_succeded, pstats.ecc_corrected, pstats.ecc_failed); return (0); } Index: head/usr.sbin/nandsim/nandsim_cfgparse.c =================================================================== --- head/usr.sbin/nandsim/nandsim_cfgparse.c (revision 289676) +++ head/usr.sbin/nandsim/nandsim_cfgparse.c (revision 289677) @@ -1,959 +1,959 @@ /*- * Copyright (C) 2009-2012 Semihalf * 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 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 #include "nandsim_cfgparse.h" #define warn(fmt, args...) do { \ printf("WARNING: " fmt "\n", ##args); } while (0) #define error(fmt, args...) do { \ printf("ERROR: " fmt "\n", ##args); } while (0) #define MSG_MANDATORYKEYMISSING "mandatory key \"%s\" value belonging to " \ "section \"%s\" is missing!\n" #define DEBUG #undef DEBUG #ifdef DEBUG #define debug(fmt, args...) do { \ printf("NANDSIM_CONF:" fmt "\n", ##args); } while (0) #else #define debug(fmt, args...) do {} while(0) #endif #define STRBUFSIZ 2000 /* Macros extracts type and type size */ #define TYPE(x) ((x) & 0xf8) #define SIZE(x) (((x) & 0x07)) /* Erase/Prog/Read time max and min values */ #define DELAYTIME_MIN 10000 #define DELAYTIME_MAX 10000000 /* Structure holding configuration for controller. */ static struct sim_ctrl ctrl_conf; /* Structure holding configuration for chip. */ static struct sim_chip chip_conf; static struct nandsim_key nandsim_ctrl_keys[] = { {"num_cs", 1, VALUE_UINT | SIZE_8, (void *)&ctrl_conf.num_cs, 0}, {"ctrl_num", 1, VALUE_UINT | SIZE_8, (void *)&ctrl_conf.num, 0}, {"ecc_layout", 1, VALUE_UINTARRAY | SIZE_16, (void *)&ctrl_conf.ecc_layout, MAX_ECC_BYTES}, {"filename", 0, VALUE_STRING, (void *)&ctrl_conf.filename, FILENAME_SIZE}, {"ecc", 0, VALUE_BOOL, (void *)&ctrl_conf.ecc, 0}, {NULL, 0, 0, NULL, 0}, }; static struct nandsim_key nandsim_chip_keys[] = { {"chip_cs", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.num, 0}, {"chip_ctrl", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.ctrl_num, 0}, {"device_id", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.device_id, 0}, {"manufacturer_id", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.manufact_id, 0}, {"model", 0, VALUE_STRING, (void *)&chip_conf.device_model, DEV_MODEL_STR_SIZE}, {"manufacturer", 0, VALUE_STRING, (void *)&chip_conf.manufacturer, MAN_STR_SIZE}, {"page_size", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.page_size, 0}, {"oob_size", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.oob_size, 0}, {"pages_per_block", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.pgs_per_blk, 0}, {"blocks_per_lun", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.blks_per_lun, 0}, {"luns", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.luns, 0}, {"column_addr_cycle", 1,VALUE_UINT | SIZE_8, (void *)&chip_conf.col_addr_cycles, 0}, {"row_addr_cycle", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.row_addr_cycles, 0}, {"program_time", 0, VALUE_UINT | SIZE_32, (void *)&chip_conf.prog_time, 0}, {"erase_time", 0, VALUE_UINT | SIZE_32, (void *)&chip_conf.erase_time, 0}, {"read_time", 0, VALUE_UINT | SIZE_32, (void *)&chip_conf.read_time, 0}, {"width", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.width, 0}, {"wear_out", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.wear_level, 0}, {"bad_block_map", 0, VALUE_UINTARRAY | SIZE_32, (void *)&chip_conf.bad_block_map, MAX_BAD_BLOCKS}, {NULL, 0, 0, NULL, 0}, }; static struct nandsim_section sections[] = { {"ctrl", (struct nandsim_key *)&nandsim_ctrl_keys}, {"chip", (struct nandsim_key *)&nandsim_chip_keys}, {NULL, NULL}, }; static uint8_t logoutputtoint(char *, int *); static uint8_t validate_chips(struct sim_chip *, int, struct sim_ctrl *, int); static uint8_t validate_ctrls(struct sim_ctrl *, int); static int configure_sim(const char *, struct rcfile *); static int create_ctrls(struct rcfile *, struct sim_ctrl **, int *); static int create_chips(struct rcfile *, struct sim_chip **, int *); static void destroy_ctrls(struct sim_ctrl *); static void destroy_chips(struct sim_chip *); static int validate_section_config(struct rcfile *, const char *, int); int convert_argint(char *arg, int *value) { if (arg == NULL || value == NULL) return (EINVAL); errno = 0; *value = (int)strtol(arg, NULL, 0); if (*value == 0 && errno != 0) { error("Cannot convert to number argument \'%s\'", arg); return (EINVAL); } return (0); } int convert_arguint(char *arg, unsigned int *value) { if (arg == NULL || value == NULL) return (EINVAL); errno = 0; *value = (unsigned int)strtol(arg, NULL, 0); if (*value == 0 && errno != 0) { error("Cannot convert to number argument \'%s\'", arg); return (EINVAL); } return (0); } /* Parse given ',' separated list of bytes into buffer. */ int parse_intarray(char *array, int **buffer) { char *tmp, *tmpstr, *origstr; unsigned int currbufp = 0, i; unsigned int count = 0, from = 0, to = 0; /* Remove square braces */ if (array[0] == '[') array ++; if (array[strlen(array)-1] == ']') array[strlen(array)-1] = ','; from = strlen(array); origstr = (char *)malloc(sizeof(char) * from); strcpy(origstr, array); tmpstr = (char *)strtok(array, ","); /* First loop checks for how big int array we need to allocate */ while (tmpstr != NULL) { errno = 0; if ((tmp = strchr(tmpstr, '-')) != NULL) { *tmp = ' '; if (convert_arguint(tmpstr, &from) || convert_arguint(tmp, &to)) { free(origstr); return (EINVAL); } count += to - from + 1; } else { if (convert_arguint(tmpstr, &from)) { free(origstr); return (EINVAL); } count++; } tmpstr = (char *)strtok(NULL, ","); } if (count == 0) goto out; /* Allocate buffer of ints */ tmpstr = (char *)strtok(origstr, ","); *buffer = malloc(count * sizeof(int)); /* Second loop is just inserting converted values into int array */ while (tmpstr != NULL) { errno = 0; if ((tmp = strchr(tmpstr, '-')) != NULL) { *tmp = ' '; from = strtol(tmpstr, NULL, 0); to = strtol(tmp, NULL, 0); tmpstr = strtok(NULL, ","); for (i = from; i <= to; i ++) (*buffer)[currbufp++] = i; continue; } errno = 0; from = (int)strtol(tmpstr, NULL, 0); (*buffer)[currbufp++] = from; tmpstr = (char *)strtok(NULL, ","); } out: free(origstr); return (count); } /* Convert logoutput strings literals into appropriate ints. */ static uint8_t logoutputtoint(char *logoutput, int *output) { int out; if (strcmp(logoutput, "file") == 0) out = NANDSIM_OUTPUT_FILE; else if (strcmp(logoutput, "console") == 0) out = NANDSIM_OUTPUT_CONSOLE; else if (strcmp(logoutput, "ram") == 0) out = NANDSIM_OUTPUT_RAM; else if (strcmp(logoutput, "none") == 0) out = NANDSIM_OUTPUT_NONE; else out = -1; *output = out; if (out == -1) return (EINVAL); else return (0); } static int configure_sim(const char *devfname, struct rcfile *f) { struct sim_param sim_conf; char buf[255]; int err, tmpv, fd; err = rc_getint(f, "sim", 0, "log_level", &tmpv); if (tmpv < 0 || tmpv > 255 || err) { error("Bad log level specified (%d)\n", tmpv); return (ENOTSUP); } else sim_conf.log_level = tmpv; rc_getstring(f, "sim", 0, "log_output", 255, (char *)&buf); tmpv = -1; err = logoutputtoint((char *)&buf, &tmpv); if (err) { error("Log output specified in config file does not seem to " "be valid (%s)!", (char *)&buf); return (ENOTSUP); } sim_conf.log_output = tmpv; fd = open(devfname, O_RDWR); if (fd == -1) { error("could not open simulator device file (%s)!", devfname); return (EX_OSFILE); } err = ioctl(fd, NANDSIM_SIM_PARAM, &sim_conf); if (err) { error("simulator parameters could not be modified: %s", strerror(errno)); close(fd); return (ENXIO); } close(fd); return (EX_OK); } static int create_ctrls(struct rcfile *f, struct sim_ctrl **ctrls, int *cnt) { int count, i; struct sim_ctrl *ctrlsptr; count = rc_getsectionscount(f, "ctrl"); if (count > MAX_SIM_DEV) { error("Too many CTRL sections specified(%d)", count); return (ENOTSUP); } else if (count == 0) { error("No ctrl sections specified"); return (ENOENT); } ctrlsptr = (struct sim_ctrl *)malloc(sizeof(struct sim_ctrl) * count); if (ctrlsptr == NULL) { error("Could not allocate memory for ctrl configuration"); return (ENOMEM); } for (i = 0; i < count; i++) { bzero((void *)&ctrl_conf, sizeof(ctrl_conf)); /* * ECC layout have to end up with 0xffff, so * we're filling buffer with 0xff. If ecc_layout is - * defined in config file, values will be overriden. + * defined in config file, values will be overridden. */ memset((void *)&ctrl_conf.ecc_layout, 0xff, sizeof(ctrl_conf.ecc_layout)); if (validate_section_config(f, "ctrl", i) != 0) { free(ctrlsptr); return (EINVAL); } if (parse_section(f, "ctrl", i) != 0) { free(ctrlsptr); return (EINVAL); } memcpy(&ctrlsptr[i], &ctrl_conf, sizeof(ctrl_conf)); /* Try to create ctrl with config parsed */ debug("NUM=%d\nNUM_CS=%d\nECC=%d\nFILENAME=%s\nECC_LAYOUT[0]" "=%d\nECC_LAYOUT[1]=%d\n\n", ctrlsptr[i].num, ctrlsptr[i].num_cs, ctrlsptr[i].ecc, ctrlsptr[i].filename, ctrlsptr[i].ecc_layout[0], ctrlsptr[i].ecc_layout[1]); } *cnt = count; *ctrls = ctrlsptr; return (0); } static void destroy_ctrls(struct sim_ctrl *ctrls) { free(ctrls); } static int create_chips(struct rcfile *f, struct sim_chip **chips, int *cnt) { struct sim_chip *chipsptr; int count, i; count = rc_getsectionscount(f, "chip"); if (count > (MAX_CTRL_CS * MAX_SIM_DEV)) { error("Too many chip sections specified(%d)", count); return (ENOTSUP); } else if (count == 0) { error("No chip sections specified"); return (ENOENT); } chipsptr = (struct sim_chip *)malloc(sizeof(struct sim_chip) * count); if (chipsptr == NULL) { error("Could not allocate memory for chip configuration"); return (ENOMEM); } for (i = 0; i < count; i++) { bzero((void *)&chip_conf, sizeof(chip_conf)); /* * Bad block map have to end up with 0xffff, so * we're filling array with 0xff. If bad block map is - * defined in config file, values will be overriden. + * defined in config file, values will be overridden. */ memset((void *)&chip_conf.bad_block_map, 0xff, sizeof(chip_conf.bad_block_map)); if (validate_section_config(f, "chip", i) != 0) { free(chipsptr); return (EINVAL); } if (parse_section(f, "chip", i) != 0) { free(chipsptr); return (EINVAL); } memcpy(&chipsptr[i], &chip_conf, sizeof(chip_conf)); /* Try to create chip with config parsed */ debug("CHIP:\nNUM=%d\nCTRL_NUM=%d\nDEVID=%d\nMANID=%d\n" "PAGE_SZ=%d\nOOBSZ=%d\nREAD_T=%d\nDEVMODEL=%s\n" "MAN=%s\nCOLADDRCYCLES=%d\nROWADDRCYCLES=%d\nCHWIDTH=%d\n" "PGS/BLK=%d\nBLK/LUN=%d\nLUNS=%d\nERR_RATIO=%d\n" "WEARLEVEL=%d\nISWP=%d\n\n\n\n", chipsptr[i].num, chipsptr[i].ctrl_num, chipsptr[i].device_id, chipsptr[i].manufact_id, chipsptr[i].page_size, chipsptr[i].oob_size, chipsptr[i].read_time, chipsptr[i].device_model, chipsptr[i].manufacturer, chipsptr[i].col_addr_cycles, chipsptr[i].row_addr_cycles, chipsptr[i].width, chipsptr[i].pgs_per_blk, chipsptr[i].blks_per_lun, chipsptr[i].luns, chipsptr[i].error_ratio, chipsptr[i].wear_level, chipsptr[i].is_wp); } *cnt = count; *chips = chipsptr; return (0); } static void destroy_chips(struct sim_chip *chips) { free(chips); } int parse_config(char *cfgfname, const char *devfname) { int err = 0, fd; unsigned int chipsectionscnt, ctrlsectionscnt, i; struct rcfile *f; struct sim_chip *chips; struct sim_ctrl *ctrls; err = rc_open(cfgfname, "r", &f); if (err) { error("could not open configuration file (%s)", cfgfname); return (EX_NOINPUT); } /* First, try to configure simulator itself. */ if (configure_sim(devfname, f) != EX_OK) { rc_close(f); return (EINVAL); } debug("SIM CONFIGURED!\n"); /* Then create controllers' configs */ if (create_ctrls(f, &ctrls, &ctrlsectionscnt) != 0) { rc_close(f); return (ENXIO); } debug("CTRLS CONFIG READ!\n"); /* Then create chips' configs */ if (create_chips(f, &chips, &chipsectionscnt) != 0) { destroy_ctrls(ctrls); rc_close(f); return (ENXIO); } debug("CHIPS CONFIG READ!\n"); if (validate_ctrls(ctrls, ctrlsectionscnt) != 0) { destroy_ctrls(ctrls); destroy_chips(chips); rc_close(f); return (EX_SOFTWARE); } if (validate_chips(chips, chipsectionscnt, ctrls, ctrlsectionscnt) != 0) { destroy_ctrls(ctrls); destroy_chips(chips); rc_close(f); return (EX_SOFTWARE); } /* Open device */ fd = open(devfname, O_RDWR); if (fd == -1) { error("could not open simulator device file (%s)!", devfname); rc_close(f); destroy_chips(chips); destroy_ctrls(ctrls); return (EX_OSFILE); } debug("SIM CONFIG STARTED!\n"); /* At this stage, both ctrls' and chips' configs should be valid */ for (i = 0; i < ctrlsectionscnt; i++) { err = ioctl(fd, NANDSIM_CREATE_CTRL, &ctrls[i]); if (err) { if (err == EEXIST) error("Controller#%d already created\n", ctrls[i].num); else if (err == EINVAL) error("Incorrect controler number (%d)\n", ctrls[i].num); else error("Could not created controller#%d\n", ctrls[i].num); /* Errors during controller creation stops parsing */ close(fd); rc_close(f); destroy_ctrls(ctrls); destroy_chips(chips); return (ENXIO); } debug("CTRL#%d CONFIG STARTED!\n", i); } for (i = 0; i < chipsectionscnt; i++) { err = ioctl(fd, NANDSIM_CREATE_CHIP, &chips[i]); if (err) { if (err == EEXIST) error("Chip#%d for controller#%d already " "created\n", chips[i].num, chips[i].ctrl_num); else if (err == EINVAL) error("Incorrect chip number (%d:%d)\n", chips[i].num, chips[i].ctrl_num); else error("Could not create chip (%d:%d)\n", chips[i].num, chips[i].ctrl_num); error("Could not start chip#%d\n", i); destroy_chips(chips); destroy_ctrls(ctrls); close(fd); rc_close(f); return (ENXIO); } } debug("CHIPS CONFIG STARTED!\n"); close(fd); rc_close(f); destroy_chips(chips); destroy_ctrls(ctrls); return (0); } /* * Function tries to get appropriate value for given key, convert it to * array of ints (of given size), and perform all the necessary checks and * conversions. */ static int get_argument_intarray(const char *sect_name, int sectno, struct nandsim_key *key, struct rcfile *f) { char strbuf[STRBUFSIZ]; int *intbuf; int getres; uint32_t cnt, i = 0; getres = rc_getstring(f, sect_name, sectno, key->keyname, STRBUFSIZ, (char *)&strbuf); if (getres != 0) { if (key->mandatory != 0) { error(MSG_MANDATORYKEYMISSING, key->keyname, sect_name); return (EINVAL); } else /* Non-mandatory key, not present -- skip */ return (0); } cnt = parse_intarray((char *)&strbuf, &intbuf); cnt = (cnt <= key->maxlength) ? cnt : key->maxlength; for (i = 0; i < cnt; i++) { if (SIZE(key->valuetype) == SIZE_8) *((uint8_t *)(key->field) + i) = (uint8_t)intbuf[i]; else if (SIZE(key->valuetype) == SIZE_16) *((uint16_t *)(key->field) + i) = (uint16_t)intbuf[i]; else *((uint32_t *)(key->field) + i) = (uint32_t)intbuf[i]; } free(intbuf); return (0); } /* * Function tries to get appropriate value for given key, convert it to * int of certain length. */ static int get_argument_int(const char *sect_name, int sectno, struct nandsim_key *key, struct rcfile *f) { int getres; uint32_t val; getres = rc_getint(f, sect_name, sectno, key->keyname, &val); if (getres != 0) { if (key->mandatory != 0) { error(MSG_MANDATORYKEYMISSING, key->keyname, sect_name); return (EINVAL); } else /* Non-mandatory key, not present -- skip */ return (0); } if (SIZE(key->valuetype) == SIZE_8) *(uint8_t *)(key->field) = (uint8_t)val; else if (SIZE(key->valuetype) == SIZE_16) *(uint16_t *)(key->field) = (uint16_t)val; else *(uint32_t *)(key->field) = (uint32_t)val; return (0); } /* Function tries to get string value for given key */ static int get_argument_string(const char *sect_name, int sectno, struct nandsim_key *key, struct rcfile *f) { char strbuf[STRBUFSIZ]; int getres; getres = rc_getstring(f, sect_name, sectno, key->keyname, STRBUFSIZ, strbuf); if (getres != 0) { if (key->mandatory != 0) { error(MSG_MANDATORYKEYMISSING, key->keyname, sect_name); return (1); } else /* Non-mandatory key, not present -- skip */ return (0); } strncpy(key->field, (char *)&strbuf, (size_t)(key->maxlength - 1)); return (0); } /* Function tries to get on/off value for given key */ static int get_argument_bool(const char *sect_name, int sectno, struct nandsim_key *key, struct rcfile *f) { int getres, val; getres = rc_getbool(f, sect_name, sectno, key->keyname, &val); if (getres != 0) { if (key->mandatory != 0) { error(MSG_MANDATORYKEYMISSING, key->keyname, sect_name); return (1); } else /* Non-mandatory key, not present -- skip */ return (0); } *(uint8_t *)key->field = (uint8_t)val; return (0); } int parse_section(struct rcfile *f, const char *sect_name, int sectno) { struct nandsim_key *key; struct nandsim_section *sect = (struct nandsim_section *)§ions; int getres = 0; while (1) { if (sect == NULL) return (EINVAL); if (strcmp(sect->name, sect_name) == 0) break; else sect++; } key = sect->keys; do { debug("->Section: %s, Key: %s, type: %d, size: %d", sect_name, key->keyname, TYPE(key->valuetype), SIZE(key->valuetype)/2); switch (TYPE(key->valuetype)) { case VALUE_UINT: /* Single int value */ getres = get_argument_int(sect_name, sectno, key, f); if (getres != 0) return (getres); break; case VALUE_UINTARRAY: /* Array of ints */ getres = get_argument_intarray(sect_name, sectno, key, f); if (getres != 0) return (getres); break; case VALUE_STRING: /* Array of chars */ getres = get_argument_string(sect_name, sectno, key, f); if (getres != 0) return (getres); break; case VALUE_BOOL: /* Boolean value (true/false/on/off/yes/no) */ getres = get_argument_bool(sect_name, sectno, key, f); if (getres != 0) return (getres); break; } } while ((++key)->keyname != NULL); return (0); } static uint8_t validate_chips(struct sim_chip *chips, int chipcnt, struct sim_ctrl *ctrls, int ctrlcnt) { int cchipcnt, i, width, j, id, max; cchipcnt = chipcnt; for (chipcnt -= 1; chipcnt >= 0; chipcnt--) { if (chips[chipcnt].num >= MAX_CTRL_CS) { error("chip no. too high (%d)!!\n", chips[chipcnt].num); return (EINVAL); } if (chips[chipcnt].ctrl_num >= MAX_SIM_DEV) { error("controller no. too high (%d)!!\n", chips[chipcnt].ctrl_num); return (EINVAL); } if (chips[chipcnt].width != 8 && chips[chipcnt].width != 16) { error("invalid width:%d for chip#%d", chips[chipcnt].width, chips[chipcnt].num); return (EINVAL); } /* Check if page size is > 512 and if its power of 2 */ if (chips[chipcnt].page_size < 512 || (chips[chipcnt].page_size & (chips[chipcnt].page_size - 1)) != 0) { error("invalid page size:%d for chip#%d at ctrl#%d!!" "\n", chips[chipcnt].page_size, chips[chipcnt].num, chips[chipcnt].ctrl_num); return (EINVAL); } /* Check if controller no. ctrl_num is configured */ for (i = 0, id = -1; i < ctrlcnt && id == -1; i++) if (ctrls[i].num == chips[chipcnt].ctrl_num) id = i; if (i == ctrlcnt && id == -1) { error("Missing configuration for controller %d" " (at least one chip is connected to it)", chips[chipcnt].ctrl_num); return (EINVAL); } else { /* * Controller is configured -> check oob_size * validity */ i = 0; max = ctrls[id].ecc_layout[0]; while (i < MAX_ECC_BYTES && ctrls[id].ecc_layout[i] != 0xffff) { if (ctrls[id].ecc_layout[i] > max) max = ctrls[id].ecc_layout[i]; i++; } if (chips[chipcnt].oob_size < (unsigned)i) { error("OOB size for chip#%d at ctrl#%d is " "smaller than ecc layout length!", chips[chipcnt].num, chips[chipcnt].ctrl_num); exit(EINVAL); } if (chips[chipcnt].oob_size < (unsigned)max) { error("OOB size for chip#%d at ctrl#%d is " "smaller than maximal ecc position in " "defined layout!", chips[chipcnt].num, chips[chipcnt].ctrl_num); exit(EINVAL); } } if ((chips[chipcnt].erase_time < DELAYTIME_MIN || chips[chipcnt].erase_time > DELAYTIME_MAX) && chips[chipcnt].erase_time != 0) { error("Invalid erase time value for chip#%d at " "ctrl#%d", chips[chipcnt].num, chips[chipcnt].ctrl_num); return (EINVAL); } if ((chips[chipcnt].prog_time < DELAYTIME_MIN || chips[chipcnt].prog_time > DELAYTIME_MAX) && chips[chipcnt].prog_time != 0) { error("Invalid prog time value for chip#%d at " "ctr#%d!", chips[chipcnt].num, chips[chipcnt].ctrl_num); return (EINVAL); } if ((chips[chipcnt].read_time < DELAYTIME_MIN || chips[chipcnt].read_time > DELAYTIME_MAX) && chips[chipcnt].read_time != 0) { error("Invalid read time value for chip#%d at " "ctrl#%d!", chips[chipcnt].num, chips[chipcnt].ctrl_num); return (EINVAL); } } /* Check if chips attached to the same controller, have same width */ for (i = 0; i < ctrlcnt; i++) { width = -1; for (j = 0; j < cchipcnt; j++) { if (chips[j].ctrl_num == i) { if (width == -1) { width = chips[j].width; } else { if (width != chips[j].width) { error("Chips attached to " "ctrl#%d have different " "widths!\n", i); return (EINVAL); } } } } } return (0); } static uint8_t validate_ctrls(struct sim_ctrl *ctrl, int ctrlcnt) { for (ctrlcnt -= 1; ctrlcnt >= 0; ctrlcnt--) { if (ctrl[ctrlcnt].num > MAX_SIM_DEV) { error("Controller no. too high (%d)!!\n", ctrl[ctrlcnt].num); return (EINVAL); } if (ctrl[ctrlcnt].num_cs > MAX_CTRL_CS) { error("Too many CS (%d)!!\n", ctrl[ctrlcnt].num_cs); return (EINVAL); } if (ctrl[ctrlcnt].ecc != 0 && ctrl[ctrlcnt].ecc != 1) { error("ECC is set to neither 0 nor 1 !\n"); return (EINVAL); } } return (0); } static int validate_section_config(struct rcfile *f, const char *sect_name, int sectno) { struct nandsim_key *key; struct nandsim_section *sect; char **keys_tbl; int i, match; for (match = 0, sect = (struct nandsim_section *)§ions; sect != NULL; sect++) { if (strcmp(sect->name, sect_name) == 0) { match = 1; break; } } if (match == 0) return (EINVAL); keys_tbl = rc_getkeys(f, sect_name, sectno); if (keys_tbl == NULL) return (ENOMEM); for (i = 0; keys_tbl[i] != NULL; i++) { key = sect->keys; match = 0; do { if (strcmp(keys_tbl[i], key->keyname) == 0) { match = 1; break; } } while ((++key)->keyname != NULL); if (match == 0) { error("Invalid key in config file: %s\n", keys_tbl[i]); free(keys_tbl); return (EINVAL); } } free(keys_tbl); return (0); } Index: head/usr.sbin/ndp/ndp.c =================================================================== --- head/usr.sbin/ndp/ndp.c (revision 289676) +++ head/usr.sbin/ndp/ndp.c (revision 289677) @@ -1,1381 +1,1381 @@ /* $FreeBSD$ */ /* $KAME: ndp.c,v 1.104 2003/06/27 07:48:39 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. * 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 project 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 PROJECT 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 PROJECT 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. */ /* * Copyright (c) 1984, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Sun Microsystems, Inc. * * 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. * 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. */ /* * Based on: * "@(#) Copyright (c) 1984, 1993\n\ * The Regents of the University of California. All rights reserved.\n"; * * "@(#)arp.c 8.2 (Berkeley) 1/2/94"; */ /* * ndp - display, set, delete and flush neighbor cache */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gmt2local.h" #define NEXTADDR(w, s) \ if (rtm->rtm_addrs & (w)) { \ bcopy((char *)&s, cp, sizeof(s)); cp += SA_SIZE(&s);} static pid_t pid; static int nflag; static int tflag; static int32_t thiszone; /* time difference with gmt */ static int s = -1; static int repeat = 0; static char host_buf[NI_MAXHOST]; /* getnameinfo() */ static char ifix_buf[IFNAMSIZ]; /* if_indextoname() */ static int file(char *); static void getsocket(void); static int set(int, char **); static void get(char *); static int delete(char *); static void dump(struct sockaddr_in6 *, int); static struct in6_nbrinfo *getnbrinfo(struct in6_addr *, int, int); static char *ether_str(struct sockaddr_dl *); static int ndp_ether_aton(char *, u_char *); static void usage(void); static int rtmsg(int); static void ifinfo(char *, int, char **); static void rtrlist(void); static void plist(void); static void pfx_flush(void); static void rtr_flush(void); static void harmonize_rtr(void); #ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */ static void getdefif(void); static void setdefif(char *); #endif static char *sec2str(time_t); static void ts_print(const struct timeval *); static char *rtpref_str[] = { "medium", /* 00 */ "high", /* 01 */ "rsv", /* 10 */ "low" /* 11 */ }; int main(int argc, char **argv) { int ch, mode = 0; char *arg = NULL; pid = getpid(); thiszone = gmt2local(0); while ((ch = getopt(argc, argv, "acd:f:Ii:nprstA:HPR")) != -1) switch (ch) { case 'a': case 'c': case 'p': case 'r': case 'H': case 'P': case 'R': case 's': case 'I': if (mode) { usage(); /*NOTREACHED*/ } mode = ch; arg = NULL; break; case 'f': exit(file(optarg) ? 1 : 0); case 'd': case 'i': if (mode) { usage(); /*NOTREACHED*/ } mode = ch; arg = optarg; break; case 'n': nflag = 1; break; case 't': tflag = 1; break; case 'A': if (mode) { usage(); /*NOTREACHED*/ } mode = 'a'; repeat = atoi(optarg); if (repeat < 0) { usage(); /*NOTREACHED*/ } break; default: usage(); } argc -= optind; argv += optind; switch (mode) { case 'a': case 'c': if (argc != 0) { usage(); /*NOTREACHED*/ } dump(0, mode == 'c'); break; case 'd': if (argc != 0) { usage(); /*NOTREACHED*/ } delete(arg); break; case 'I': #ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */ if (argc > 1) { usage(); /*NOTREACHED*/ } else if (argc == 1) { if (strcmp(*argv, "delete") == 0 || if_nametoindex(*argv)) setdefif(*argv); else errx(1, "invalid interface %s", *argv); } getdefif(); /* always call it to print the result */ break; #else errx(1, "not supported yet"); /*NOTREACHED*/ #endif case 'p': if (argc != 0) { usage(); /*NOTREACHED*/ } plist(); break; case 'i': ifinfo(arg, argc, argv); break; case 'r': if (argc != 0) { usage(); /*NOTREACHED*/ } rtrlist(); break; case 's': if (argc < 2 || argc > 4) usage(); exit(set(argc, argv) ? 1 : 0); case 'H': if (argc != 0) { usage(); /*NOTREACHED*/ } harmonize_rtr(); break; case 'P': if (argc != 0) { usage(); /*NOTREACHED*/ } pfx_flush(); break; case 'R': if (argc != 0) { usage(); /*NOTREACHED*/ } rtr_flush(); break; case 0: if (argc != 1) { usage(); /*NOTREACHED*/ } get(argv[0]); break; } exit(0); } /* * Process a file to set standard ndp entries */ static int file(char *name) { FILE *fp; int i, retval; char line[100], arg[5][50], *args[5], *p; if ((fp = fopen(name, "r")) == NULL) err(1, "cannot open %s", name); args[0] = &arg[0][0]; args[1] = &arg[1][0]; args[2] = &arg[2][0]; args[3] = &arg[3][0]; args[4] = &arg[4][0]; retval = 0; while (fgets(line, sizeof(line), fp) != NULL) { if ((p = strchr(line, '#')) != NULL) *p = '\0'; for (p = line; isblank(*p); p++); if (*p == '\n' || *p == '\0') continue; i = sscanf(line, "%49s %49s %49s %49s %49s", arg[0], arg[1], arg[2], arg[3], arg[4]); if (i < 2) { warnx("bad line: %s", line); retval = 1; continue; } if (set(i, args)) retval = 1; } fclose(fp); return (retval); } static void getsocket() { if (s < 0) { s = socket(PF_ROUTE, SOCK_RAW, 0); if (s < 0) { err(1, "socket"); /* NOTREACHED */ } } } static struct sockaddr_in6 so_mask = { .sin6_len = sizeof(so_mask), .sin6_family = AF_INET6 }; static struct sockaddr_in6 blank_sin = { .sin6_len = sizeof(blank_sin), .sin6_family = AF_INET6 }; static struct sockaddr_in6 sin_m; static struct sockaddr_dl blank_sdl = { .sdl_len = sizeof(blank_sdl), .sdl_family = AF_LINK }; static struct sockaddr_dl sdl_m; static time_t expire_time; static int flags, found_entry; static struct { struct rt_msghdr m_rtm; char m_space[512]; } m_rtmsg; /* * Set an individual neighbor cache entry */ static int set(int argc, char **argv) { register struct sockaddr_in6 *sin = &sin_m; register struct sockaddr_dl *sdl; register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); struct addrinfo hints, *res; int gai_error; u_char *ea; char *host = argv[0], *eaddr = argv[1]; getsocket(); argc -= 2; argv += 2; sdl_m = blank_sdl; sin_m = blank_sin; bzero(&hints, sizeof(hints)); hints.ai_family = AF_INET6; gai_error = getaddrinfo(host, NULL, &hints, &res); if (gai_error) { fprintf(stderr, "ndp: %s: %s\n", host, gai_strerror(gai_error)); return 1; } sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; sin->sin6_scope_id = ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; ea = (u_char *)LLADDR(&sdl_m); if (ndp_ether_aton(eaddr, ea) == 0) sdl_m.sdl_alen = 6; flags = expire_time = 0; while (argc-- > 0) { if (strncmp(argv[0], "temp", 4) == 0) { struct timeval now; gettimeofday(&now, 0); expire_time = now.tv_sec + 20 * 60; } else if (strncmp(argv[0], "proxy", 5) == 0) flags |= RTF_ANNOUNCE; argv++; } if (rtmsg(RTM_GET) < 0) { errx(1, "RTM_GET(%s) failed", host); /* NOTREACHED */ } sin = (struct sockaddr_in6 *)(rtm + 1); sdl = (struct sockaddr_dl *)(ALIGN(sin->sin6_len) + (char *)sin); if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) { if (sdl->sdl_family == AF_LINK && !(rtm->rtm_flags & RTF_GATEWAY)) { switch (sdl->sdl_type) { case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023: case IFT_ISO88024: case IFT_ISO88025: case IFT_L2VLAN: case IFT_BRIDGE: goto overwrite; } } fprintf(stderr, "set: cannot configure a new entry\n"); return 1; } overwrite: if (sdl->sdl_family != AF_LINK) { printf("cannot intuit interface index and type for %s\n", host); return (1); } sdl_m.sdl_type = sdl->sdl_type; sdl_m.sdl_index = sdl->sdl_index; return (rtmsg(RTM_ADD)); } /* * Display an individual neighbor cache entry */ static void get(char *host) { struct sockaddr_in6 *sin = &sin_m; struct addrinfo hints, *res; int gai_error; sin_m = blank_sin; bzero(&hints, sizeof(hints)); hints.ai_family = AF_INET6; gai_error = getaddrinfo(host, NULL, &hints, &res); if (gai_error) { fprintf(stderr, "ndp: %s: %s\n", host, gai_strerror(gai_error)); return; } sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; sin->sin6_scope_id = ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; dump(sin, 0); if (found_entry == 0) { getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, sizeof(host_buf), NULL ,0, (nflag ? NI_NUMERICHOST : 0)); printf("%s (%s) -- no entry\n", host, host_buf); exit(1); } } /* * Delete a neighbor cache entry */ static int delete(char *host) { struct sockaddr_in6 *sin = &sin_m; register struct rt_msghdr *rtm = &m_rtmsg.m_rtm; register char *cp = m_rtmsg.m_space; struct sockaddr_dl *sdl; struct addrinfo hints, *res; int gai_error; getsocket(); sin_m = blank_sin; bzero(&hints, sizeof(hints)); hints.ai_family = AF_INET6; gai_error = getaddrinfo(host, NULL, &hints, &res); if (gai_error) { fprintf(stderr, "ndp: %s: %s\n", host, gai_strerror(gai_error)); return 1; } sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; sin->sin6_scope_id = ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; if (rtmsg(RTM_GET) < 0) { errx(1, "RTM_GET(%s) failed", host); /* NOTREACHED */ } sin = (struct sockaddr_in6 *)(rtm + 1); sdl = (struct sockaddr_dl *)(ALIGN(sin->sin6_len) + (char *)sin); if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) { if (sdl->sdl_family == AF_LINK && !(rtm->rtm_flags & RTF_GATEWAY)) { goto delete; } fprintf(stderr, "delete: cannot delete non-NDP entry\n"); return 1; } delete: if (sdl->sdl_family != AF_LINK) { printf("cannot locate %s\n", host); return (1); } /* * need to reinit the field because it has rt_key * but we want the actual address */ NEXTADDR(RTA_DST, sin_m); rtm->rtm_flags |= RTF_LLDATA; if (rtmsg(RTM_DELETE) == 0) { getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, sizeof(host_buf), NULL, 0, (nflag ? NI_NUMERICHOST : 0)); printf("%s (%s) deleted\n", host, host_buf); } return 0; } #define W_ADDR 36 #define W_LL 17 #define W_IF 6 /* * Dump the entire neighbor cache */ static void dump(struct sockaddr_in6 *addr, int cflag) { int mib[6]; size_t needed; char *lim, *buf, *next; struct rt_msghdr *rtm; struct sockaddr_in6 *sin; struct sockaddr_dl *sdl; extern int h_errno; struct in6_nbrinfo *nbi; struct timeval now; int addrwidth; int llwidth; int ifwidth; char flgbuf[8]; char *ifname; /* Print header */ if (!tflag && !cflag) printf("%-*.*s %-*.*s %*.*s %-9.9s %1s %5s\n", W_ADDR, W_ADDR, "Neighbor", W_LL, W_LL, "Linklayer Address", W_IF, W_IF, "Netif", "Expire", "S", "Flags"); again:; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET6; mib[4] = NET_RT_FLAGS; #ifdef RTF_LLINFO mib[5] = RTF_LLINFO; #else mib[5] = 0; #endif if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) err(1, "sysctl(PF_ROUTE estimate)"); if (needed > 0) { if ((buf = malloc(needed)) == NULL) err(1, "malloc"); if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)"); lim = buf + needed; } else buf = lim = NULL; for (next = buf; next && next < lim; next += rtm->rtm_msglen) { int isrouter = 0, prbs = 0; rtm = (struct rt_msghdr *)next; sin = (struct sockaddr_in6 *)(rtm + 1); sdl = (struct sockaddr_dl *)((char *)sin + ALIGN(sin->sin6_len)); /* * Some OSes can produce a route that has the LINK flag but * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD * and BSD/OS, where xx is not the interface identifier on * lo0). Such routes entry would annoy getnbrinfo() below, * so we skip them. * XXX: such routes should have the GATEWAY flag, not the * LINK flag. However, there is rotten routing software * that advertises all routes that have the GATEWAY flag. * Thus, KAME kernel intentionally does not set the LINK flag. * What is to be fixed is not ndp, but such routing software * (and the kernel workaround)... */ if (sdl->sdl_family != AF_LINK) continue; if (!(rtm->rtm_flags & RTF_HOST)) continue; if (addr) { if (IN6_ARE_ADDR_EQUAL(&addr->sin6_addr, &sin->sin6_addr) == 0 || addr->sin6_scope_id != sin->sin6_scope_id) continue; found_entry = 1; } else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr)) continue; if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&sin->sin6_addr)) { /* XXX: should scope id be filled in the kernel? */ if (sin->sin6_scope_id == 0) sin->sin6_scope_id = sdl->sdl_index; } getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, sizeof(host_buf), NULL, 0, (nflag ? NI_NUMERICHOST : 0)); if (cflag) { #ifdef RTF_WASCLONED if (rtm->rtm_flags & RTF_WASCLONED) delete(host_buf); #elif defined(RTF_CLONED) if (rtm->rtm_flags & RTF_CLONED) delete(host_buf); #else if (rtm->rtm_flags & RTF_PINNED) continue; delete(host_buf); #endif continue; } gettimeofday(&now, 0); if (tflag) ts_print(&now); addrwidth = strlen(host_buf); if (addrwidth < W_ADDR) addrwidth = W_ADDR; llwidth = strlen(ether_str(sdl)); if (W_ADDR + W_LL - addrwidth > llwidth) llwidth = W_ADDR + W_LL - addrwidth; ifname = if_indextoname(sdl->sdl_index, ifix_buf); if (!ifname) ifname = "?"; ifwidth = strlen(ifname); if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth) ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth; printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, host_buf, llwidth, llwidth, ether_str(sdl), ifwidth, ifwidth, ifname); - /* Print neighbor discovery specific informations */ + /* Print neighbor discovery specific information */ nbi = getnbrinfo(&sin->sin6_addr, sdl->sdl_index, 1); if (nbi) { if (nbi->expire > now.tv_sec) { printf(" %-9.9s", sec2str(nbi->expire - now.tv_sec)); } else if (nbi->expire == 0) printf(" %-9.9s", "permanent"); else printf(" %-9.9s", "expired"); switch (nbi->state) { case ND6_LLINFO_NOSTATE: printf(" N"); break; #ifdef ND6_LLINFO_WAITDELETE case ND6_LLINFO_WAITDELETE: printf(" W"); break; #endif case ND6_LLINFO_INCOMPLETE: printf(" I"); break; case ND6_LLINFO_REACHABLE: printf(" R"); break; case ND6_LLINFO_STALE: printf(" S"); break; case ND6_LLINFO_DELAY: printf(" D"); break; case ND6_LLINFO_PROBE: printf(" P"); break; default: printf(" ?"); break; } isrouter = nbi->isrouter; prbs = nbi->asked; } else { warnx("failed to get neighbor information"); printf(" "); } /* * other flags. R: router, P: proxy, W: ?? */ if ((rtm->rtm_addrs & RTA_NETMASK) == 0) { snprintf(flgbuf, sizeof(flgbuf), "%s%s", isrouter ? "R" : "", (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : ""); } else { #if 0 /* W and P are mystery even for us */ sin = (struct sockaddr_in6 *) (sdl->sdl_len + (char *)sdl); snprintf(flgbuf, sizeof(flgbuf), "%s%s%s%s", isrouter ? "R" : "", !IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr) ? "P" : "", (sin->sin6_len != sizeof(struct sockaddr_in6)) ? "W" : "", (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : ""); #else snprintf(flgbuf, sizeof(flgbuf), "%s%s", isrouter ? "R" : "", (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : ""); #endif } printf(" %s", flgbuf); if (prbs) printf(" %d", prbs); printf("\n"); } if (buf != NULL) free(buf); if (repeat) { printf("\n"); fflush(stdout); sleep(repeat); goto again; } } static struct in6_nbrinfo * getnbrinfo(struct in6_addr *addr, int ifindex, int warning) { static struct in6_nbrinfo nbi; int s; if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) err(1, "socket"); bzero(&nbi, sizeof(nbi)); if_indextoname(ifindex, nbi.ifname); nbi.addr = *addr; if (ioctl(s, SIOCGNBRINFO_IN6, (caddr_t)&nbi) < 0) { if (warning) warn("ioctl(SIOCGNBRINFO_IN6)"); close(s); return(NULL); } close(s); return(&nbi); } static char * ether_str(struct sockaddr_dl *sdl) { static char hbuf[NI_MAXHOST]; if (sdl->sdl_alen == ETHER_ADDR_LEN) { strlcpy(hbuf, ether_ntoa((struct ether_addr *)LLADDR(sdl)), sizeof(hbuf)); } else if (sdl->sdl_alen) { int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; snprintf(hbuf, sizeof(hbuf), "%s", link_ntoa(sdl) + n); } else snprintf(hbuf, sizeof(hbuf), "(incomplete)"); return(hbuf); } static int ndp_ether_aton(char *a, u_char *n) { int i, o[6]; i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2], &o[3], &o[4], &o[5]); if (i != 6) { fprintf(stderr, "ndp: invalid Ethernet address '%s'\n", a); return (1); } for (i = 0; i < 6; i++) n[i] = o[i]; return (0); } static void usage() { printf("usage: ndp [-nt] hostname\n"); printf(" ndp [-nt] -a | -c | -p | -r | -H | -P | -R\n"); printf(" ndp [-nt] -A wait\n"); printf(" ndp [-nt] -d hostname\n"); printf(" ndp [-nt] -f filename\n"); printf(" ndp [-nt] -i interface [flags...]\n"); #ifdef SIOCSDEFIFACE_IN6 printf(" ndp [-nt] -I [interface|delete]\n"); #endif printf(" ndp [-nt] -s nodename etheraddr [temp] [proxy]\n"); exit(1); } static int rtmsg(int cmd) { static int seq; int rlen; register struct rt_msghdr *rtm = &m_rtmsg.m_rtm; register char *cp = m_rtmsg.m_space; register int l; errno = 0; if (cmd == RTM_DELETE) goto doit; bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); rtm->rtm_flags = flags; rtm->rtm_version = RTM_VERSION; switch (cmd) { default: fprintf(stderr, "ndp: internal wrong cmd\n"); exit(1); case RTM_ADD: rtm->rtm_addrs |= RTA_GATEWAY; if (expire_time) { rtm->rtm_rmx.rmx_expire = expire_time; rtm->rtm_inits = RTV_EXPIRE; } rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA); #if 0 /* we don't support ipv6addr/128 type proxying */ if (rtm->rtm_flags & RTF_ANNOUNCE) { rtm->rtm_flags &= ~RTF_HOST; rtm->rtm_addrs |= RTA_NETMASK; } #endif /* FALLTHROUGH */ case RTM_GET: rtm->rtm_addrs |= RTA_DST; } NEXTADDR(RTA_DST, sin_m); NEXTADDR(RTA_GATEWAY, sdl_m); #if 0 /* we don't support ipv6addr/128 type proxying */ memset(&so_mask.sin6_addr, 0xff, sizeof(so_mask.sin6_addr)); NEXTADDR(RTA_NETMASK, so_mask); #endif rtm->rtm_msglen = cp - (char *)&m_rtmsg; doit: l = rtm->rtm_msglen; rtm->rtm_seq = ++seq; rtm->rtm_type = cmd; if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { if (errno != ESRCH || cmd != RTM_DELETE) { err(1, "writing to routing socket"); /* NOTREACHED */ } } do { l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); if (l < 0) (void) fprintf(stderr, "ndp: read from routing socket: %s\n", strerror(errno)); return (0); } static void ifinfo(char *ifname, int argc, char **argv) { struct in6_ndireq nd; int i, s; u_int32_t newflags; #ifdef IPV6CTL_USETEMPADDR u_int8_t nullbuf[8]; #endif if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { err(1, "socket"); /* NOTREACHED */ } bzero(&nd, sizeof(nd)); strlcpy(nd.ifname, ifname, sizeof(nd.ifname)); if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) { err(1, "ioctl(SIOCGIFINFO_IN6)"); /* NOTREACHED */ } #define ND nd.ndi newflags = ND.flags; for (i = 0; i < argc; i++) { int clear = 0; char *cp = argv[i]; if (*cp == '-') { clear = 1; cp++; } #define SETFLAG(s, f) \ do {\ if (strcmp(cp, (s)) == 0) {\ if (clear)\ newflags &= ~(f);\ else\ newflags |= (f);\ }\ } while (0) /* * XXX: this macro is not 100% correct, in that it matches "nud" against * "nudbogus". But we just let it go since this is minor. */ #define SETVALUE(f, v) \ do { \ char *valptr; \ unsigned long newval; \ v = 0; /* unspecified */ \ if (strncmp(cp, f, strlen(f)) == 0) { \ valptr = strchr(cp, '='); \ if (valptr == NULL) \ err(1, "syntax error in %s field", (f)); \ errno = 0; \ newval = strtoul(++valptr, NULL, 0); \ if (errno) \ err(1, "syntax error in %s's value", (f)); \ v = newval; \ } \ } while (0) SETFLAG("disabled", ND6_IFF_IFDISABLED); SETFLAG("nud", ND6_IFF_PERFORMNUD); #ifdef ND6_IFF_ACCEPT_RTADV SETFLAG("accept_rtadv", ND6_IFF_ACCEPT_RTADV); #endif #ifdef ND6_IFF_AUTO_LINKLOCAL SETFLAG("auto_linklocal", ND6_IFF_AUTO_LINKLOCAL); #endif #ifdef ND6_IFF_NO_PREFER_IFACE SETFLAG("no_prefer_iface", ND6_IFF_NO_PREFER_IFACE); #endif SETVALUE("basereachable", ND.basereachable); SETVALUE("retrans", ND.retrans); SETVALUE("curhlim", ND.chlim); ND.flags = newflags; if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd) < 0) { err(1, "ioctl(SIOCSIFINFO_IN6)"); /* NOTREACHED */ } #undef SETFLAG #undef SETVALUE } if (!ND.initialized) { errx(1, "%s: not initialized yet", ifname); /* NOTREACHED */ } if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) { err(1, "ioctl(SIOCGIFINFO_IN6)"); /* NOTREACHED */ } printf("linkmtu=%d", ND.linkmtu); printf(", maxmtu=%d", ND.maxmtu); printf(", curhlim=%d", ND.chlim); printf(", basereachable=%ds%dms", ND.basereachable / 1000, ND.basereachable % 1000); printf(", reachable=%ds", ND.reachable); printf(", retrans=%ds%dms", ND.retrans / 1000, ND.retrans % 1000); #ifdef IPV6CTL_USETEMPADDR memset(nullbuf, 0, sizeof(nullbuf)); if (memcmp(nullbuf, ND.randomid, sizeof(nullbuf)) != 0) { int j; u_int8_t *rbuf; for (i = 0; i < 3; i++) { switch (i) { case 0: printf("\nRandom seed(0): "); rbuf = ND.randomseed0; break; case 1: printf("\nRandom seed(1): "); rbuf = ND.randomseed1; break; case 2: printf("\nRandom ID: "); rbuf = ND.randomid; break; default: errx(1, "impossible case for tempaddr display"); } for (j = 0; j < 8; j++) printf("%02x", rbuf[j]); } } #endif if (ND.flags) { printf("\nFlags: "); #ifdef ND6_IFF_IFDISABLED if ((ND.flags & ND6_IFF_IFDISABLED)) printf("disabled "); #endif if ((ND.flags & ND6_IFF_PERFORMNUD)) printf("nud "); #ifdef ND6_IFF_ACCEPT_RTADV if ((ND.flags & ND6_IFF_ACCEPT_RTADV)) printf("accept_rtadv "); #endif #ifdef ND6_IFF_AUTO_LINKLOCAL if ((ND.flags & ND6_IFF_AUTO_LINKLOCAL)) printf("auto_linklocal "); #endif #ifdef ND6_IFF_NO_PREFER_IFACE if ((ND.flags & ND6_IFF_NO_PREFER_IFACE)) printf("no_prefer_iface "); #endif } putc('\n', stdout); #undef ND close(s); } #ifndef ND_RA_FLAG_RTPREF_MASK /* XXX: just for compilation on *BSD release */ #define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */ #endif static void rtrlist() { int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_DRLIST }; char *buf; struct in6_defrouter *p, *ep; size_t l; struct timeval now; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) { err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)"); /*NOTREACHED*/ } if (l == 0) return; buf = malloc(l); if (!buf) { err(1, "malloc"); /*NOTREACHED*/ } if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) { err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)"); /*NOTREACHED*/ } ep = (struct in6_defrouter *)(buf + l); for (p = (struct in6_defrouter *)buf; p < ep; p++) { int rtpref; if (getnameinfo((struct sockaddr *)&p->rtaddr, p->rtaddr.sin6_len, host_buf, sizeof(host_buf), NULL, 0, (nflag ? NI_NUMERICHOST : 0)) != 0) strlcpy(host_buf, "?", sizeof(host_buf)); printf("%s if=%s", host_buf, if_indextoname(p->if_index, ifix_buf)); printf(", flags=%s%s", p->flags & ND_RA_FLAG_MANAGED ? "M" : "", p->flags & ND_RA_FLAG_OTHER ? "O" : ""); rtpref = ((p->flags & ND_RA_FLAG_RTPREF_MASK) >> 3) & 0xff; printf(", pref=%s", rtpref_str[rtpref]); gettimeofday(&now, 0); if (p->expire == 0) printf(", expire=Never\n"); else printf(", expire=%s\n", sec2str(p->expire - now.tv_sec)); } free(buf); } static void plist() { int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_PRLIST }; char *buf; struct in6_prefix *p, *ep, *n; struct sockaddr_in6 *advrtr; size_t l; struct timeval now; const int niflags = NI_NUMERICHOST; int ninflags = nflag ? NI_NUMERICHOST : 0; char namebuf[NI_MAXHOST]; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) { err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)"); /*NOTREACHED*/ } buf = malloc(l); if (!buf) { err(1, "malloc"); /*NOTREACHED*/ } if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) { err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)"); /*NOTREACHED*/ } ep = (struct in6_prefix *)(buf + l); for (p = (struct in6_prefix *)buf; p < ep; p = n) { advrtr = (struct sockaddr_in6 *)(p + 1); n = (struct in6_prefix *)&advrtr[p->advrtrs]; if (getnameinfo((struct sockaddr *)&p->prefix, p->prefix.sin6_len, namebuf, sizeof(namebuf), NULL, 0, niflags) != 0) strlcpy(namebuf, "?", sizeof(namebuf)); printf("%s/%d if=%s\n", namebuf, p->prefixlen, if_indextoname(p->if_index, ifix_buf)); gettimeofday(&now, 0); /* * meaning of fields, especially flags, is very different * by origin. notify the difference to the users. */ printf("flags=%s%s%s%s%s", p->raflags.onlink ? "L" : "", p->raflags.autonomous ? "A" : "", (p->flags & NDPRF_ONLINK) != 0 ? "O" : "", (p->flags & NDPRF_DETACHED) != 0 ? "D" : "", #ifdef NDPRF_HOME (p->flags & NDPRF_HOME) != 0 ? "H" : "" #else "" #endif ); if (p->vltime == ND6_INFINITE_LIFETIME) printf(" vltime=infinity"); else printf(" vltime=%lu", (unsigned long)p->vltime); if (p->pltime == ND6_INFINITE_LIFETIME) printf(", pltime=infinity"); else printf(", pltime=%lu", (unsigned long)p->pltime); if (p->expire == 0) printf(", expire=Never"); else if (p->expire >= now.tv_sec) printf(", expire=%s", sec2str(p->expire - now.tv_sec)); else printf(", expired"); printf(", ref=%d", p->refcnt); printf("\n"); /* * "advertising router" list is meaningful only if the prefix * information is from RA. */ if (p->advrtrs) { int j; struct sockaddr_in6 *sin6; sin6 = advrtr; printf(" advertised by\n"); for (j = 0; j < p->advrtrs; j++) { struct in6_nbrinfo *nbi; if (getnameinfo((struct sockaddr *)sin6, sin6->sin6_len, namebuf, sizeof(namebuf), NULL, 0, ninflags) != 0) strlcpy(namebuf, "?", sizeof(namebuf)); printf(" %s", namebuf); nbi = getnbrinfo(&sin6->sin6_addr, p->if_index, 0); if (nbi) { switch (nbi->state) { case ND6_LLINFO_REACHABLE: case ND6_LLINFO_STALE: case ND6_LLINFO_DELAY: case ND6_LLINFO_PROBE: printf(" (reachable)\n"); break; default: printf(" (unreachable)\n"); } } else printf(" (no neighbor state)\n"); sin6++; } } else printf(" No advertising router\n"); } free(buf); } static void pfx_flush() { char dummyif[IFNAMSIZ+8]; int s; if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) err(1, "socket"); strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */ if (ioctl(s, SIOCSPFXFLUSH_IN6, (caddr_t)&dummyif) < 0) err(1, "ioctl(SIOCSPFXFLUSH_IN6)"); } static void rtr_flush() { char dummyif[IFNAMSIZ+8]; int s; if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) err(1, "socket"); strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */ if (ioctl(s, SIOCSRTRFLUSH_IN6, (caddr_t)&dummyif) < 0) err(1, "ioctl(SIOCSRTRFLUSH_IN6)"); close(s); } static void harmonize_rtr() { char dummyif[IFNAMSIZ+8]; int s; if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) err(1, "socket"); strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */ if (ioctl(s, SIOCSNDFLUSH_IN6, (caddr_t)&dummyif) < 0) err(1, "ioctl(SIOCSNDFLUSH_IN6)"); close(s); } #ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */ static void setdefif(char *ifname) { struct in6_ndifreq ndifreq; unsigned int ifindex; if (strcasecmp(ifname, "delete") == 0) ifindex = 0; else { if ((ifindex = if_nametoindex(ifname)) == 0) err(1, "failed to resolve i/f index for %s", ifname); } if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) err(1, "socket"); strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */ ndifreq.ifindex = ifindex; if (ioctl(s, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) < 0) err(1, "ioctl(SIOCSDEFIFACE_IN6)"); close(s); } static void getdefif() { struct in6_ndifreq ndifreq; char ifname[IFNAMSIZ+8]; if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) err(1, "socket"); memset(&ndifreq, 0, sizeof(ndifreq)); strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */ if (ioctl(s, SIOCGDEFIFACE_IN6, (caddr_t)&ndifreq) < 0) err(1, "ioctl(SIOCGDEFIFACE_IN6)"); if (ndifreq.ifindex == 0) printf("No default interface.\n"); else { if ((if_indextoname(ndifreq.ifindex, ifname)) == NULL) err(1, "failed to resolve ifname for index %lu", ndifreq.ifindex); printf("ND default interface = %s\n", ifname); } close(s); } #endif static char * sec2str(time_t total) { static char result[256]; int days, hours, mins, secs; int first = 1; char *p = result; char *ep = &result[sizeof(result)]; int n; days = total / 3600 / 24; hours = (total / 3600) % 24; mins = (total / 60) % 60; secs = total % 60; if (days) { first = 0; n = snprintf(p, ep - p, "%dd", days); if (n < 0 || n >= ep - p) return "?"; p += n; } if (!first || hours) { first = 0; n = snprintf(p, ep - p, "%dh", hours); if (n < 0 || n >= ep - p) return "?"; p += n; } if (!first || mins) { first = 0; n = snprintf(p, ep - p, "%dm", mins); if (n < 0 || n >= ep - p) return "?"; p += n; } snprintf(p, ep - p, "%ds", secs); return(result); } /* * Print the timestamp * from tcpdump/util.c */ static void ts_print(const struct timeval *tvp) { int s; /* Default */ s = (tvp->tv_sec + thiszone) % 86400; (void)printf("%02d:%02d:%02d.%06u ", s / 3600, (s % 3600) / 60, s % 60, (u_int32_t)tvp->tv_usec); } #undef NEXTADDR Index: head/usr.sbin/newsyslog/newsyslog.c =================================================================== --- head/usr.sbin/newsyslog/newsyslog.c (revision 289676) +++ head/usr.sbin/newsyslog/newsyslog.c (revision 289677) @@ -1,2664 +1,2664 @@ /*- * ------+---------+---------+-------- + --------+---------+---------+---------* * This file includes significant modifications done by: * Copyright (c) 2003, 2004 - Garance Alistair Drosehn . * 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. * * ------+---------+---------+-------- + --------+---------+---------+---------* */ /* * This file contains changes from the Open Software Foundation. */ /* * Copyright 1988, 1989 by the Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the names of M.I.T. and the M.I.T. S.I.P.B. not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. and the M.I.T. * S.I.P.B. make no representations about the suitability of this software * for any purpose. It is provided "as is" without express or implied * warranty. * */ /* * newsyslog - roll over selected logs at the appropriate time, keeping the a * specified number of backup files around. */ #include __FBSDID("$FreeBSD$"); #define OSF #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" #include "extern.h" /* * Compression suffixes */ #ifndef COMPRESS_SUFFIX_GZ #define COMPRESS_SUFFIX_GZ ".gz" #endif #ifndef COMPRESS_SUFFIX_BZ2 #define COMPRESS_SUFFIX_BZ2 ".bz2" #endif #ifndef COMPRESS_SUFFIX_XZ #define COMPRESS_SUFFIX_XZ ".xz" #endif #define COMPRESS_SUFFIX_MAXLEN MAX(MAX(sizeof(COMPRESS_SUFFIX_GZ),sizeof(COMPRESS_SUFFIX_BZ2)),sizeof(COMPRESS_SUFFIX_XZ)) /* * Compression types */ #define COMPRESS_TYPES 4 /* Number of supported compression types */ #define COMPRESS_NONE 0 #define COMPRESS_GZIP 1 #define COMPRESS_BZIP2 2 #define COMPRESS_XZ 3 /* * Bit-values for the 'flags' parsed from a config-file entry. */ #define CE_BINARY 0x0008 /* Logfile is in binary, do not add status */ /* messages to logfile(s) when rotating. */ #define CE_NOSIGNAL 0x0010 /* There is no process to signal when */ /* trimming this file. */ #define CE_TRIMAT 0x0020 /* trim file at a specific time. */ #define CE_GLOB 0x0040 /* name of the log is file name pattern. */ #define CE_SIGNALGROUP 0x0080 /* Signal a process-group instead of a single */ /* process when trimming this file. */ #define CE_CREATE 0x0100 /* Create the log file if it does not exist. */ #define CE_NODUMP 0x0200 /* Set 'nodump' on newly created log file. */ #define CE_PID2CMD 0x0400 /* Replace PID file with a shell command.*/ #define MIN_PID 5 /* Don't touch pids lower than this */ #define MAX_PID 99999 /* was lower, see /usr/include/sys/proc.h */ #define kbytes(size) (((size) + 1023) >> 10) #define DEFAULT_MARKER "" #define DEBUG_MARKER "" #define INCLUDE_MARKER "" #define DEFAULT_TIMEFNAME_FMT "%Y%m%dT%H%M%S" #define MAX_OLDLOGS 65536 /* Default maximum number of old logfiles */ struct compress_types { const char *flag; /* Flag in configuration file */ const char *suffix; /* Compression suffix */ const char *path; /* Path to compression program */ }; static const struct compress_types compress_type[COMPRESS_TYPES] = { { "", "", "" }, /* no compression */ { "Z", COMPRESS_SUFFIX_GZ, _PATH_GZIP }, /* gzip compression */ { "J", COMPRESS_SUFFIX_BZ2, _PATH_BZIP2 }, /* bzip2 compression */ { "X", COMPRESS_SUFFIX_XZ, _PATH_XZ } /* xz compression */ }; struct conf_entry { STAILQ_ENTRY(conf_entry) cf_nextp; char *log; /* Name of the log */ char *pid_cmd_file; /* PID or command file */ char *r_reason; /* The reason this file is being rotated */ int firstcreate; /* Creating log for the first time (-C). */ int rotate; /* Non-zero if this file should be rotated */ int fsize; /* size found for the log file */ uid_t uid; /* Owner of log */ gid_t gid; /* Group of log */ int numlogs; /* Number of logs to keep */ int trsize; /* Size cutoff to trigger trimming the log */ int hours; /* Hours between log trimming */ struct ptime_data *trim_at; /* Specific time to do trimming */ unsigned int permissions; /* File permissions on the log */ int flags; /* CE_BINARY */ int compress; /* Compression */ int sig; /* Signal to send */ int def_cfg; /* Using the rule for this file */ }; struct sigwork_entry { SLIST_ENTRY(sigwork_entry) sw_nextp; int sw_signum; /* the signal to send */ int sw_pidok; /* true if pid value is valid */ pid_t sw_pid; /* the process id from the PID file */ const char *sw_pidtype; /* "daemon" or "process group" */ int sw_runcmd; /* run command or send PID to signal */ char sw_fname[1]; /* file the PID was read from or shell cmd */ }; struct zipwork_entry { SLIST_ENTRY(zipwork_entry) zw_nextp; const struct conf_entry *zw_conf; /* for chown/perm/flag info */ const struct sigwork_entry *zw_swork; /* to know success of signal */ int zw_fsize; /* size of the file to compress */ char zw_fname[1]; /* the file to compress */ }; struct include_entry { STAILQ_ENTRY(include_entry) inc_nextp; const char *file; /* Name of file to process */ }; struct oldlog_entry { char *fname; /* Filename of the log file */ time_t t; /* Parsed timestamp of the logfile */ }; typedef enum { FREE_ENT, KEEP_ENT } fk_entry; STAILQ_HEAD(cflist, conf_entry); static SLIST_HEAD(swlisthead, sigwork_entry) swhead = SLIST_HEAD_INITIALIZER(swhead); static SLIST_HEAD(zwlisthead, zipwork_entry) zwhead = SLIST_HEAD_INITIALIZER(zwhead); STAILQ_HEAD(ilist, include_entry); int dbg_at_times; /* -D Show details of 'trim_at' code */ static int archtodir = 0; /* Archive old logfiles to other directory */ static int createlogs; /* Create (non-GLOB) logfiles which do not */ /* already exist. 1=='for entries with */ /* C flag', 2=='for all entries'. */ int verbose = 0; /* Print out what's going on */ static int needroot = 1; /* Root privs are necessary */ int noaction = 0; /* Don't do anything, just show it */ static int norotate = 0; /* Don't rotate */ static int nosignal; /* Do not send any signals */ static int enforcepid = 0; /* If PID file does not exist or empty, do nothing */ static int force = 0; /* Force the trim no matter what */ static int rotatereq = 0; /* -R = Always rotate the file(s) as given */ /* on the command (this also requires */ /* that a list of files *are* given on */ /* the run command). */ static char *requestor; /* The name given on a -R request */ static char *timefnamefmt = NULL;/* Use time based filenames instead of .0 */ static char *archdirname; /* Directory path to old logfiles archive */ static char *destdir = NULL; /* Directory to treat at root for logs */ static const char *conf; /* Configuration file to use */ struct ptime_data *dbg_timenow; /* A "timenow" value set via -D option */ static struct ptime_data *timenow; /* The time to use for checking at-fields */ #define DAYTIME_LEN 16 static char daytime[DAYTIME_LEN];/* The current time in human readable form, * used for rotation-tracking messages. */ static char hostname[MAXHOSTNAMELEN]; /* hostname */ static const char *path_syslogpid = _PATH_SYSLOGPID; static struct cflist *get_worklist(char **files); static void parse_file(FILE *cf, struct cflist *work_p, struct cflist *glob_p, struct conf_entry *defconf_p, struct ilist *inclist); static void add_to_queue(const char *fname, struct ilist *inclist); static char *sob(char *p); static char *son(char *p); static int isnumberstr(const char *); static int isglobstr(const char *); static char *missing_field(char *p, char *errline); static void change_attrs(const char *, const struct conf_entry *); static const char *get_logfile_suffix(const char *logfile); static fk_entry do_entry(struct conf_entry *); static fk_entry do_rotate(const struct conf_entry *); static void do_sigwork(struct sigwork_entry *); static void do_zipwork(struct zipwork_entry *); static struct sigwork_entry * save_sigwork(const struct conf_entry *); static struct zipwork_entry * save_zipwork(const struct conf_entry *, const struct sigwork_entry *, int, const char *); static void set_swpid(struct sigwork_entry *, const struct conf_entry *); static int sizefile(const char *); static void expand_globs(struct cflist *work_p, struct cflist *glob_p); static void free_clist(struct cflist *list); static void free_entry(struct conf_entry *ent); static struct conf_entry *init_entry(const char *fname, struct conf_entry *src_entry); static void parse_args(int argc, char **argv); static int parse_doption(const char *doption); static void usage(void); static int log_trim(const char *logname, const struct conf_entry *log_ent); static int age_old_log(const char *file); static void savelog(char *from, char *to); static void createdir(const struct conf_entry *ent, char *dirpart); static void createlog(const struct conf_entry *ent); /* * All the following take a parameter of 'int', but expect values in the * range of unsigned char. Define wrappers which take values of type 'char', * whether signed or unsigned, and ensure they end up in the right range. */ #define isdigitch(Anychar) isdigit((u_char)(Anychar)) #define isprintch(Anychar) isprint((u_char)(Anychar)) #define isspacech(Anychar) isspace((u_char)(Anychar)) #define tolowerch(Anychar) tolower((u_char)(Anychar)) int main(int argc, char **argv) { struct cflist *worklist; struct conf_entry *p; struct sigwork_entry *stmp; struct zipwork_entry *ztmp; SLIST_INIT(&swhead); SLIST_INIT(&zwhead); parse_args(argc, argv); argc -= optind; argv += optind; if (needroot && getuid() && geteuid()) errx(1, "must have root privs"); worklist = get_worklist(argv); /* * Rotate all the files which need to be rotated. Note that * some users have *hundreds* of entries in newsyslog.conf! */ while (!STAILQ_EMPTY(worklist)) { p = STAILQ_FIRST(worklist); STAILQ_REMOVE_HEAD(worklist, cf_nextp); if (do_entry(p) == FREE_ENT) free_entry(p); } /* * Send signals to any processes which need a signal to tell * them to close and re-open the log file(s) we have rotated. * Note that zipwork_entries include pointers to these * sigwork_entry's, so we can not free the entries here. */ if (!SLIST_EMPTY(&swhead)) { if (noaction || verbose) printf("Signal all daemon process(es)...\n"); SLIST_FOREACH(stmp, &swhead, sw_nextp) do_sigwork(stmp); if (noaction) printf("\tsleep 10\n"); else { if (verbose) printf("Pause 10 seconds to allow daemon(s)" " to close log file(s)\n"); sleep(10); } } /* * Compress all files that we're expected to compress, now * that all processes should have closed the files which * have been rotated. */ if (!SLIST_EMPTY(&zwhead)) { if (noaction || verbose) printf("Compress all rotated log file(s)...\n"); while (!SLIST_EMPTY(&zwhead)) { ztmp = SLIST_FIRST(&zwhead); do_zipwork(ztmp); SLIST_REMOVE_HEAD(&zwhead, zw_nextp); free(ztmp); } } /* Now free all the sigwork entries. */ while (!SLIST_EMPTY(&swhead)) { stmp = SLIST_FIRST(&swhead); SLIST_REMOVE_HEAD(&swhead, sw_nextp); free(stmp); } while (wait(NULL) > 0 || errno == EINTR) ; return (0); } static struct conf_entry * init_entry(const char *fname, struct conf_entry *src_entry) { struct conf_entry *tempwork; if (verbose > 4) printf("\t--> [creating entry for %s]\n", fname); tempwork = malloc(sizeof(struct conf_entry)); if (tempwork == NULL) err(1, "malloc of conf_entry for %s", fname); if (destdir == NULL || fname[0] != '/') tempwork->log = strdup(fname); else asprintf(&tempwork->log, "%s%s", destdir, fname); if (tempwork->log == NULL) err(1, "strdup for %s", fname); if (src_entry != NULL) { tempwork->pid_cmd_file = NULL; if (src_entry->pid_cmd_file) tempwork->pid_cmd_file = strdup(src_entry->pid_cmd_file); tempwork->r_reason = NULL; tempwork->firstcreate = 0; tempwork->rotate = 0; tempwork->fsize = -1; tempwork->uid = src_entry->uid; tempwork->gid = src_entry->gid; tempwork->numlogs = src_entry->numlogs; tempwork->trsize = src_entry->trsize; tempwork->hours = src_entry->hours; tempwork->trim_at = NULL; if (src_entry->trim_at != NULL) tempwork->trim_at = ptime_init(src_entry->trim_at); tempwork->permissions = src_entry->permissions; tempwork->flags = src_entry->flags; tempwork->compress = src_entry->compress; tempwork->sig = src_entry->sig; tempwork->def_cfg = src_entry->def_cfg; } else { /* Initialize as a "do-nothing" entry */ tempwork->pid_cmd_file = NULL; tempwork->r_reason = NULL; tempwork->firstcreate = 0; tempwork->rotate = 0; tempwork->fsize = -1; tempwork->uid = (uid_t)-1; tempwork->gid = (gid_t)-1; tempwork->numlogs = 1; tempwork->trsize = -1; tempwork->hours = -1; tempwork->trim_at = NULL; tempwork->permissions = 0; tempwork->flags = 0; tempwork->compress = COMPRESS_NONE; tempwork->sig = SIGHUP; tempwork->def_cfg = 0; } return (tempwork); } static void free_entry(struct conf_entry *ent) { if (ent == NULL) return; if (ent->log != NULL) { if (verbose > 4) printf("\t--> [freeing entry for %s]\n", ent->log); free(ent->log); ent->log = NULL; } if (ent->pid_cmd_file != NULL) { free(ent->pid_cmd_file); ent->pid_cmd_file = NULL; } if (ent->r_reason != NULL) { free(ent->r_reason); ent->r_reason = NULL; } if (ent->trim_at != NULL) { ptime_free(ent->trim_at); ent->trim_at = NULL; } free(ent); } static void free_clist(struct cflist *list) { struct conf_entry *ent; while (!STAILQ_EMPTY(list)) { ent = STAILQ_FIRST(list); STAILQ_REMOVE_HEAD(list, cf_nextp); free_entry(ent); } free(list); list = NULL; } static fk_entry do_entry(struct conf_entry * ent) { #define REASON_MAX 80 int modtime; fk_entry free_or_keep; double diffsecs; char temp_reason[REASON_MAX]; int oversized; free_or_keep = FREE_ENT; if (verbose) printf("%s <%d%s>: ", ent->log, ent->numlogs, compress_type[ent->compress].flag); ent->fsize = sizefile(ent->log); oversized = ((ent->trsize > 0) && (ent->fsize >= ent->trsize)); modtime = age_old_log(ent->log); ent->rotate = 0; ent->firstcreate = 0; if (ent->fsize < 0) { /* * If either the C flag or the -C option was specified, * and if we won't be creating the file, then have the * verbose message include a hint as to why the file * will not be created. */ temp_reason[0] = '\0'; if (createlogs > 1) ent->firstcreate = 1; else if ((ent->flags & CE_CREATE) && createlogs) ent->firstcreate = 1; else if (ent->flags & CE_CREATE) strlcpy(temp_reason, " (no -C option)", REASON_MAX); else if (createlogs) strlcpy(temp_reason, " (no C flag)", REASON_MAX); if (ent->firstcreate) { if (verbose) printf("does not exist -> will create.\n"); createlog(ent); } else if (verbose) { printf("does not exist, skipped%s.\n", temp_reason); } } else { if (ent->flags & CE_TRIMAT && !force && !rotatereq && !oversized) { diffsecs = ptimeget_diff(timenow, ent->trim_at); if (diffsecs < 0.0) { /* trim_at is some time in the future. */ if (verbose) { ptime_adjust4dst(ent->trim_at, timenow); printf("--> will trim at %s", ptimeget_ctime(ent->trim_at)); } return (free_or_keep); } else if (diffsecs >= 3600.0) { /* * trim_at is more than an hour in the past, * so find the next valid trim_at time, and * tell the user what that will be. */ if (verbose && dbg_at_times) printf("\n\t--> prev trim at %s\t", ptimeget_ctime(ent->trim_at)); if (verbose) { ptimeset_nxtime(ent->trim_at); printf("--> will trim at %s", ptimeget_ctime(ent->trim_at)); } return (free_or_keep); } else if (verbose && noaction && dbg_at_times) { /* * If we are just debugging at-times, then * a detailed message is helpful. Also * skip "doing" any commands, since they * would all be turned off by no-action. */ printf("\n\t--> timematch at %s", ptimeget_ctime(ent->trim_at)); return (free_or_keep); } else if (verbose && ent->hours <= 0) { printf("--> time is up\n"); } } if (verbose && (ent->trsize > 0)) printf("size (Kb): %d [%d] ", ent->fsize, ent->trsize); if (verbose && (ent->hours > 0)) printf(" age (hr): %d [%d] ", modtime, ent->hours); /* * Figure out if this logfile needs to be rotated. */ temp_reason[0] = '\0'; if (rotatereq) { ent->rotate = 1; snprintf(temp_reason, REASON_MAX, " due to -R from %s", requestor); } else if (force) { ent->rotate = 1; snprintf(temp_reason, REASON_MAX, " due to -F request"); } else if (oversized) { ent->rotate = 1; snprintf(temp_reason, REASON_MAX, " due to size>%dK", ent->trsize); } else if (ent->hours <= 0 && (ent->flags & CE_TRIMAT)) { ent->rotate = 1; } else if ((ent->hours > 0) && ((modtime >= ent->hours) || (modtime < 0))) { ent->rotate = 1; } /* * If the file needs to be rotated, then rotate it. */ if (ent->rotate && !norotate) { if (temp_reason[0] != '\0') ent->r_reason = strdup(temp_reason); if (verbose) printf("--> trimming log....\n"); if (noaction && !verbose) printf("%s <%d%s>: trimming\n", ent->log, ent->numlogs, compress_type[ent->compress].flag); free_or_keep = do_rotate(ent); } else { if (verbose) printf("--> skipping\n"); } } return (free_or_keep); #undef REASON_MAX } static void parse_args(int argc, char **argv) { int ch; char *p; timenow = ptime_init(NULL); ptimeset_time(timenow, time(NULL)); strlcpy(daytime, ptimeget_ctime(timenow) + 4, DAYTIME_LEN); /* Let's get our hostname */ (void)gethostname(hostname, sizeof(hostname)); /* Truncate domain */ if ((p = strchr(hostname, '.')) != NULL) *p = '\0'; /* Parse command line options. */ while ((ch = getopt(argc, argv, "a:d:f:nrst:vCD:FNPR:S:")) != -1) switch (ch) { case 'a': archtodir++; archdirname = optarg; break; case 'd': destdir = optarg; break; case 'f': conf = optarg; break; case 'n': noaction++; /* FALLTHROUGH */ case 'r': needroot = 0; break; case 's': nosignal = 1; break; case 't': if (optarg[0] == '\0' || strcmp(optarg, "DEFAULT") == 0) timefnamefmt = strdup(DEFAULT_TIMEFNAME_FMT); else timefnamefmt = strdup(optarg); break; case 'v': verbose++; break; case 'C': /* Useful for things like rc.diskless... */ createlogs++; break; case 'D': /* * Set some debugging option. The specific option * depends on the value of optarg. These options * may come and go without notice or documentation. */ if (parse_doption(optarg)) break; usage(); /* NOTREACHED */ case 'F': force++; break; case 'N': norotate++; break; case 'P': enforcepid++; break; case 'R': rotatereq++; requestor = strdup(optarg); break; case 'S': path_syslogpid = optarg; break; case 'm': /* Used by OpenBSD for "monitor mode" */ default: usage(); /* NOTREACHED */ } if (force && norotate) { warnx("Only one of -F and -N may be specified."); usage(); /* NOTREACHED */ } if (rotatereq) { if (optind == argc) { warnx("At least one filename must be given when -R is specified."); usage(); /* NOTREACHED */ } /* Make sure "requestor" value is safe for a syslog message. */ for (p = requestor; *p != '\0'; p++) { if (!isprintch(*p) && (*p != '\t')) *p = '.'; } } if (dbg_timenow) { /* * Note that the 'daytime' variable is not changed. * That is only used in messages that track when a * logfile is rotated, and if a file *is* rotated, * then it will still rotated at the "real now" time. */ ptime_free(timenow); timenow = dbg_timenow; fprintf(stderr, "Debug: Running as if TimeNow is %s", ptimeget_ctime(dbg_timenow)); } } /* * These debugging options are mainly meant for developer use, such * as writing regression-tests. They would not be needed by users * during normal operation of newsyslog... */ static int parse_doption(const char *doption) { const char TN[] = "TN="; int res; if (strncmp(doption, TN, sizeof(TN) - 1) == 0) { /* * The "TimeNow" debugging option. This might be off * by an hour when crossing a timezone change. */ dbg_timenow = ptime_init(NULL); res = ptime_relparse(dbg_timenow, PTM_PARSE_ISO8601, time(NULL), doption + sizeof(TN) - 1); if (res == -2) { warnx("Non-existent time specified on -D %s", doption); return (0); /* failure */ } else if (res < 0) { warnx("Malformed time given on -D %s", doption); return (0); /* failure */ } return (1); /* successfully parsed */ } if (strcmp(doption, "ats") == 0) { dbg_at_times++; return (1); /* successfully parsed */ } /* XXX - This check could probably be dropped. */ if ((strcmp(doption, "neworder") == 0) || (strcmp(doption, "oldorder") == 0)) { warnx("NOTE: newsyslog always uses 'neworder'."); return (1); /* successfully parsed */ } warnx("Unknown -D (debug) option: '%s'", doption); return (0); /* failure */ } static void usage(void) { fprintf(stderr, "usage: newsyslog [-CFNPnrsv] [-a directory] [-d directory] [-f config_file]\n" " [-S pidfile] [-t timefmt] [[-R tagname] file ...]\n"); exit(1); } /* * Parse a configuration file and return a linked list of all the logs * which should be processed. */ static struct cflist * get_worklist(char **files) { FILE *f; char **given; struct cflist *cmdlist, *filelist, *globlist; struct conf_entry *defconf, *dupent, *ent; struct ilist inclist; struct include_entry *inc; int gmatch, fnres; defconf = NULL; STAILQ_INIT(&inclist); filelist = malloc(sizeof(struct cflist)); if (filelist == NULL) err(1, "malloc of filelist"); STAILQ_INIT(filelist); globlist = malloc(sizeof(struct cflist)); if (globlist == NULL) err(1, "malloc of globlist"); STAILQ_INIT(globlist); inc = malloc(sizeof(struct include_entry)); if (inc == NULL) err(1, "malloc of inc"); inc->file = conf; if (inc->file == NULL) inc->file = _PATH_CONF; STAILQ_INSERT_TAIL(&inclist, inc, inc_nextp); STAILQ_FOREACH(inc, &inclist, inc_nextp) { if (strcmp(inc->file, "-") != 0) f = fopen(inc->file, "r"); else { f = stdin; inc->file = ""; } if (!f) err(1, "%s", inc->file); if (verbose) printf("Processing %s\n", inc->file); parse_file(f, filelist, globlist, defconf, &inclist); (void) fclose(f); } /* * All config-file information has been read in and turned into * a filelist and a globlist. If there were no specific files * given on the run command, then the only thing left to do is to * call a routine which finds all files matched by the globlist * and adds them to the filelist. Then return the worklist. */ if (*files == NULL) { expand_globs(filelist, globlist); free_clist(globlist); if (defconf != NULL) free_entry(defconf); return (filelist); /* NOTREACHED */ } /* * If newsyslog was given a specific list of files to process, * it may be that some of those files were not listed in any * config file. Those unlisted files should get the default * rotation action. First, create the default-rotation action * if none was found in a system config file. */ if (defconf == NULL) { defconf = init_entry(DEFAULT_MARKER, NULL); defconf->numlogs = 3; defconf->trsize = 50; defconf->permissions = S_IRUSR|S_IWUSR; } /* * If newsyslog was run with a list of specific filenames, * then create a new worklist which has only those files in * it, picking up the rotation-rules for those files from * the original filelist. * * XXX - Note that this will copy multiple rules for a single * logfile, if multiple entries are an exact match for * that file. That matches the historic behavior, but do * we want to continue to allow it? If so, it should * probably be handled more intelligently. */ cmdlist = malloc(sizeof(struct cflist)); if (cmdlist == NULL) err(1, "malloc of cmdlist"); STAILQ_INIT(cmdlist); for (given = files; *given; ++given) { /* * First try to find exact-matches for this given file. */ gmatch = 0; STAILQ_FOREACH(ent, filelist, cf_nextp) { if (strcmp(ent->log, *given) == 0) { gmatch++; dupent = init_entry(*given, ent); STAILQ_INSERT_TAIL(cmdlist, dupent, cf_nextp); } } if (gmatch) { if (verbose > 2) printf("\t+ Matched entry %s\n", *given); continue; } /* * There was no exact-match for this given file, so look * for a "glob" entry which does match. */ gmatch = 0; if (verbose > 2 && globlist != NULL) printf("\t+ Checking globs for %s\n", *given); STAILQ_FOREACH(ent, globlist, cf_nextp) { fnres = fnmatch(ent->log, *given, FNM_PATHNAME); if (verbose > 2) printf("\t+ = %d for pattern %s\n", fnres, ent->log); if (fnres == 0) { gmatch++; dupent = init_entry(*given, ent); /* This new entry is not a glob! */ dupent->flags &= ~CE_GLOB; STAILQ_INSERT_TAIL(cmdlist, dupent, cf_nextp); /* Only allow a match to one glob-entry */ break; } } if (gmatch) { if (verbose > 2) printf("\t+ Matched %s via %s\n", *given, ent->log); continue; } /* * This given file was not found in any config file, so * add a worklist item based on the default entry. */ if (verbose > 2) printf("\t+ No entry matched %s (will use %s)\n", *given, DEFAULT_MARKER); dupent = init_entry(*given, defconf); /* Mark that it was *not* found in a config file */ dupent->def_cfg = 1; STAILQ_INSERT_TAIL(cmdlist, dupent, cf_nextp); } /* * Free all the entries in the original work list, the list of * glob entries, and the default entry. */ free_clist(filelist); free_clist(globlist); free_entry(defconf); /* And finally, return a worklist which matches the given files. */ return (cmdlist); } /* * Expand the list of entries with filename patterns, and add all files * which match those glob-entries onto the worklist. */ static void expand_globs(struct cflist *work_p, struct cflist *glob_p) { int gmatch, gres; size_t i; char *mfname; struct conf_entry *dupent, *ent, *globent; glob_t pglob; struct stat st_fm; /* * The worklist contains all fully-specified (non-GLOB) names. * * Now expand the list of filename-pattern (GLOB) entries into * a second list, which (by definition) will only match files * that already exist. Do not add a glob-related entry for any * file which already exists in the fully-specified list. */ STAILQ_FOREACH(globent, glob_p, cf_nextp) { gres = glob(globent->log, GLOB_NOCHECK, NULL, &pglob); if (gres != 0) { warn("cannot expand pattern (%d): %s", gres, globent->log); continue; } if (verbose > 2) printf("\t+ Expanding pattern %s\n", globent->log); for (i = 0; i < pglob.gl_matchc; i++) { mfname = pglob.gl_pathv[i]; /* See if this file already has a specific entry. */ gmatch = 0; STAILQ_FOREACH(ent, work_p, cf_nextp) { if (strcmp(mfname, ent->log) == 0) { gmatch++; break; } } if (gmatch) continue; /* Make sure the named matched is a file. */ gres = lstat(mfname, &st_fm); if (gres != 0) { /* Error on a file that glob() matched?!? */ warn("Skipping %s - lstat() error", mfname); continue; } if (!S_ISREG(st_fm.st_mode)) { /* We only rotate files! */ if (verbose > 2) printf("\t+ . skipping %s (!file)\n", mfname); continue; } if (verbose > 2) printf("\t+ . add file %s\n", mfname); dupent = init_entry(mfname, globent); /* This new entry is not a glob! */ dupent->flags &= ~CE_GLOB; /* Add to the worklist. */ STAILQ_INSERT_TAIL(work_p, dupent, cf_nextp); } globfree(&pglob); if (verbose > 2) printf("\t+ Done with pattern %s\n", globent->log); } } /* * Parse a configuration file and update a linked list of all the logs to * process. */ static void parse_file(FILE *cf, struct cflist *work_p, struct cflist *glob_p, struct conf_entry *defconf_p, struct ilist *inclist) { char line[BUFSIZ], *parse, *q; char *cp, *errline, *group; struct conf_entry *working; struct passwd *pwd; struct group *grp; glob_t pglob; int eol, ptm_opts, res, special; size_t i; errline = NULL; while (fgets(line, BUFSIZ, cf)) { if ((line[0] == '\n') || (line[0] == '#') || (strlen(line) == 0)) continue; if (errline != NULL) free(errline); errline = strdup(line); for (cp = line + 1; *cp != '\0'; cp++) { if (*cp != '#') continue; if (*(cp - 1) == '\\') { strcpy(cp - 1, cp); cp--; continue; } *cp = '\0'; break; } q = parse = missing_field(sob(line), errline); parse = son(line); if (!*parse) errx(1, "malformed line (missing fields):\n%s", errline); *parse = '\0'; /* * Allow people to set debug options via the config file. * (NOTE: debug options are undocumented, and may disappear * at any time, etc). */ if (strcasecmp(DEBUG_MARKER, q) == 0) { q = parse = missing_field(sob(parse + 1), errline); parse = son(parse); if (!*parse) warnx("debug line specifies no option:\n%s", errline); else { *parse = '\0'; parse_doption(q); } continue; } else if (strcasecmp(INCLUDE_MARKER, q) == 0) { if (verbose) printf("Found: %s", errline); q = parse = missing_field(sob(parse + 1), errline); parse = son(parse); if (!*parse) { warnx("include line missing argument:\n%s", errline); continue; } *parse = '\0'; if (isglobstr(q)) { res = glob(q, GLOB_NOCHECK, NULL, &pglob); if (res != 0) { warn("cannot expand pattern (%d): %s", res, q); continue; } if (verbose > 2) printf("\t+ Expanding pattern %s\n", q); for (i = 0; i < pglob.gl_matchc; i++) add_to_queue(pglob.gl_pathv[i], inclist); globfree(&pglob); } else add_to_queue(q, inclist); continue; } special = 0; working = init_entry(q, NULL); if (strcasecmp(DEFAULT_MARKER, q) == 0) { special = 1; if (defconf_p != NULL) { warnx("Ignoring duplicate entry for %s!", q); free_entry(working); continue; } defconf_p = working; } q = parse = missing_field(sob(parse + 1), errline); parse = son(parse); if (!*parse) errx(1, "malformed line (missing fields):\n%s", errline); *parse = '\0'; if ((group = strchr(q, ':')) != NULL || (group = strrchr(q, '.')) != NULL) { *group++ = '\0'; if (*q) { if (!(isnumberstr(q))) { if ((pwd = getpwnam(q)) == NULL) errx(1, "error in config file; unknown user:\n%s", errline); working->uid = pwd->pw_uid; } else working->uid = atoi(q); } else working->uid = (uid_t)-1; q = group; if (*q) { if (!(isnumberstr(q))) { if ((grp = getgrnam(q)) == NULL) errx(1, "error in config file; unknown group:\n%s", errline); working->gid = grp->gr_gid; } else working->gid = atoi(q); } else working->gid = (gid_t)-1; q = parse = missing_field(sob(parse + 1), errline); parse = son(parse); if (!*parse) errx(1, "malformed line (missing fields):\n%s", errline); *parse = '\0'; } else { working->uid = (uid_t)-1; working->gid = (gid_t)-1; } if (!sscanf(q, "%o", &working->permissions)) errx(1, "error in config file; bad permissions:\n%s", errline); q = parse = missing_field(sob(parse + 1), errline); parse = son(parse); if (!*parse) errx(1, "malformed line (missing fields):\n%s", errline); *parse = '\0'; if (!sscanf(q, "%d", &working->numlogs) || working->numlogs < 0) errx(1, "error in config file; bad value for count of logs to save:\n%s", errline); q = parse = missing_field(sob(parse + 1), errline); parse = son(parse); if (!*parse) errx(1, "malformed line (missing fields):\n%s", errline); *parse = '\0'; if (isdigitch(*q)) working->trsize = atoi(q); else if (strcmp(q, "*") == 0) working->trsize = -1; else { warnx("Invalid value of '%s' for 'size' in line:\n%s", q, errline); working->trsize = -1; } working->flags = 0; working->compress = COMPRESS_NONE; q = parse = missing_field(sob(parse + 1), errline); parse = son(parse); eol = !*parse; *parse = '\0'; { char *ep; u_long ul; ul = strtoul(q, &ep, 10); if (ep == q) working->hours = 0; else if (*ep == '*') working->hours = -1; else if (ul > INT_MAX) errx(1, "interval is too large:\n%s", errline); else working->hours = ul; if (*ep == '\0' || strcmp(ep, "*") == 0) goto no_trimat; if (*ep != '@' && *ep != '$') errx(1, "malformed interval/at:\n%s", errline); working->flags |= CE_TRIMAT; working->trim_at = ptime_init(NULL); ptm_opts = PTM_PARSE_ISO8601; if (*ep == '$') ptm_opts = PTM_PARSE_DWM; ptm_opts |= PTM_PARSE_MATCHDOM; res = ptime_relparse(working->trim_at, ptm_opts, ptimeget_secs(timenow), ep + 1); if (res == -2) errx(1, "nonexistent time for 'at' value:\n%s", errline); else if (res < 0) errx(1, "malformed 'at' value:\n%s", errline); } no_trimat: if (eol) q = NULL; else { q = parse = sob(parse + 1); /* Optional field */ parse = son(parse); if (!*parse) eol = 1; *parse = '\0'; } for (; q && *q && !isspacech(*q); q++) { switch (tolowerch(*q)) { case 'b': working->flags |= CE_BINARY; break; case 'c': /* * XXX - Ick! Ugly! Remove ASAP! * We want `c' and `C' for "create". But we * will temporarily treat `c' as `g', because * FreeBSD releases <= 4.8 have a typo of * checking ('G' || 'c') for CE_GLOB. */ if (*q == 'c') { warnx("Assuming 'g' for 'c' in flags for line:\n%s", errline); warnx("The 'c' flag will eventually mean 'CREATE'"); working->flags |= CE_GLOB; break; } working->flags |= CE_CREATE; break; case 'd': working->flags |= CE_NODUMP; break; case 'g': working->flags |= CE_GLOB; break; case 'j': working->compress = COMPRESS_BZIP2; break; case 'n': working->flags |= CE_NOSIGNAL; break; case 'r': working->flags |= CE_PID2CMD; break; case 'u': working->flags |= CE_SIGNALGROUP; break; case 'w': /* Depreciated flag - keep for compatibility purposes */ break; case 'x': working->compress = COMPRESS_XZ; break; case 'z': working->compress = COMPRESS_GZIP; break; case '-': break; case 'f': /* Used by OpenBSD for "CE_FOLLOW" */ case 'm': /* Used by OpenBSD for "CE_MONITOR" */ case 'p': /* Used by NetBSD for "CE_PLAIN0" */ default: errx(1, "illegal flag in config file -- %c", *q); } } if (eol) q = NULL; else { q = parse = sob(parse + 1); /* Optional field */ parse = son(parse); if (!*parse) eol = 1; *parse = '\0'; } working->pid_cmd_file = NULL; if (q && *q) { if (*q == '/') working->pid_cmd_file = strdup(q); else if (isdigit(*q)) goto got_sig; else errx(1, "illegal pid file or signal number in config file:\n%s", errline); } if (eol) q = NULL; else { q = parse = sob(parse + 1); /* Optional field */ *(parse = son(parse)) = '\0'; } working->sig = SIGHUP; if (q && *q) { if (isdigit(*q)) { got_sig: working->sig = atoi(q); } else { err_sig: errx(1, "illegal signal number in config file:\n%s", errline); } if (working->sig < 1 || working->sig >= NSIG) goto err_sig; } /* * Finish figuring out what pid-file to use (if any) in * later processing if this logfile needs to be rotated. */ if ((working->flags & CE_NOSIGNAL) == CE_NOSIGNAL) { /* * This config-entry specified 'n' for nosignal, * see if it also specified an explicit pid_cmd_file. * This would be a pretty pointless combination. */ if (working->pid_cmd_file != NULL) { warnx("Ignoring '%s' because flag 'n' was specified in line:\n%s", working->pid_cmd_file, errline); free(working->pid_cmd_file); working->pid_cmd_file = NULL; } } else if (working->pid_cmd_file == NULL) { /* * This entry did not specify the 'n' flag, which * means it should signal syslogd unless it had * specified some other pid-file (and obviously the * syslog pid-file will not be for a process-group). * Also, we should only try to notify syslog if we * are root. */ if (working->flags & CE_SIGNALGROUP) { warnx("Ignoring flag 'U' in line:\n%s", errline); working->flags &= ~CE_SIGNALGROUP; } if (needroot) working->pid_cmd_file = strdup(path_syslogpid); } /* * Add this entry to the appropriate list of entries, unless * it was some kind of special entry (eg: ). */ if (special) { ; /* Do not add to any list */ } else if (working->flags & CE_GLOB) { STAILQ_INSERT_TAIL(glob_p, working, cf_nextp); } else { STAILQ_INSERT_TAIL(work_p, working, cf_nextp); } } if (errline != NULL) free(errline); } static char * missing_field(char *p, char *errline) { if (!p || !*p) errx(1, "missing field in config file:\n%s", errline); return (p); } /* * In our sort we return it in the reverse of what qsort normally * would do, as we want the newest files first. If we have two * entries with the same time we don't really care about order. * * Support function for qsort() in delete_oldest_timelog(). */ static int oldlog_entry_compare(const void *a, const void *b) { const struct oldlog_entry *ola = a, *olb = b; if (ola->t > olb->t) return (-1); else if (ola->t < olb->t) return (1); else return (0); } /* * Check whether the file corresponding to dp is an archive of the logfile * logfname, based on the timefnamefmt format string. Return true and fill out * tm if this is the case; otherwise return false. */ static int validate_old_timelog(int fd, const struct dirent *dp, const char *logfname, struct tm *tm) { struct stat sb; size_t logfname_len; char *s; int c; logfname_len = strlen(logfname); if (dp->d_type != DT_REG) { /* * Some filesystems (e.g. NFS) don't fill out the d_type field * and leave it set to DT_UNKNOWN; in this case we must obtain * the file type ourselves. */ if (dp->d_type != DT_UNKNOWN || fstatat(fd, dp->d_name, &sb, AT_SYMLINK_NOFOLLOW) != 0 || !S_ISREG(sb.st_mode)) return (0); } /* Ignore everything but files with our logfile prefix. */ if (strncmp(dp->d_name, logfname, logfname_len) != 0) return (0); /* Ignore the actual non-rotated logfile. */ if (dp->d_namlen == logfname_len) return (0); /* * Make sure we created have found a logfile, so the * postfix is valid, IE format is: '.