#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <strings.h>
#include <signal.h>
#include <errno.h>
#include <netdb.h>
#include <stdio.h>

#include "pathnames.h"

#if defined(sun) || defined(ultrix)
extern int sys_nerr;
extern char *sys_errlist[];
#endif

#if defined(sun) || defined(vax) || defined(ibm032) || defined(ultrix)
char *strerror(errno)
int errno;
{
     if (errno < sys_nerr)
	  return(sys_errlist[errno]);
     else
	  return("Unknown error");
}
#endif

char *whoami;
char buf[256];

main(argc, argv)
int argc;
char *argv[];
{
     int on = 0;
     whoami = (whoami = rindex(argv[0], '/')) ? whoami + 1 : argv[0];

     if (! strncmp(whoami, "access_on", 9))
	  on = 1;
     else if (! strncmp(whoami, "access_off", 10))
	  on = 0;
     else {
	  fprintf(stderr,
       "Unknown command \"%s\" -- should be \"access_on\" or \"access_off\"\n",
		  whoami);
	  exit(1);
     }

     if (argc == 1) {
	  if (on)
	       exit(access(SIGUSR1));
	  else
	       exit(access(SIGUSR2));
     }
     else if (argc == 2 || argc == 3) {
	  exit(toggle_service(on, argc, argv));
     }
     else {
	  usage();
	  exit(1);
     }
}

access(sig)
int sig;
{
     FILE *pidfile;
     int pid;

     pidfile = fopen(_PATH_INETDPID, "r");
     if (! pidfile) {
	  fprintf(stderr, "%s: opening \"%s\": %s\n", whoami,
		  _PATH_INETDPID, strerror(errno));
	  return(1);
     }

     if (! fgets(buf, sizeof(buf), pidfile)) {
	  fprintf(stderr, "%s: error reading from \"%s\"\n", whoami,
		  _PATH_INETDPID);
	  fclose(pidfile);
	  return(1);
     }

     pid = atoi(buf);
     if (pid <= 0) {
	  fprintf(stderr, "%s: invalid PID in \"%s\"\n", whoami,
		  _PATH_INETDPID);
	  return(1);
     }

     if (kill(pid, sig) < 0) {
	  fprintf(stderr, "%s: signalling inetd: %s\n", whoami,
		  strerror(errno));
	  return(1);
     }

     return(0);
}

toggle_service(on, argc, argv)
int on, argc;
char *argv[];
{
     char *name, *protocol;
     struct servent *service;
     int s;
     struct sockaddr_un addr;
     char *cp;
     int c, length;

     name = argv[1];
     if (argc == 3)
	  protocol = argv[2];
     else
	  protocol = 0;

     service = getservbyname(name, protocol);

     if (! service) {
	  if (protocol)
	       fprintf(stderr, "%s: invalid service/protocol: %s/%s\n",
		       whoami, name, protocol);
	  else
	       fprintf(stderr, "%s: invalid service: %s\n", whoami, name);
	  return(1);
     }

     s = socket(PF_UNIX, SOCK_STREAM, 0);
     if (s < 0) {
	  fprintf(stderr, "%s: socket: %s\n", whoami, strerror(errno));
	  return(1);
     }

     bzero((char *) &addr, sizeof(addr));
     addr.sun_family = AF_UNIX;
     strcpy(addr.sun_path, _PATH_INETDSOCK);

     if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
	  fprintf(stderr, "%s: connect: %s\n", whoami, strerror(errno));
	  (void) close(s);
	  return(1);
     }

     if (protocol)
	  sprintf(buf, "%s%s %s %s", getuid() ? "" : "\001",
		  service->s_name, service->s_proto,
		  on ? "on" : "off");
     else
	  sprintf(buf, "%s%s %s", getuid() ? "" : "\001",
		  service->s_name, on ? "on" : "off");

     cp = buf;
     length = strlen(buf) + 1;
     while ((c = write(s, cp, length - (cp - buf))) > 0) {
	  cp += c;
	  if (cp - buf == length)
	       break;
     }

     if ((c = read(s, buf, sizeof(buf))) > 0) {
	  buf[c] = '\0';
	  fprintf(stderr, "%s: error from inetd: %s\n", whoami, buf);
	  (void) close(s);
	  return(1);
     }

     if (c < 0) {
	  fprintf(stderr, "%s: write: %s\n", whoami, strerror(errno));
	  (void) close(s);
	  return(1);
     }

     (void) close(s);

     return(0);
}

usage()
{
     fprintf(stderr, "Usage: %s [ service [ protocol ] ]\n", whoami);
}
