]> git.ozlabs.org Git - ppp.git/commitdiff
Add set and reset options to control environment variables for scripts.
authorJames Carlson <carlsonj@workingcode.com>
Mon, 23 Aug 2010 13:59:56 +0000 (09:59 -0400)
committerJames Carlson <carlsonj@workingcode.com>
Mon, 23 Aug 2010 13:59:56 +0000 (09:59 -0400)
pppd/main.c
pppd/options.c
pppd/pppd.8
pppd/pppd.h
pppd/utils.c

index 014d614773d1330931f07cd19a3573f5949b11f4..1b513ca3030e1d621625ea6fedf8deb8a4ba0b2a 100644 (file)
@@ -1615,6 +1615,52 @@ safe_fork(int infd, int outfd, int errfd)
        return 0;
 }
 
        return 0;
 }
 
+static bool
+add_script_env(pos, newstring)
+    int pos;
+    char *newstring;
+{
+    if (pos + 1 >= s_env_nalloc) {
+       int new_n = pos + 17;
+       char **newenv = realloc(script_env, new_n * sizeof(char *));
+       if (newenv == NULL) {
+           free(newstring - 1);
+           return 0;
+       }
+       script_env = newenv;
+       s_env_nalloc = new_n;
+    }
+    script_env[pos] = newstring;
+    script_env[pos + 1] = NULL;
+    return 1;
+}
+
+static void
+remove_script_env(pos)
+    int pos;
+{
+    free(script_env[pos] - 1);
+    while ((script_env[pos] = script_env[pos + 1]) != NULL)
+       pos++;
+}
+
+/*
+ * update_system_environment - process the list of set/unset options
+ * and update the system environment.
+ */
+static void
+update_system_environment()
+{
+    struct userenv *uep;
+
+    for (uep = userenv_list; uep != NULL; uep = uep->ue_next) {
+       if (uep->ue_isset)
+           setenv(uep->ue_name, uep->ue_value, 1);
+       else
+           unsetenv(uep->ue_name);
+    }
+}
+
 /*
  * device_script - run a program to talk to the specified fds
  * (e.g. to run the connector or disconnector script).
 /*
  * device_script - run a program to talk to the specified fds
  * (e.g. to run the connector or disconnector script).
@@ -1670,6 +1716,7 @@ device_script(program, in, out, dont_wait)
        fprintf(stderr, "pppd: setuid failed\n");
        exit(1);
     }
        fprintf(stderr, "pppd: setuid failed\n");
        exit(1);
     }
+    update_system_environment();
     execl("/bin/sh", "sh", "-c", program, (char *)0);
     perror("pppd: could not exec /bin/sh");
     exit(99);
     execl("/bin/sh", "sh", "-c", program, (char *)0);
     perror("pppd: could not exec /bin/sh");
     exit(99);
@@ -1677,6 +1724,44 @@ device_script(program, in, out, dont_wait)
 }
 
 
 }
 
 
+/*
+ * update_script_environment - process the list of set/unset options
+ * and update the script environment.  Note that we intentionally do
+ * not update the TDB.  These changes are layered on top right before
+ * exec.  It is not possible to use script_setenv() or
+ * script_unsetenv() safely after this routine is run.
+ */
+static void
+update_script_environment()
+{
+    struct userenv *uep;
+
+    for (uep = userenv_list; uep != NULL; uep = uep->ue_next) {
+       int i;
+       char *p, *newstring;
+       int nlen = strlen(uep->ue_name);
+
+       for (i = 0; (p = script_env[i]) != NULL; i++) {
+           if (strncmp(p, uep->ue_name, nlen) == 0 && p[nlen] == '=')
+               break;
+       }
+       if (uep->ue_isset) {
+           nlen += strlen(uep->ue_value) + 2;
+           newstring = malloc(nlen + 1);
+           if (newstring == NULL)
+               continue;
+           *newstring++ = 0;
+           slprintf(newstring, nlen, "%s=%s", uep->ue_name, uep->ue_value);
+           if (p != NULL)
+               script_env[i] = newstring;
+           else
+               add_script_env(i, newstring);
+       } else {
+           remove_script_env(i);
+       }
+    }
+}
+
 /*
  * run_program - execute a program with given arguments,
  * but don't wait for it unless wait is non-zero.
 /*
  * run_program - execute a program with given arguments,
  * but don't wait for it unless wait is non-zero.
@@ -1747,6 +1832,7 @@ run_program(prog, args, must_exist, done, arg, wait)
 #endif
 
     /* run the program */
 #endif
 
     /* run the program */
