#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <net/if.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <syslog.h>
#include <netdb.h>
#include <utmp.h>
#include <pwd.h>
#include "pppd.h"
#include "magic.h"
#include "fsm.h"
#include "lcp.h"
#include "ipcp.h"
#include "upap.h"
#include "chap.h"
#include "ccp.h"
#include "pathnames.h"
#include "patchlevel.h"
#ifdef CBCP_SUPPORT
#include "cbcp.h"
#endif
#if defined(SUNOS4)
extern char *strerror();
#endif
#ifdef AT_CHANGE
#include "atcp.h"
#endif
char ifname[IFNAMSIZ];
int ifunit;
char hostname[HOST_NAME_MAX+1];
static char default_devnam[PATH_MAX];
static pid_t pid;
static uid_t uid;
static int conn_running;
static volatile sig_atomic_t crashed = 0;
int ttyfd = -1;
mode_t tty_mode = -1;
int baud_rate;
int hungup;
int privileged;
int need_holdoff;
int detached;
int phase;
volatile sig_atomic_t kill_link;
volatile sig_atomic_t open_ccp_flag;
volatile sig_atomic_t got_sigchld;
char **script_env;
int s_env_nalloc;
u_char outpacket_buf[PPP_MRU+PPP_HDRLEN];
u_char inpacket_buf[PPP_MRU+PPP_HDRLEN];
static int locked;
char *no_ppp_msg = "Sorry - this system lacks PPP kernel support\n";
static void cleanup(void);
static void close_tty(void);
static void get_input(void);
static void calltimeout(void);
static struct timeval *timeleft(struct timeval *);
static void kill_my_pg(int);
static void hup(int);
static void term(int);
static void chld(int);
static void toggle_debug(int);
static void open_ccp(int);
static void bad_signal(int);
static void holdoff_end(void *);
static int device_script(char *, int, int);
static void reap_kids(void);
static void pr_log(void *, char *, ...);
extern char *ttyname(int);
extern char *getlogin(void);
int main(int, char *[]);
#ifdef ultrix
#undef O_NONBLOCK
#define O_NONBLOCK O_NDELAY
#endif
#ifdef ULTRIX
#define setlogmask(x)
#endif
struct protent *protocols[] = {
&lcp_protent,
&pap_protent,
&chap_protent,
#ifdef CBCP_SUPPORT
&cbcp_protent,
#endif
&ipcp_protent,
&ccp_protent,
#ifdef AT_CHANGE
&atcp_protent,
#endif
NULL
};
int
main(int argc, char *argv[])
{
int i, fdflags;
struct sigaction sa;
char *p;
struct passwd *pw;
struct timeval timo;
sigset_t mask;
struct protent *protp;
struct stat statbuf;
char numbuf[16];
phase = PHASE_INITIALIZE;
p = ttyname(0);
if (p)
strlcpy(devnam, p, PATH_MAX);
strlcpy(default_devnam, devnam, sizeof default_devnam);
script_env = NULL;
#ifdef ULTRIX
openlog("pppd", LOG_PID);
#else
openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP);
setlogmask(LOG_UPTO(LOG_INFO));
#endif
if (gethostname(hostname, sizeof hostname) < 0 ) {
option_error("Couldn't get hostname: %m");
die(1);
}
uid = getuid();
privileged = uid == 0;
snprintf(numbuf, sizeof numbuf, "%u", uid);
script_setenv("UID", numbuf);
for (i = 0; (protp = protocols[i]) != NULL; ++i)
(*protp->init)(0);
if (!options_from_file(_PATH_SYSOPTIONS, !privileged, 0, 1)
|| !options_from_user())
exit(1);
scan_args(argc-1, argv+1);
if (!options_for_tty()
|| !parse_args(argc-1, argv+1))
exit(1);
if (geteuid() != 0) {
option_error("must be root to run %s, since it is not setuid-root",
argv[0]);
die(1);
}
if (!ppp_available()) {
option_error(no_ppp_msg);
exit(1);
}
sys_check_options();
auth_check_options();
for (i = 0; (protp = protocols[i]) != NULL; ++i)
if (protp->check_options != NULL)
(*protp->check_options)();
if (demand && connector == 0) {
option_error("connect script required for demand-dialling\n");
exit(1);
}
script_setenv("DEVICE", devnam);
snprintf(numbuf, sizeof numbuf, "%d", baud_rate);
script_setenv("SPEED", numbuf);
if (!default_device && strcmp(devnam, default_devnam) == 0)
default_device = 1;
if (default_device)
nodetach = 1;
sys_init();
magic_init();
if (debug)
setlogmask(LOG_UPTO(LOG_DEBUG));
if (nodetach == 0)
detach();
pid = getpid();
p = getlogin();
if (p == NULL) {
pw = getpwuid(uid);
if (pw != NULL && pw->pw_name != NULL)
p = pw->pw_name;
else
p = "(unknown)";
}
syslog(LOG_NOTICE, "pppd %s.%d%s started by %s, uid %u",
VERSION, PATCHLEVEL, IMPLEMENTATION, p, uid);
sigemptyset(&mask);
sigaddset(&mask, SIGHUP);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGCHLD);
#define SIGNAL(s, handler) { \
sa.sa_handler = handler; \
if (sigaction(s, &sa, NULL) < 0) { \
syslog(LOG_ERR, "Couldn't establish signal handler (%d): %m", s); \
die(1); \
} \
}
sa.sa_mask = mask;
sa.sa_flags = 0;
SIGNAL(SIGHUP, hup);
SIGNAL(SIGINT, term);
SIGNAL(SIGTERM, term);
SIGNAL(SIGCHLD, chld);
SIGNAL(SIGUSR1, toggle_debug);
SIGNAL(SIGUSR2, open_ccp);
SIGNAL(SIGABRT, bad_signal);
SIGNAL(SIGALRM, bad_signal);
SIGNAL(SIGFPE, bad_signal);
SIGNAL(SIGILL, bad_signal);
SIGNAL(SIGPIPE, bad_signal);
SIGNAL(SIGQUIT, bad_signal);
#if SIGSEGV_CHECK
SIGNAL(SIGSEGV, bad_signal);
#endif
#ifdef SIGBUS
SIGNAL(SIGBUS, bad_signal);
#endif
#ifdef SIGEMT
SIGNAL(SIGEMT, bad_signal);
#endif
#ifdef SIGPOLL
SIGNAL(SIGPOLL, bad_signal);
#endif
#ifdef SIGPROF
SIGNAL(SIGPROF, bad_signal);
#endif
#ifdef SIGSYS
SIGNAL(SIGSYS, bad_signal);
#endif
#ifdef SIGTRAP
SIGNAL(SIGTRAP, bad_signal);
#endif
#ifdef SIGVTALRM
SIGNAL(SIGVTALRM, bad_signal);
#endif
#ifdef SIGXCPU
SIGNAL(SIGXCPU, bad_signal);
#endif
#ifdef SIGXFSZ
SIGNAL(SIGXFSZ, bad_signal);
#endif
signal(SIGPIPE, SIG_IGN);
if (demand) {
open_ppp_loopback();
syslog(LOG_INFO, "Using interface ppp%d", ifunit);
(void) snprintf(ifname, sizeof ifname, "ppp%d", ifunit);
script_setenv("IFNAME", ifname);
demand_conf();
}
for (;;) {
need_holdoff = 1;
if (demand) {
phase = PHASE_DORMANT;
kill_link = 0;
demand_unblock();
for (;;) {
wait_loop_output(timeleft(&timo));
calltimeout();
if (kill_link) {
if (!persist)
die(0);
kill_link = 0;
}
if (get_loop_output())
break;
reap_kids();
}
demand_drop();
syslog(LOG_INFO, "Starting link");
}
if (lockflag && !default_device) {
if (lock(devnam) < 0)
goto fail;
locked = 1;
}
while ((ttyfd = open(devnam, O_NONBLOCK | O_RDWR)) < 0) {
if (errno != EINTR)
syslog(LOG_ERR, "Failed to open %s: %m", devnam);
if (!persist || errno != EINTR)
goto fail;
}
if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1
|| fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
syslog(LOG_WARNING,
"Couldn't reset non-blocking mode on device: %m");
hungup = 0;
kill_link = 0;
if (fstat(ttyfd, &statbuf) < 0
|| fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) {
syslog(LOG_WARNING,
"Couldn't restrict write permissions to %s: %m", devnam);
} else
tty_mode = statbuf.st_mode;
if (connector && connector[0]) {
MAINDEBUG((LOG_INFO, "Connecting with <%s>", connector));
set_up_tty(ttyfd, (modem_chat == 0));
if (!default_device && modem) {
setdtr(ttyfd, FALSE);
sleep(1);
setdtr(ttyfd, TRUE);
}
if (device_script(connector, ttyfd, ttyfd) < 0) {
syslog(LOG_ERR, "Connect script failed");
setdtr(ttyfd, FALSE);
goto fail;
}
syslog(LOG_INFO, "Serial connection established.");
sleep(1);
}
set_up_tty(ttyfd, 0);
if (connector == NULL && modem) {
while ((i = open(devnam, O_RDWR)) < 0) {
if (errno != EINTR)
syslog(LOG_ERR, "Failed to reopen %s: %m", devnam);
if (!persist || errno != EINTR || hungup || kill_link)
goto fail;
}
close(i);
}
if (welcomer && welcomer[0]) {
if (device_script(welcomer, ttyfd, ttyfd) < 0)
syslog(LOG_WARNING, "Welcome script failed");
}
establish_ppp(ttyfd);
if (!demand) {
syslog(LOG_INFO, "Using interface ppp%d", ifunit);
(void) snprintf(ifname, sizeof ifname, "ppp%d", ifunit);
script_setenv("IFNAME", ifname);
}
syslog(LOG_NOTICE, "Connect: %s <--> %s", ifname, devnam);
lcp_lowerup(0);
lcp_open(0);
for (phase = PHASE_ESTABLISH; phase != PHASE_DEAD; ) {
wait_input(timeleft(&timo));
calltimeout();
get_input();
if (kill_link) {
lcp_close(0, "User request");
kill_link = 0;
}
if (open_ccp_flag) {
if (phase == PHASE_NETWORK) {
ccp_fsm[0].flags = OPT_RESTART;
(*ccp_protent.open)(0);
}
open_ccp_flag = 0;
}
reap_kids();
}
clean_check();
if (demand)
restore_loop();
disestablish_ppp(ttyfd);
if (disconnector && !hungup) {
set_up_tty(ttyfd, 1);
if (device_script(disconnector, ttyfd, ttyfd) < 0) {
syslog(LOG_WARNING, "disconnect script failed");
} else {
syslog(LOG_INFO, "Serial link disconnected.");
}
}
fail:
if (ttyfd >= 0)
close_tty();
if (locked) {
unlock();
locked = 0;
}
if (!persist)
die(1);
if (holdoff > 0 && need_holdoff) {
phase = PHASE_HOLDOFF;
TIMEOUT(holdoff_end, NULL, holdoff);
do {
wait_time(timeleft(&timo));
calltimeout();
if (kill_link) {
if (!persist)
die(0);
kill_link = 0;
phase = PHASE_DORMANT;
}
reap_kids();
} while (phase == PHASE_HOLDOFF);
}
}
die(0);
return 0;
}
void
detach(void)
{
if (detached)
return;
if (daemon(0, 0) < 0) {
perror("Couldn't detach from controlling terminal");
die(1);
}
detached = 1;
pid = getpid();
}
static void
holdoff_end(void *arg)
{
phase = PHASE_DORMANT;
}
static void
get_input(void)
{
int len, i;
u_char *p;
u_short protocol;
struct protent *protp;
p = inpacket_buf;
len = read_packet(inpacket_buf);
if (len < 0)
return;
if (len == 0) {
syslog(LOG_NOTICE, "Modem hangup");
hungup = 1;
lcp_lowerdown(0);
link_terminated(0);
return;
}
if (debug )
log_packet(p, len, "rcvd ", LOG_DEBUG);
if (len < PPP_HDRLEN) {
MAINDEBUG((LOG_INFO, "io(): Received short packet."));
return;
}
p += 2;
GETSHORT(protocol, p);
len -= PPP_HDRLEN;
if (protocol != PPP_LCP && lcp_fsm[0].state != OPENED) {
MAINDEBUG((LOG_INFO,
"get_input: Received non-LCP packet when LCP not open."));
return;
}
if (phase <= PHASE_AUTHENTICATE
&& !(protocol == PPP_LCP || protocol == PPP_LQR
|| protocol == PPP_PAP || protocol == PPP_CHAP)) {
MAINDEBUG((LOG_INFO, "get_input: discarding proto 0x%x in phase %d",
protocol, phase));
return;
}
for (i = 0; (protp = protocols[i]) != NULL; ++i) {
if (protp->protocol == protocol && protp->enabled_flag) {
(*protp->input)(0, p, len);
return;
}
if (protocol == (protp->protocol & ~0x8000) && protp->enabled_flag
&& protp->datainput != NULL) {
(*protp->datainput)(0, p, len);
return;
}
}
if (debug)
syslog(LOG_WARNING, "Unsupported protocol (0x%x) received", protocol);
lcp_sprotrej(0, p - PPP_HDRLEN, len + PPP_HDRLEN);
}
void
quit(void)
{
die(1);
}
void
die(int status)
{
struct syslog_data sdata = SYSLOG_DATA_INIT;
cleanup();
syslog_r(LOG_INFO, &sdata, "Exit.");
_exit(status);
}
static void
cleanup(void)
{
sys_cleanup();
if (ttyfd >= 0)
close_tty();
if (locked)
unlock();
}
static void
close_tty(void)
{
disestablish_ppp(ttyfd);
if (modem) {
setdtr(ttyfd, FALSE);
sleep(1);
}
restore_tty(ttyfd);
if (tty_mode != (mode_t) -1)
fchmod(ttyfd, tty_mode);
close(ttyfd);
ttyfd = -1;
}
struct callout {
struct timeval c_time;
void *c_arg;
void (*c_func)(void *);
struct callout *c_next;
};
static struct callout *callout = NULL;
static struct timeval timenow;
void
timeout(void (*func)(void *), void *arg, int time)
{
struct callout *newp, *p, **pp;
MAINDEBUG((LOG_DEBUG, "Timeout %lx:%lx in %d seconds.",
(long) func, (long) arg, time));
if ((newp = (struct callout *) malloc(sizeof(struct callout))) == NULL) {
syslog(LOG_ERR, "Out of memory in timeout()!");
die(1);
}
newp->c_arg = arg;
newp->c_func = func;
gettimeofday(&timenow, NULL);
newp->c_time.tv_sec = timenow.tv_sec + time;
newp->c_time.tv_usec = timenow.tv_usec;
for (pp = &callout; (p = *pp); pp = &p->c_next)
if (newp->c_time.tv_sec < p->c_time.tv_sec
|| (newp->c_time.tv_sec == p->c_time.tv_sec
&& newp->c_time.tv_usec < p->c_time.tv_sec))
break;
newp->c_next = p;
*pp = newp;
}
void
untimeout(void (*func)(void *), void *arg)
{
struct callout **copp, *freep;
MAINDEBUG((LOG_DEBUG, "Untimeout %lx:%lx.", (long) func, (long) arg));
for (copp = &callout; (freep = *copp); copp = &freep->c_next)
if (freep->c_func == func && freep->c_arg == arg) {
*copp = freep->c_next;
(void) free((char *) freep);
break;
}
}
static void
calltimeout(void)
{
struct callout *p;
while (callout != NULL) {
p = callout;
if (gettimeofday(&timenow, NULL) < 0) {
syslog(LOG_ERR, "Failed to get time of day: %m");
die(1);
}
if (!(p->c_time.tv_sec < timenow.tv_sec
|| (p->c_time.tv_sec == timenow.tv_sec
&& p->c_time.tv_usec <= timenow.tv_usec)))
break;
callout = p->c_next;
(*p->c_func)(p->c_arg);
free((char *) p);
}
}
static struct timeval *
timeleft(struct timeval *tvp)
{
if (callout == NULL)
return NULL;
gettimeofday(&timenow, NULL);
tvp->tv_sec = callout->c_time.tv_sec - timenow.tv_sec;
tvp->tv_usec = callout->c_time.tv_usec - timenow.tv_usec;
if (tvp->tv_usec < 0) {
tvp->tv_usec += 1000000;
tvp->tv_sec -= 1;
}
if (tvp->tv_sec < 0)
tvp->tv_sec = tvp->tv_usec = 0;
return tvp;
}
static void
kill_my_pg(int sig)
{
struct sigaction act, oldact;
act.sa_handler = SIG_IGN;
act.sa_flags = 0;
kill(0, sig);
sigaction(sig, &act, &oldact);
sigaction(sig, &oldact, NULL);
}
static void
hup(int sig)
{
int save_errno = errno;
struct syslog_data sdata = SYSLOG_DATA_INIT;
if (crashed)
_exit(127);
syslog_r(LOG_INFO, &sdata, "Hangup (SIGHUP)");
kill_link = 1;
if (conn_running)
kill_my_pg(sig);
errno = save_errno;
}
static void
term(int sig)
{
int save_errno = errno;
struct syslog_data sdata = SYSLOG_DATA_INIT;
if (crashed)
_exit(127);
syslog_r(LOG_INFO, &sdata, "Terminating on signal %d.", sig);
persist = 0;
kill_link = 1;
if (conn_running)
kill_my_pg(sig);
errno = save_errno;
}
static void
chld(int sig)
{
got_sigchld = 1;
}
static void
toggle_debug(int sig)
{
debug = !debug;
if (debug) {
setlogmask(LOG_UPTO(LOG_DEBUG));
} else {
setlogmask(LOG_UPTO(LOG_WARNING));
}
}
static void
open_ccp(int sig)
{
open_ccp_flag = 1;
}
static void
bad_signal(int sig)
{
struct syslog_data sdata = SYSLOG_DATA_INIT;
if (crashed)
_exit(127);
crashed = 1;
syslog_r(LOG_ERR, &sdata, "Fatal signal %d", sig);
if (conn_running)
kill_my_pg(SIGTERM);
die(1);
}
static int
device_script(char *program, int in, int out)
{
pid_t pid;
int status;
int errfd;
gid_t gid;
uid_t uid;
conn_running = 1;
pid = fork();
if (pid < 0) {
conn_running = 0;
syslog(LOG_ERR, "Failed to create child process: %m");
die(1);
}
if (pid == 0) {
sys_close();
closelog();
if (in == out) {
if (in != 0) {
dup2(in, 0);
close(in);
}
dup2(0, 1);
} else {
if (out == 0)
out = dup(out);
if (in != 0) {
dup2(in, 0);
close(in);
}
if (out != 1) {
dup2(out, 1);
close(out);
}
}
if (nodetach == 0) {
close(2);
errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600);
if (errfd >= 0 && errfd != 2) {
dup2(errfd, 2);
close(errfd);
}
}
gid = getgid();
uid = getuid();
if (setresgid(gid, gid, gid) == -1 || setresuid(uid, uid, uid) == -1) {
syslog(LOG_ERR, "revoke privileges: %s", strerror(errno));
_exit(1);
}
execl("/bin/sh", "sh", "-c", program, (char *)NULL);
syslog(LOG_ERR, "could not exec /bin/sh: %m");
_exit(99);
}
while (waitpid(pid, &status, 0) < 0) {
if (errno == EINTR)
continue;
syslog(LOG_ERR, "error waiting for (dis)connection process: %m");
die(1);
}
conn_running = 0;
return (status == 0 ? 0 : -1);
}
int
run_program(char *prog, char **args, int must_exist)
{
pid_t pid;
uid_t uid;
gid_t gid;
pid = fork();
if (pid == -1) {
syslog(LOG_ERR, "Failed to create child process for %s: %m", prog);
return -1;
}
if (pid == 0) {
int new_fd;
(void) setsid();
(void) umask (S_IRWXG|S_IRWXO);
(void) chdir ("/");
uid = getuid();
gid = getgid();
if (setresgid(gid, gid, gid) == -1 || setresuid(uid, uid, uid) == -1) {
syslog(LOG_ERR, "revoke privileges: %s", strerror(errno));
_exit(1);
}
sys_close();
closelog();
close (0);
close (1);
close (2);
close (ttyfd);
new_fd = open (_PATH_DEVNULL, O_RDWR);
if (new_fd >= 0) {
if (new_fd != 0) {
dup2 (new_fd, 0);
close (new_fd);
}
dup2 (0, 1);
dup2 (0, 2);
}
if (setpriority (PRIO_PROCESS, 0, 0) < 0)
syslog (LOG_WARNING, "can't reset priority to 0: %m");
execve(prog, args, script_env);
if (must_exist || errno != ENOENT)
syslog(LOG_WARNING, "Can't execute %s: %m", prog);
_exit(1);
}
MAINDEBUG((LOG_DEBUG, "Script %s started; pid = %ld", prog, (long)pid));
return 0;
}
static void
reap_kids(void)
{
int status;
pid_t pid;
if (!got_sigchld)
return;
got_sigchld = 0;
for (;;) {
pid = waitpid(-1, &status, WNOHANG);
switch (pid) {
case -1:
if (errno == EINTR)
continue;
if (errno != ECHILD)
syslog(LOG_ERR, "Error waiting for child process: %m");
return;
case 0:
return;
default:
if (WIFSIGNALED(status)) {
syslog(LOG_WARNING,
"Child process %d terminated with signal %d",
(int)pid, WTERMSIG(status));
}
break;
}
}
}
char line[256];
char *linep;
void
log_packet(u_char *p, int len, char *prefix, int level)
{
strlcpy(line, prefix, sizeof line);
linep = line + strlen(line);
format_packet(p, len, pr_log, NULL);
if (linep != line)
syslog(level, "%s", line);
}
void
format_packet(u_char *p, int len, void (*printer)(void *, char *, ...), void *arg)
{
int i, n;
u_short proto;
u_char x;
struct protent *protp;
if (len >= PPP_HDRLEN && p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) {
p += 2;
GETSHORT(proto, p);
len -= PPP_HDRLEN;
for (i = 0; (protp = protocols[i]) != NULL; ++i)
if (proto == protp->protocol)
break;
if (protp != NULL) {
printer(arg, "[%s", protp->name);
n = (*protp->printpkt)(p, len, printer, arg);
printer(arg, "]");
p += n;
len -= n;
} else {
printer(arg, "[proto=0x%x]", proto);
}
}
for (; len > 0; --len) {
GETCHAR(x, p);
printer(arg, " %.2x", x);
}
}
static void
pr_log(void *arg, char *fmt, ...)
{
int n;
va_list pvar;
char buf[256];
va_start(pvar, fmt);
n = vfmtmsg(buf, sizeof(buf), fmt, pvar);
va_end(pvar);
if (linep + n + 1 > line + sizeof(line)) {
syslog(LOG_DEBUG, "%s", line);
linep = line;
}
strlcpy(linep, buf, line + sizeof line - linep);
linep += n;
}
void
print_string(char *p, int len, void (*printer)(void *, char *, ...), void *arg)
{
int c;
printer(arg, "\"");
for (; len > 0; --len) {
c = *p++;
if (' ' <= c && c <= '~') {
if (c == '\\' || c == '"')
printer(arg, "\\");
printer(arg, "%c", c);
} else {
switch (c) {
case '\n':
printer(arg, "\\n");
break;
case '\r':
printer(arg, "\\r");
break;
case '\t':
printer(arg, "\\t");
break;
default:
printer(arg, "\\%.3o", c);
}
}
}
printer(arg, "\"");
}
void
novm(char *msg)
{
syslog(LOG_ERR, "Virtual memory exhausted allocating %s", msg);
die(1);
}
int
fmtmsg(char *buf, int buflen, char *fmt, ...)
{
va_list args;
int n;
va_start(args, fmt);
n = vfmtmsg(buf, buflen, fmt, args);
va_end(args);
return n;
}
#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0)
int
vfmtmsg(char *buf, int buflen, char *fmt, va_list args)
{
int c, i, n;
int width, prec, fillch;
int base, len, neg, quoted;
unsigned long val = 0;
char *str, *f, *buf0;
unsigned char *p;
char num[32];
time_t t;
static char hexchars[] = "0123456789abcdef";
buf0 = buf;
--buflen;
while (buflen > 0) {
for (f = fmt; *f != '%' && *f != 0; ++f)
;
if (f > fmt) {
len = f - fmt;
if (len > buflen)
len = buflen;
memcpy(buf, fmt, len);
buf += len;
buflen -= len;
fmt = f;
}
if (*fmt == 0)
break;
c = *++fmt;
width = prec = 0;
fillch = ' ';
if (c == '0') {
fillch = '0';
c = *++fmt;
}
if (c == '*') {
width = va_arg(args, int);
c = *++fmt;
} else {
while (isdigit(c)) {
width = width * 10 + c - '0';
c = *++fmt;
}
}
if (c == '.') {
c = *++fmt;
if (c == '*') {
prec = va_arg(args, int);
c = *++fmt;
} else {
while (isdigit(c)) {
prec = prec * 10 + c - '0';
c = *++fmt;
}
}
}
str = 0;
base = 0;
neg = 0;
++fmt;
switch (c) {
case 'd':
i = va_arg(args, int);
if (i < 0) {
neg = 1;
val = -i;
} else
val = i;
base = 10;
break;
case 'o':
val = va_arg(args, unsigned int);
base = 8;
break;
case 'x':
val = va_arg(args, unsigned int);
base = 16;
break;
case 'p':
val = (unsigned long) va_arg(args, void *);
base = 16;
neg = 2;
break;
case 's':
str = va_arg(args, char *);
break;
case 'c':
num[0] = va_arg(args, int);
num[1] = 0;
str = num;
break;
case 'm':
str = strerror(errno);
break;
case 'I':
str = ip_ntoa(va_arg(args, u_int32_t));
break;
case 't':
time(&t);
str = ctime(&t);
str += 4;
str[15] = 0;
break;
case 'v':
case 'q':
quoted = c == 'q';
p = va_arg(args, unsigned char *);
if (fillch == '0' && prec > 0) {
n = prec;
} else {
n = strlen((char *)p);
if (prec > 0 && prec < n)
n = prec;
}
while (n > 0 && buflen > 0) {
c = *p++;
--n;
if (!quoted && c >= 0x80) {
OUTCHAR('M');
OUTCHAR('-');
c -= 0x80;
}
if (quoted && (c == '"' || c == '\\'))
OUTCHAR('\\');
if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
if (quoted) {
OUTCHAR('\\');
switch (c) {
case '\t': OUTCHAR('t'); break;
case '\n': OUTCHAR('n'); break;
case '\b': OUTCHAR('b'); break;
case '\f': OUTCHAR('f'); break;
default:
OUTCHAR('x');
OUTCHAR(hexchars[c >> 4]);
OUTCHAR(hexchars[c & 0xf]);
}
} else {
if (c == '\t')
OUTCHAR(c);
else {
OUTCHAR('^');
OUTCHAR(c ^ 0x40);
}
}
} else
OUTCHAR(c);
}
continue;
default:
*buf++ = '%';
if (c != '%')
--fmt;
--buflen;
continue;
}
if (base != 0) {
str = num + sizeof(num);
*--str = 0;
while (str > num + neg) {
*--str = hexchars[val % base];
val = val / base;
if (--prec <= 0 && val == 0)
break;
}
switch (neg) {
case 1:
*--str = '-';
break;
case 2:
*--str = 'x';
*--str = '0';
break;
}
len = num + sizeof(num) - 1 - str;
} else {
len = strlen(str);
if (prec > 0 && len > prec)
len = prec;
}
if (width > 0) {
if (width > buflen)
width = buflen;
if ((n = width - len) > 0) {
buflen -= n;
for (; n > 0; --n)
*buf++ = fillch;
}
}
if (len > buflen)
len = buflen;
memcpy(buf, str, len);
buf += len;
buflen -= len;
}
*buf = 0;
return buf - buf0;
}
void
script_setenv(char *var, char *value)
{
int vl = strlen(var);
int i;
char *p, *newstring;
if (asprintf(&newstring, "%s=%s", var, value) == -1)
novm("script_setenv");
if (script_env != 0) {
for (i = 0; (p = script_env[i]) != 0; ++i) {
if (strncmp(p, var, vl) == 0 && p[vl] == '=') {
free(p);
script_env[i] = newstring;
return;
}
}
} else {
i = 0;
script_env = (char **) calloc(16, sizeof(char *));
if (script_env == 0)
novm("script_setenv");
s_env_nalloc = 16;
}
if (i + 1 >= s_env_nalloc) {
int new_n = i + 17;
char **newenv = reallocarray(script_env,
new_n, sizeof(char *));
if (newenv == 0)
novm("script_setenv");
script_env = newenv;
s_env_nalloc = new_n;
}
script_env[i] = newstring;
script_env[i+1] = 0;
}