+    update_script_environment();
     execve(prog, args, script_env);
     if (must_exist || errno != ENOENT) {
        /* have to reopen the log, there's nowhere else
     execve(prog, args, script_env);
     if (must_exist || errno != ENOENT) {
        /* have to reopen the log, there's nowhere else
@@ -1967,25 +2053,16 @@ script_setenv(var, value, iskey)
     } else {
        /* no space allocated for script env. ptrs. yet */
        i = 0;
     } else {
        /* no space allocated for script env. ptrs. yet */
        i = 0;
-       script_env = (char **) malloc(16 * sizeof(char *));
-       if (script_env == 0)
+       script_env = malloc(16 * sizeof(char *));
+       if (script_env == 0) {
+           free(newstring - 1);
            return;
            return;
+       }
        s_env_nalloc = 16;
     }
 
        s_env_nalloc = 16;
     }
 
-    /* reallocate script_env with more space if needed */
-    if (i + 1 >= s_env_nalloc) {
-       int new_n = i + 17;
-       char **newenv = (char **) realloc((void *)script_env,
-                                         new_n * sizeof(char *));
-       if (newenv == 0)
-           return;
-       script_env = newenv;
-       s_env_nalloc = new_n;
-    }
-
-    script_env[i] = newstring;
-    script_env[i+1] = 0;
+    if (!add_script_env(i, newstring))
+       return;
 
 #ifdef USE_TDB
     if (pppdb != NULL) {
 
 #ifdef USE_TDB
     if (pppdb != NULL) {
@@ -2016,9 +2093,7 @@ script_unsetenv(var)
            if (p[-1] && pppdb != NULL)
                delete_db_key(p);
 #endif
            if (p[-1] && pppdb != NULL)
                delete_db_key(p);
 #endif
-           free(p-1);
-           while ((script_env[i] = script_env[i+1]) != 0)
-               ++i;
+           remove_script_env(i);
            break;
        }
     }
            break;
        }
     }
index 482eab99e3274086d7c6b13aac4b7c1254045c27..434ab95748fb34497af2d3fe79e43789638b1736 100644 (file)
@@ -119,6 +119,7 @@ bool        dump_options;           /* print out option values */
 bool   dryrun;                 /* print out option values and exit */
 char   *domain;                /* domain name set by domain option */
 int    child_wait = 5;         /* # seconds to wait for children at exit */
 bool   dryrun;                 /* print out option values and exit */
 char   *domain;                /* domain name set by domain option */
 int    child_wait = 5;         /* # seconds to wait for children at exit */
+struct userenv *userenv_list;  /* user environment variables */
 
 #ifdef MAXOCTETS
 unsigned int  maxoctets = 0;    /* default - no limit */
 
 #ifdef MAXOCTETS
 unsigned int  maxoctets = 0;    /* default - no limit */
@@ -135,6 +136,7 @@ struct      bpf_program pass_filter;/* Filter program for packets to pass */
 struct bpf_program active_filter; /* Filter program for link-active pkts */
 #endif
 
 struct bpf_program active_filter; /* Filter program for link-active pkts */
 #endif
 
+static option_t *curopt;       /* pointer to option being processed */
 char *current_option;          /* the name of the option being parsed */
 int  privileged_option;                /* set iff the current option came from root */
 char *option_source;           /* string saying where the option came from */
 char *current_option;          /* the name of the option being parsed */
 int  privileged_option;                /* set iff the current option came from root */
 char *option_source;           /* string saying where the option came from */
@@ -167,6 +169,11 @@ static int setactivefilter __P((char **));
 static int setmodir __P((char **));
 #endif
 
 static int setmodir __P((char **));
 #endif
 
+static int user_setenv __P((char **));
+static void user_setprint __P((option_t *, printer_func, void *));
+static int user_unsetenv __P((char **));
+static void user_unsetprint __P((option_t *, printer_func, void *));
+
 static option_t *find_option __P((const char *name));
 static int process_option __P((option_t *, char *, char **));
 static int n_arguments __P((option_t *));
 static option_t *find_option __P((const char *name));
 static int process_option __P((option_t *, char *, char **));
 static int n_arguments __P((option_t *));
@@ -281,6 +288,13 @@ option_t general_options[] = {
       "Number of seconds to wait for child processes at exit",
       OPT_PRIO },
 
       "Number of seconds to wait for child processes at exit",
       OPT_PRIO },
 
+    { "set", o_special, (void *)user_setenv,
+      "Set user environment variable",
+      OPT_A2PRINTER | OPT_NOPRINT, (void *)user_setprint },
+    { "unset", o_special, (void *)user_unsetenv,
+      "Unset user environment variable",
+      OPT_A2PRINTER | OPT_NOPRINT, (void *)user_unsetprint },
+
 #ifdef HAVE_MULTILINK
     { "multilink", o_bool, &multilink,
       "Enable multilink operation", OPT_PRIO | 1 },
 #ifdef HAVE_MULTILINK
     { "multilink", o_bool, &multilink,
       "Enable multilink operation", OPT_PRIO | 1 },
@@ -768,6 +782,7 @@ process_option(opt, cmd, argv)
     case o_special_noarg:
     case o_special:
        parser = (int (*) __P((char **))) opt->addr;
     case o_special_noarg:
     case o_special:
        parser = (int (*) __P((char **))) opt->addr;
+       curopt = opt;
        if (!(*parser)(argv))
            return 0;
        if (opt->flags & OPT_A2LIST) {
        if (!(*parser)(argv))
            return 0;
        if (opt->flags & OPT_A2LIST) {
@@ -881,7 +896,7 @@ check_options()
 static void
 print_option(opt, mainopt, printer, arg)
     option_t *opt, *mainopt;
 static void
 print_option(opt, mainopt, printer, arg)
     option_t *opt, *mainopt;
-    void (*printer) __P((void *, char *, ...));
+    printer_func printer;
     void *arg;
 {
        int i, v;
     void *arg;
 {
        int i, v;
@@ -944,11 +959,8 @@ print_option(opt, mainopt, printer, arg)
                        printer(arg, " ");
                }
                if (opt->flags & OPT_A2PRINTER) {
                        printer(arg, " ");
                }
                if (opt->flags & OPT_A2PRINTER) {
-                       void (*oprt) __P((option_t *,
-                                         void ((*)__P((void *, char *, ...))),
-                                         void *));
-                       oprt = (void (*) __P((option_t *,
-                                        void ((*)__P((void *, char *, ...))),
+                       void (*oprt) __P((option_t *, printer_func, void *));
+                       oprt = (void (*) __P((option_t *, printer_func,
                                         void *)))opt->addr2;
                        (*oprt)(opt, printer, arg);
                } else if (opt->flags & OPT_A2STRVAL) {
                                         void *)))opt->addr2;
                        (*oprt)(opt, printer, arg);
                } else if (opt->flags & OPT_A2STRVAL) {
@@ -986,7 +998,7 @@ print_option(opt, mainopt, printer, arg)
 static void
 print_option_list(opt, printer, arg)
     option_t *opt;
 static void
 print_option_list(opt, printer, arg)
     option_t *opt;
-    void (*printer) __P((void *, char *, ...));
+    printer_func printer;
     void *arg;
 {
        while (opt->name != NULL) {
     void *arg;
 {
        while (opt->name != NULL) {
@@ -1004,7 +1016,7 @@ print_option_list(opt, printer, arg)
  */
 void
 print_options(printer, arg)
  */
 void
 print_options(printer, arg)
-    void (*printer) __P((void *, char *, ...));
+    printer_func printer;
     void *arg;
 {
        struct option_list *list;
     void *arg;
 {
        struct option_list *list;
@@ -1623,3 +1635,154 @@ loadplugin(argv)
     return 0;
 }
 #endif /* PLUGIN */
     return 0;
 }
 #endif /* PLUGIN */
+
+/*
+ * Set an environment variable specified by the user.
+ */
+static int
+user_setenv(argv)
+    char **argv;
+{
+    char *arg = argv[0];
+    char *eqp;
+    struct userenv *uep, **insp;
+
+    if ((eqp = strchr(arg, '=')) == NULL) {
+       option_error("missing = in name=value: %s", arg);
+       return 0;
+    }
+    if (eqp == arg) {
+       option_error("missing variable name: %s", arg);
+       return 0;
+    }
+    for (uep = userenv_list; uep != NULL; uep = uep->ue_next) {
+       int nlen = strlen(uep->ue_name);
+       if (nlen == (eqp - arg) &&
+           strncmp(arg, uep->ue_name, nlen) == 0)
+           break;
+    }
+    /* Ignore attempts by unprivileged users to override privileged sources */
+    if (uep != NULL && !privileged_option && uep->ue_priv)
+       return 1;
+    /* The name never changes, so allocate it with the structure */
+    if (uep == NULL) {
+       uep = malloc(sizeof (*uep) + (eqp-arg));
+       strncpy(uep->ue_name, arg, eqp-arg);
+       uep->ue_name[eqp-arg] = '\0';
+       uep->ue_next = NULL;
+       insp = &userenv_list;
+       while (*insp != NULL)
+           insp = &(*insp)->ue_next;
+       *insp = uep;
+    } else {
+       struct userenv *uep2;
+       for (uep2 = userenv_list; uep2 != NULL; uep2 = uep2->ue_next) {
+           if (uep2 != uep && !uep2->ue_isset)
+               break;
+       }
+       if (uep2 == NULL && !uep->ue_isset)
+           find_option("unset")->flags |= OPT_NOPRINT;
+       free(uep->ue_value);
+    }
+    uep->ue_isset = 1;
+    uep->ue_priv = privileged_option;
+    uep->ue_source = option_source;
+    uep->ue_value = strdup(eqp + 1);
+    curopt->flags &= ~OPT_NOPRINT;
+    return 1;
+}
+
+static void
+user_setprint(opt, printer, arg)
+    option_t *opt;
+    printer_func printer;
+    void *arg;
+{
+    struct userenv *uep, *uepnext;
+
+    uepnext = userenv_list;
+    while (uepnext != NULL && !uepnext->ue_isset)
+       uepnext = uepnext->ue_next;
+    while ((uep = uepnext) != NULL) {
+       uepnext = uep->ue_next;
+       while (uepnext != NULL && !uepnext->ue_isset)
+           uepnext = uepnext->ue_next;
+       (*printer)(arg, "%s=%s", uep->ue_name, uep->ue_value);
+       if (uepnext != NULL)
+           (*printer)(arg, "\t\t# (from %s)\n%s ", uep->ue_source, opt->name);
+       else
+           opt->source = uep->ue_source;
+    }
+}
+
+static int
+user_unsetenv(argv)
+    char **argv;
+{
+    struct userenv *uep, **insp;
+    char *arg = argv[0];
+
+    if (strchr(arg, '=') != NULL) {
+       option_error("unexpected = in name: %s", arg);
+       return 0;
+    }
+    if (arg == '\0') {
+       option_error("missing variable name for unset");
+       return 0;
+    }
+    for (uep = userenv_list; uep != NULL; uep = uep->ue_next) {
+       if (strcmp(arg, uep->ue_name) == 0)
+           break;
+    }
+    /* Ignore attempts by unprivileged users to override privileged sources */
+    if (uep != NULL && !privileged_option && uep->ue_priv)
+       return 1;
+    /* The name never changes, so allocate it with the structure */
+    if (uep == NULL) {
+       uep = malloc(sizeof (*uep) + strlen(arg));
+       strcpy(uep->ue_name, arg);
+       uep->ue_next = NULL;
+       insp = &userenv_list;
+       while (*insp != NULL)
+           insp = &(*insp)->ue_next;
+       *insp = uep;
+    } else {
+       struct userenv *uep2;
+       for (uep2 = userenv_list; uep2 != NULL; uep2 = uep2->ue_next) {
+           if (uep2 != uep && uep2->ue_isset)
+               break;
+       }
+       if (uep2 == NULL && uep->ue_isset)
+           find_option("set")->flags |= OPT_NOPRINT;
+       free(uep->ue_value);
+    }
+    uep->ue_isset = 0;
+    uep->ue_priv = privileged_option;
+    uep->ue_source = option_source;
+    uep->ue_value = NULL;
+    curopt->flags &= ~OPT_NOPRINT;
+    return 1;
+}
+
+static void
+user_unsetprint(opt, printer, arg)
+    option_t *opt;
+    printer_func printer;
+    void *arg;
+{
+    struct userenv *uep, *uepnext;
+
+    uepnext = userenv_list;
+    while (uepnext != NULL && uepnext->ue_isset)
+       uepnext = uepnext->ue_next;
+    while ((uep = uepnext) != NULL) {
+       uepnext = uep->ue_next;
+       while (uepnext != NULL && uepnext->ue_isset)
+           uepnext = uepnext->ue_next;
+       (*printer)(arg, "%s", uep->ue_name);
+       if (uepnext != NULL)
+           (*printer)(arg, "\t\t# (from %s)\n%s ", uep->ue_source, opt->name);
+       else
+           opt->source = uep->ue_source;
+    }
+}
index b7adc77f28178d87cb45df17c5e767c19e9cf97a..7ad24c51f3a42678697ec8a66723bd6a29e49e92 100644 (file)
@@ -1018,6 +1018,13 @@ Authentication Protocol] authentication.
 Require the peer to authenticate itself using PAP [Password
 Authentication Protocol] authentication.
 .TP
 Require the peer to authenticate itself using PAP [Password
 Authentication Protocol] authentication.
 .TP
+.B set \fIname\fR=\fIvalue
+Set an environment variable for scripts that are invoked by pppd.
+When set by a privileged source, the variable specified by \fIname\fR
+cannot be changed by options contained in an unprivileged source.  See
+also the \fIunset\fR option and the environment described in
+\fISCRIPTS\fR.
+.TP
 .B show\-password
 When logging the contents of PAP packets, this option causes pppd to
 show the password string in the log message.
 .B show\-password
 When logging the contents of PAP packets, this option causes pppd to
 show the password string in the log message.
@@ -1058,6 +1065,13 @@ under Linux and FreeBSD 2.2.8 and later.
 Sets the ppp unit number (for a ppp0 or ppp1 etc interface name) for outbound
 connections.
 .TP
 Sets the ppp unit number (for a ppp0 or ppp1 etc interface name) for outbound
 connections.
 .TP
+.B unset \fIname
+Remove a variable from the environment variable for scripts that are
+invoked by pppd.  When specified by a privileged source, the variable
+\fIname\fR cannot be set by options contained in an unprivileged
+source.  See also the \fIset\fR option and the environment described
+in \fISCRIPTS\fR.
+.TP
 .B updetach
 With this option, pppd will detach from its controlling terminal once
 it has successfully established the ppp connection (to the point where
 .B updetach
 With this option, pppd will detach from its controlling terminal once
 it has successfully established the ppp connection (to the point where
index cf9840a416bbd9a57098fdeb9d4d0c10bd8882d4..936cc606f318f959dc21c135f5cab30f2a45a537 100644 (file)
@@ -139,7 +139,7 @@ typedef struct {
 #define OPT_INITONLY   0x4000000 /* option can only be set in init phase */
 #define OPT_DEVEQUIV   0x8000000 /* equiv to device name */
 #define OPT_DEVNAM     (OPT_INITONLY | OPT_DEVEQUIV)
 #define OPT_INITONLY   0x4000000 /* option can only be set in init phase */
 #define OPT_DEVEQUIV   0x8000000 /* equiv to device name */
 #define OPT_DEVNAM     (OPT_INITONLY | OPT_DEVEQUIV)
-#define OPT_A2PRINTER  0x10000000 /* *addr2 is a fn for printing option */
+#define OPT_A2PRINTER  0x10000000 /* *addr2 printer_func to print option */
 #define OPT_A2STRVAL   0x20000000 /* *addr2 points to current string value */
 #define OPT_NOPRINT    0x40000000 /* don't print this option at all */
 
 #define OPT_A2STRVAL   0x20000000 /* *addr2 points to current string value */
 #define OPT_NOPRINT    0x40000000 /* don't print this option at all */
 
@@ -199,6 +199,7 @@ struct epdisc {
 #define EPD_PHONENUM   5
 
 typedef void (*notify_func) __P((void *, int));
 #define EPD_PHONENUM   5
 
 typedef void (*notify_func) __P((void *, int));
+typedef void (*printer_func) __P((void *, char *, ...));
 
 struct notifier {
     struct notifier *next;
 
 struct notifier {
     struct notifier *next;
@@ -406,8 +407,7 @@ struct protent {
     /* Close the protocol */
     void (*close) __P((int unit, char *reason));
     /* Print a packet in readable form */
     /* Close the protocol */
     void (*close) __P((int unit, char *reason));
     /* Print a packet in readable form */
-    int  (*printpkt) __P((u_char *pkt, int len,
-                         void (*printer) __P((void *, char *, ...)),
+    int  (*printpkt) __P((u_char *pkt, int len, printer_func printer,
                          void *arg));
     /* Process a received data packet */
     void (*datainput) __P((int unit, u_char *pkt, int len));
                          void *arg));
     /* Process a received data packet */
     void (*datainput) __P((int unit, u_char *pkt, int len));
@@ -460,6 +460,21 @@ struct channel {
 
 extern struct channel *the_channel;
 
 
 extern struct channel *the_channel;
 
+/*
+ * This structure contains environment variables that are set or unset
+ * by the user.
+ */
+struct userenv {
+       struct userenv *ue_next;
+       char *ue_value;         /* value (set only) */
+       bool ue_isset;          /* 1 for set, 0 for unset */
+       bool ue_priv;           /* from privileged source */
+       const char *ue_source;  /* source name */
+       char ue_name[1];        /* variable name */
+};
+
+extern struct userenv *userenv_list;
+
 /*
  * Prototypes.
  */
 /*
  * Prototypes.
  */
@@ -504,8 +519,8 @@ void tty_init __P((void));
 /* Procedures exported from utils.c. */
 void log_packet __P((u_char *, int, char *, int));
                                /* Format a packet and log it with syslog */
 /* Procedures exported from utils.c. */
 void log_packet __P((u_char *, int, char *, int));
                                /* Format a packet and log it with syslog */
-void print_string __P((char *, int,  void (*) (void *, char *, ...),
-               void *));       /* Format a string for output */
+void print_string __P((char *, int,  printer_func, void *));
+                               /* Format a string for output */
 int slprintf __P((char *, int, char *, ...));          /* sprintf++ */
 int vslprintf __P((char *, int, char *, va_list));     /* vsprintf++ */
 size_t strlcpy __P((char *, const char *, size_t));    /* safe strcpy */
 int slprintf __P((char *, int, char *, ...));          /* sprintf++ */
 int vslprintf __P((char *, int, char *, va_list));     /* vsprintf++ */
 size_t strlcpy __P((char *, const char *, size_t));    /* safe strcpy */
@@ -691,7 +706,7 @@ void add_options __P((option_t *)); /* Add extra options */
 void check_options __P((void));        /* check values after all options parsed */
 int  override_value __P((const char *, int, const char *));
                                /* override value if permitted by priority */
 void check_options __P((void));        /* check values after all options parsed */
 int  override_value __P((const char *, int, const char *));
                                /* override value if permitted by priority */
-void print_options __P((void (*) __P((void *, char *, ...)), void *));
+void print_options __P((printer_func, void *));
                                /* print out values of all options */
 
 int parse_dotted_ip __P((char *, u_int32_t *));
                                /* print out values of all options */
 
 int parse_dotted_ip __P((char *, u_int32_t *));
index 062b17e4a8db64cc11e42c43ffe184219f313f51..5c9ba6437757d6d4a679c2e0f642f65020ef8368 100644 (file)
@@ -68,8 +68,7 @@ extern char *strerror();
 static void logit __P((int, char *, va_list));
 static void log_write __P((int, char *));
 static void vslp_printer __P((void *, char *, ...));
 static void logit __P((int, char *, va_list));
 static void log_write __P((int, char *));
 static void vslp_printer __P((void *, char *, ...));
-static void format_packet __P((u_char *, int, void (*) (void *, char *, ...),
-                              void *));
+static void format_packet __P((u_char *, int, printer_func, void *));
 
 struct buffer_info {
     char *ptr;
 
 struct buffer_info {
     char *ptr;
@@ -475,7 +474,7 @@ static void
 format_packet(p, len, printer, arg)
     u_char *p;
     int len;
 format_packet(p, len, printer, arg)
     u_char *p;
     int len;
-    void (*printer) __P((void *, char *, ...));
+    printer_func printer;
     void *arg;
 {
     int i, n;
     void *arg;
 {
     int i, n;
@@ -613,7 +612,7 @@ void
 print_string(p, len, printer, arg)
     char *p;
     int len;
 print_string(p, len, printer, arg)
     char *p;
     int len;
-    void (*printer) __P((void *, char *, ...));
+    printer_func printer;
     void *arg;
 {
     int c;
     void *arg;
 {
     int c;