Beispiel #1
0
/* perform actions that are common to all NAMED group addresses: checking if 
   the entry exists, parsing options, ev.removing old filesystem entry or
   setting early owners and permissions.
   It applies options of PH_EARLY and PH_PREOPEN.
   If the path exists, its st_mode field is returned.
   After this sub you may proceed with open() or whatever...
   */
int _xioopen_named_early(int argc, const char *argv[], xiofile_t *xfd,
			 int groups,
		      bool *exists, struct opt *opts) {
   const char *path = argv[1];
   unsigned int iogroups = 0;
#if HAVE_STAT64
   struct stat64 statbuf;
#else
   struct stat statbuf;
#endif /* !HAVE_STAT64 */
   bool opt_unlink_early = false;

   if (argc != 2) {
      Error2("%s: wrong number of parameters (%d instead of 1)", argv[0]?argv[0]:"<named>", argc);
   }
   statbuf.st_mode = 0;
   /* find the appropriate groupbits */
   if (
#if HAVE_STAT64
       Stat64(path, &statbuf) < 0
#else
       Stat(path, &statbuf) < 0
#endif /* !HAVE_STAT64 */
      ) {
      if (errno != ENOENT) {
	 Error2("stat(\"%s\"): %s", path, strerror(errno));
	    return STAT_RETRYLATER;
      }
      iogroups = GROUP_REG;
      *exists = false;
   } else {
      iogroups = _groupbits(statbuf.st_mode);
      *exists = true;
   }

   if (applyopts_single(&xfd->stream, opts, PH_INIT) < 0)  return -1;
   applyopts(-1, opts, PH_INIT);

   retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
   if (*exists && opt_unlink_early) {
      Info1("\"%s\" already exists; removing it", path);
      if (Unlink(path) < 0) {
	 Error2("unlink(\"%s\"): %s", path, strerror(errno));
	 *exists = true;
      } else {
	 *exists = false;
      }
   }

   applyopts(-1, opts, PH_EARLY);
   applyopts_named(path, opts, PH_EARLY);
   if (*exists) {
      applyopts_named(path, opts, PH_PREOPEN);
   } else {
      dropopts(opts, PH_PREOPEN);
   }

   return statbuf.st_mode;
}
Beispiel #2
0
/* retrieve the OPEN group options and perform the open() call.
   returns the file descriptor or a negative value.
   Applies options of phases PREOPEN, OPEN, PASTOPEN, and FD
*/
int _xioopen_open(const char *path, int rw, struct opt *opts) {
   mode_t mode = 0666;
   flags_t flags = rw;
   bool flag;
   int fd;

   applyopts_named(path, opts, PH_PREOPEN);

   /* this only applies pure OPEN flags, not mixed OPEN/FCNTL options */
   applyopts_flags(opts, GROUP_OPEN, &flags);

   /* we have to handle mixed OPEN/FCNTL flags specially */
   if (retropt_bool(opts, OPT_O_APPEND, &flag) >= 0   && flag)
      flags |= O_APPEND;
   if (retropt_bool(opts, OPT_O_NONBLOCK, &flag) >= 0 && flag)
      flags |= O_NONBLOCK;
#ifdef O_ASYNC
   if (retropt_bool(opts, OPT_O_ASYNC, &flag) >= 0    && flag)
      flags |= O_ASYNC;
#endif
   if (retropt_bool(opts, OPT_O_TRUNC, &flag) >= 0  && flag)
      flags |= O_TRUNC;
#ifdef O_BINARY
   if (retropt_bool(opts, OPT_O_BINARY,    &flag) >= 0   && flag)
      flags |= O_BINARY;
#endif
#ifdef O_TEXT
   if (retropt_bool(opts, OPT_O_TEXT,      &flag) >= 0   && flag)
      flags |= O_TEXT;
#endif
#ifdef O_NOINHERIT
   if (retropt_bool(opts, OPT_O_NOINHERIT, &flag) >= 0   && flag)
      flags |= O_NOINHERIT;
#endif
#ifdef O_NOATIME
   if (retropt_bool(opts, OPT_O_NOATIME,   &flag) >= 0   && flag)
      flags |= O_NOATIME;
#endif

   retropt_modet(opts, OPT_PERM,      &mode);

   if ((fd = Open(path, flags, mode)) < 0) {
      Error4("open(\"%s\", 0%lo, 0%03o): %s",
	     path, flags, mode, strerror(errno));
      return STAT_RETRYLATER;
   }
   /*0 Info4("open(\"%s\", 0%o, 0%03o) -> %d", path, flags, mode, fd);*/
   applyopts_named(path, opts, PH_PASTOPEN);
#if 0
   applyopts_named(path, opts, PH_FD);
   applyopts(fd, opts, PH_FD);
   applyopts_cloexec(fd, opts);
#endif
   return fd;
}
Beispiel #3
0
/* open for writing:
   if the filesystem entry already exists, the data is appended
   if it does not exist, a file is created and the data is appended
*/
static int xioopen_open(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, unsigned groups, int dummy1, int dummy2, int dummy3) {
   const char *filename = argv[1];
   int rw = (xioflags & XIO_ACCMODE);
   bool exists;
   bool opt_unlink_close = false;
   int result;

   /* remove old file, or set user/permissions on old file; parse options */
   if ((result = _xioopen_named_early(argc, argv, fd, groups, &exists, opts)) < 0) {
      return result;
   }

   retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
   if (opt_unlink_close) {
      if ((fd->stream.unlink_close = strdup(filename)) == NULL) {
	 Error1("strdup(\"%s\"): out of memory", filename);
      }
      fd->stream.opt_unlink_close = true;
   }

   Notice3("opening %s \"%s\" for %s",
	   filetypenames[(result&S_IFMT)>>12], filename, ddirection[rw]);
   if ((result = _xioopen_open(filename, rw, opts)) < 0)
      return result;
   fd->stream.fd = result;

#if WITH_TERMIOS
   if (Isatty(fd->stream.fd)) {
      if (Tcgetattr(fd->stream.fd, &fd->stream.savetty) < 0) {
	 Warn2("cannot query current terminal settings on fd %d: %s",
	       fd->stream.fd, strerror(errno));
      } else {
	 fd->stream.ttyvalid = true;
      }
   }
#endif /* WITH_TERMIOS */

   applyopts_named(filename, opts, PH_FD);
   applyopts(fd->stream.fd, opts, PH_FD);
   applyopts_cloexec(fd->stream.fd, opts);

   applyopts_fchown(fd->stream.fd, opts);

   if ((result = _xio_openlate(&fd->stream, opts)) < 0)
      return result;

   return 0;
}
Beispiel #4
0
static int xioopen_creat(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, unsigned groups, int dummy1, int dummy2, int dummy3) {
   const char *filename = argv[1];
   int rw = (xioflags&XIO_ACCMODE);
   int fd;
   bool exists;
   bool opt_unlink_close = false;
   int result;

   /* remove old file, or set user/permissions on old file; parse options */
   if ((result = _xioopen_named_early(argc, argv, xfd, groups, &exists, opts)) < 0) {
      return result;
   }

   retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
   if (opt_unlink_close) {
      if ((xfd->stream.unlink_close = strdup(filename)) == NULL) {
	 Error1("strdup(\"%s\"): out of memory", filename);
      }
      xfd->stream.opt_unlink_close = true;
   }

   Notice2("creating regular file \"%s\" for %s", filename, ddirection[rw]);
   if ((fd = _xioopen_creat(filename, rw, opts)) < 0)
      return fd;
   if (XIOWITHRD(rw))  xfd->stream.rfd = fd;
   if (XIOWITHWR(rw))  xfd->stream.wfd = fd;

   applyopts_named(filename, opts, PH_PASTOPEN);
   if ((result = applyopts2(fd, opts, PH_PASTOPEN, PH_LATE2)) < 0)
      return result;

   applyopts_cloexec(fd, opts);

   applyopts_fchown(fd, opts);

   if ((result = _xio_openlate(&xfd->stream, opts)) < 0)
      return result;

   return 0;
}
Beispiel #5
0
static int xioopen_unix_listen(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int abstract, int dummy2, int dummy3) {
   /* we expect the form: filename */
   const char *name;
   xiosingle_t *xfd = &xxfd->stream;
   int pf = PF_UNIX;
   int socktype = SOCK_STREAM;
   int protocol = 0;
   struct sockaddr_un us;
   socklen_t uslen;
   bool tight = true;
   struct opt *opts0 = NULL;
   pid_t pid = Getpid();
   bool opt_unlink_early = false;
   bool opt_unlink_close = true;
   int result;

   if (argc != 2) {
      Error2("%s: wrong number of parameters (%d instead of 1)",
	     argv[0], argc-1);
      return STAT_NORETRY;
   }

   name = argv[1];
   retropt_socket_pf(opts, &pf);
   retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight);
   uslen = xiosetunix(pf, &us, name, abstract, tight);

   xfd->howtoend = END_SHUTDOWN;

   if (!(ABSTRACT && abstract)) {
      /* only for non abstract because abstract do not work in file system */
      retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
      retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
   }

   if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY;
   applyopts(-1, opts, PH_INIT);
   applyopts(-1, opts, PH_EARLY);

   if (!(ABSTRACT && abstract)) {
      if (opt_unlink_early) {
	 if (Unlink(name) < 0) {
	    if (errno == ENOENT) {
	       Warn2("unlink(\"%s\"): %s", name, strerror(errno));
	    } else {
	       Error2("unlink(\"%s\"): %s", name, strerror(errno));
	    }
	 }
      }

      /* trying to set user-early, perm-early etc. here is useless because
	 file system entry is available only past bind() call. */
      applyopts_named(name, opts, PH_EARLY);	/* umask! */
   }

   opts0 = copyopts(opts, GROUP_ALL);

   if ((result =
	xioopen_listen(xfd, xioflags,
		       (struct sockaddr *)&us, uslen,
		       opts, opts0, pf, socktype, protocol))
       != 0)
      return result;

   /* we set this option as late as now because we should not remove an
      existing entry when bind() failed */
   if (!(ABSTRACT && abstract)) {
      if (opt_unlink_close) {
	 if (pid == Getpid()) {
	    if ((xfd->unlink_close = strdup(name)) == NULL) {
	       Error1("strdup(\"%s\"): out of memory", name);
	    }
	    xfd->opt_unlink_close = true;
	 }
      }
   }
   return 0;
}
Beispiel #6
0
static int xioopen_tun(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, unsigned groups, int dummy1, int dummy2, int dummy3) {
   char *tundevice = NULL;
   char *tunname = NULL, *tuntype = NULL;
   int pf = /*! PF_UNSPEC*/ PF_INET;
   struct xiorange network;
   bool no_pi = false;
   const char *namedargv[] = { "tun", NULL, NULL };
   int rw = (xioflags & XIO_ACCMODE);
   bool exists;
   struct ifreq ifr;
   int sockfd;
   char *ifaddr;
   int result;

   if (argc > 2 || argc < 0) {
      Error2("%s: wrong number of parameters (%d instead of 0 or 1)",
	     argv[0], argc-1);
   }

   if (retropt_string(opts, OPT_TUN_DEVICE, &tundevice) != 0) {
      tundevice = strdup("/dev/net/tun");
   }

   /*! socket option here? */
   retropt_socket_pf(opts, &pf);

   namedargv[1] = tundevice;
   /* open the tun cloning device */
   if ((result = _xioopen_named_early(2, namedargv, xfd, groups, &exists, opts)) < 0) {
      return result;
   }

   /*========================= the tunnel interface =========================*/
   Notice("creating tunnel network interface");
   if ((result = _xioopen_open(tundevice, rw, opts)) < 0)
      return result;
   xfd->stream.fd = result;

   /* prepare configuration of the new network interface */
   memset(&ifr, 0,sizeof(ifr));

   if (retropt_string(opts, OPT_TUN_NAME, &tunname) == 0) {
      strncpy(ifr.ifr_name, tunname, IFNAMSIZ);	/* ok */
      free(tunname);
   } else {
      ifr.ifr_name[0] = '\0';
   }

   ifr.ifr_flags = IFF_TUN;
   if (retropt_string(opts, OPT_TUN_TYPE, &tuntype) == 0) {
      if (!strcmp(tuntype, "tap")) {
	 ifr.ifr_flags = IFF_TAP;
      } else if (strcmp(tuntype, "tun")) {
	 Error1("unknown tun-type \"%s\"", tuntype);
      }
   }

   if (retropt_bool(opts, OPT_IFF_NO_PI, &no_pi) == 0) {
      if (no_pi) {
	 ifr.ifr_flags |= IFF_NO_PI;
#if 0 /* not neccessary for now */
      } else {
	 ifr.ifr_flags &= ~IFF_NO_PI;
#endif
      }
   }

   if (Ioctl(xfd->stream.fd, TUNSETIFF, &ifr) < 0) {
      Error3("ioctl(%d, TUNSETIFF, {\"%s\"}: %s",
	     xfd->stream.fd, ifr.ifr_name, strerror(errno));
      Close(xfd->stream.fd);
   }

   /*===================== setting interface properties =====================*/

   /* we seem to need a socket for manipulating the interface */
   if ((sockfd = Socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
      Error1("socket(PF_INET, SOCK_DGRAM, 0): %s", strerror(errno));
      sockfd = xfd->stream.fd;	/* desparate fallback attempt */
   }

   /*--------------------- setting interface address and netmask ------------*/
   if (argc == 2) {
       if ((ifaddr = strdup(argv[1])) == NULL) {
          Error1("strdup(\"%s\"): out of memory", argv[1]);
          return STAT_RETRYLATER;
       }
       if ((result = xioparsenetwork(ifaddr, pf, &network)) != STAT_OK) {
          /*! recover */
          return result;
       }
       socket_init(pf, (union sockaddr_union *)&ifr.ifr_addr);
       ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr =
          network.netaddr.ip4.sin_addr;
       if (Ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) {
          Error4("ioctl(%d, SIOCSIFADDR, {\"%s\", \"%s\"}: %s",
             sockfd, ifr.ifr_name, ifaddr, strerror(errno));
       }
       ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr =
          network.netmask.ip4.sin_addr;
       if (Ioctl(sockfd, SIOCSIFNETMASK, &ifr) < 0) {
          Error4("ioctl(%d, SIOCSIFNETMASK, {\"0x%08u\", \"%s\"}, %s",
             sockfd, ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr,
             ifaddr, strerror(errno));
       }
       free(ifaddr);
   }
   /*--------------------- setting interface flags --------------------------*/
   applyopts_single(&xfd->stream, opts, PH_FD);

   if (Ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
      Error3("ioctl(%d, SIOCGIFFLAGS, {\"%s\"}: %s",
	     sockfd, ifr.ifr_name, strerror(errno));
   }
   Debug2("\"%s\": system set flags: 0x%hx", ifr.ifr_name, ifr.ifr_flags);
   ifr.ifr_flags |= xfd->stream.para.tun.iff_opts[0];
   ifr.ifr_flags &= ~xfd->stream.para.tun.iff_opts[1];
   Debug2("\"%s\": xio merged flags: 0x%hx", ifr.ifr_name, ifr.ifr_flags);
   if (Ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
      Error4("ioctl(%d, SIOCSIFFLAGS, {\"%s\", %hd}: %s",
	     sockfd, ifr.ifr_name, ifr.ifr_flags, strerror(errno));
   }
   ifr.ifr_flags = 0;
   if (Ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
      Error3("ioctl(%d, SIOCGIFFLAGS, {\"%s\"}: %s",
	     sockfd, ifr.ifr_name, strerror(errno));
   }
   Debug2("\"%s\": resulting flags: 0x%hx", ifr.ifr_name, ifr.ifr_flags);


#if LATER
   applyopts_named(tundevice, opts, PH_FD);
#endif
   applyopts(xfd->stream.fd, opts, PH_FD);
   applyopts_cloexec(xfd->stream.fd, opts);

   applyopts_fchown(xfd->stream.fd, opts);

   if ((result = _xio_openlate(&xfd->stream, opts)) < 0)
      return result;

   return 0;
}
Beispiel #7
0
/* creates the listening socket, bind, applies options; waits for incoming
   connection, checks its source address and port. Depending on fork option, it
   may fork a subprocess.
   pf specifies the syntax expected for range option. In the case of generic
   socket it is 0 (expecting raw binary data), and the real pf can be obtained
   from us->af_family; for other socket types pf == us->af_family
   Returns 0 if a connection was accepted; with fork option, this is always in
   a subprocess!
   Other return values indicate a problem; this can happen in the master
   process or in a subprocess.
   This function does not retry. If you need retries, handle this in a
   loop in the calling function (and always provide the options...)
   After fork, we set the forever/retry of the child process to 0
   applies and consumes the following option:
   PH_INIT, PH_PASTSOCKET, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY,
   PH_PREOPEN, PH_FD, PH_CONNECTED, PH_LATE, PH_LATE2
   OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_BACKLOG, OPT_RANGE, tcpwrap,
   OPT_SOURCEPORT, OPT_LOWPORT, cloexec
 */
int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, socklen_t uslen,
		 struct opt *opts, int pf, int socktype, int proto, int level) {
   struct sockaddr sa;
   socklen_t salen;
   int backlog = 5;	/* why? 1 seems to cause problems under some load */
   char *rangename;
   bool dofork = false;
   int maxchildren = 0;
   char infobuff[256];
   char lisname[256];
   union sockaddr_union _peername;
   union sockaddr_union _sockname;
   union sockaddr_union *pa = &_peername;	/* peer address */
   union sockaddr_union *la = &_sockname;	/* local address */
   socklen_t pas = sizeof(_peername);	/* peer address size */
   socklen_t las = sizeof(_sockname);	/* local address size */
   int result;

   retropt_bool(opts, OPT_FORK, &dofork);

   if (dofork) {
      if (!(xioflags & XIO_MAYFORK)) {
	 Error("option fork not allowed here");
	 return STAT_NORETRY;
      }
      xfd->flags |= XIO_DOESFORK;
   }

   retropt_int(opts, OPT_MAX_CHILDREN, &maxchildren);

   if (! dofork && maxchildren) {
       Error("option max-children not allowed without option fork");
       return STAT_NORETRY;
   }

   if (applyopts_single(xfd, opts, PH_INIT) < 0)  return -1;

   if (dofork) {
      xiosetchilddied();	/* set SIGCHLD handler */
   }

   if ((xfd->fd = xiosocket(opts, us->sa_family, socktype, proto, level)) < 0) {
      return STAT_RETRYLATER;
   }

   applyopts_cloexec(xfd->fd, opts);

   applyopts(xfd->fd, opts, PH_PREBIND);
   applyopts(xfd->fd, opts, PH_BIND);
   if (Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) {
      Msg4(level, "bind(%d, {%s}, "F_socklen"): %s", xfd->fd,
	   sockaddr_info(us, uslen, infobuff, sizeof(infobuff)), uslen,
	   strerror(errno));
      Close(xfd->fd);
      return STAT_RETRYLATER;
   }

#if WITH_UNIX
   if (us->sa_family == AF_UNIX) {
      applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_FD);
   }
#endif
   /* under some circumstances (e.g., TCP listen on port 0) bind() fills empty
      fields that we want to know. */
   salen = sizeof(sa);
   if (Getsockname(xfd->fd, us, &uslen) < 0) {
      Warn4("getsockname(%d, %p, {%d}): %s",
	    xfd->fd, &us, uslen, strerror(errno));
   }

   applyopts(xfd->fd, opts, PH_PASTBIND);
#if WITH_UNIX
   if (us->sa_family == AF_UNIX) {
      /*applyopts_early(((struct sockaddr_un *)us)->sun_path, opts);*/
      applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_EARLY);
      applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_PREOPEN);
   }
#endif /* WITH_UNIX */

#if WITH_IP4 /*|| WITH_IP6*/
   if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
      if (xioparserange(rangename, pf, &xfd->para.socket.range)
	  < 0) {
	 free(rangename);
	 return STAT_NORETRY;
      }
      free(rangename);
      xfd->para.socket.dorange = true;
   }
#endif

#if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP
   xio_retropt_tcpwrap(xfd, opts);
#endif /* && (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */

#if WITH_TCP || WITH_UDP
   if (retropt_ushort(opts, OPT_SOURCEPORT, &xfd->para.socket.ip.sourceport) >= 0) {
      xfd->para.socket.ip.dosourceport = true;
   }
   retropt_bool(opts, OPT_LOWPORT, &xfd->para.socket.ip.lowport);
#endif /* WITH_TCP || WITH_UDP */

   applyopts(xfd->fd, opts, PH_PRELISTEN);
   retropt_int(opts, OPT_BACKLOG, &backlog);
   if (Listen(xfd->fd, backlog) < 0) {
      Error3("listen(%d, %d): %s", xfd->fd, backlog, strerror(errno));
      return STAT_RETRYLATER;
   }

   if (xioopts.logopt == 'm') {
      Info("starting accept loop, switching to syslog");
      diag_set('y', xioopts.syslogfac);  xioopts.logopt = 'y';
   } else {
      Info("starting accept loop");
   }
   while (true) {	/* but we only loop if fork option is set */
      char peername[256];
      char sockname[256];
      int ps;		/* peer socket */

      pa = &_peername;
      la = &_sockname;
      salen = sizeof(struct sockaddr);
      do {
	 /*? int level = E_ERROR;*/
	 Notice1("listening on %s", sockaddr_info(us, uslen, lisname, sizeof(lisname)));
	 ps = Accept(xfd->fd, (struct sockaddr *)&sa, &salen);
	 if (ps >= 0) {
	    /*0 Info4("accept(%d, %p, {"F_Zu"}) -> %d", xfd->fd, &sa, salen, ps);*/
	    break;	/* success, break out of loop */
	 }
	 if (errno == EINTR) {
	    continue;
	 }
	 if (errno == ECONNABORTED) {
	    Notice4("accept(%d, %p, {"F_socklen"}): %s",
		    xfd->fd, &sa, salen, strerror(errno));
	    continue;
	 }
	 Msg4(level, "accept(%d, %p, {"F_socklen"}): %s",
	      xfd->fd, &sa, salen, strerror(errno));
	 Close(xfd->fd);
	 return STAT_RETRYLATER;
      } while (true);
      applyopts_cloexec(ps, opts);
      if (Getpeername(ps, &pa->soa, &pas) < 0) {
	 Warn4("getpeername(%d, %p, {"F_socklen"}): %s",
	       ps, pa, pas, strerror(errno));
	 pa = NULL;
      }
      if (Getsockname(ps, &la->soa, &las) < 0) {
	 Warn4("getsockname(%d, %p, {"F_socklen"}): %s",
	       ps, la, las, strerror(errno));
	 la = NULL;
      }
      Notice2("accepting connection from %s on %s",
	      pa?
	      sockaddr_info(&pa->soa, pas, peername, sizeof(peername)):"NULL",
	      la?
	      sockaddr_info(&la->soa, las, sockname, sizeof(sockname)):"NULL");

      if (pa != NULL && la != NULL && xiocheckpeer(xfd, pa, la) < 0) {
	 if (Shutdown(ps, 2) < 0) {
	    Info2("shutdown(%d, 2): %s", ps, strerror(errno));
	 }
	 Close(ps);
	 continue;
      }

      if (pa != NULL)
	 Info1("permitting connection from %s",
	       sockaddr_info((struct sockaddr *)pa, pas,
			     infobuff, sizeof(infobuff)));

      if (dofork) {
	 pid_t pid;	/* mostly int; only used with fork */
         sigset_t mask_sigchld;

         /* we must prevent that the current packet triggers another fork;
            therefore we wait for a signal from the recent child: USR1
            indicates that is has consumed the last packet; CHLD means it has
            terminated */
         /* block SIGCHLD and SIGUSR1 until parent is ready to react */
         sigemptyset(&mask_sigchld);
         sigaddset(&mask_sigchld, SIGCHLD);
         Sigprocmask(SIG_BLOCK, &mask_sigchld, NULL);

	 if ((pid = xio_fork(false, level==E_ERROR?level:E_WARN)) < 0) {
	    Close(xfd->fd);
	    Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
	    return STAT_RETRYLATER;
	 }
	 if (pid == 0) {	/* child */
	    pid_t cpid = Getpid();
	    Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);

	    Info1("just born: child process "F_pid, cpid);
	    xiosetenvulong("PID", cpid, 1);

	    if (Close(xfd->fd) < 0) {
	       Info2("close(%d): %s", xfd->fd, strerror(errno));
	    }
	    xfd->fd = ps;

#if WITH_RETRY
	    /* !? */
	    xfd->forever = false;  xfd->retry = 0;
	    level = E_ERROR;
#endif /* WITH_RETRY */

	    break;
	 }

	 /* server: continue loop with listen */
	 /* shutdown() closes the socket even for the child process, but
	    close() does what we want */
	 if (Close(ps) < 0) {
	    Info2("close(%d): %s", ps, strerror(errno));
	 }

         /* now we are ready to handle signals */
         Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);

	 while (maxchildren) {
	    if (num_child < maxchildren) break;
	    Notice("maxchildren are active, waiting");
	    /* UINT_MAX would even be nicer, but Openindiana works only
	       with 31 bits */
	    while (!Sleep(INT_MAX)) ;	/* any signal lets us continue */
	 }
	 Info("still listening");
      } else {
	 if (Close(xfd->fd) < 0) {
	    Info2("close(%d): %s", xfd->fd, strerror(errno));
	 }
	 xfd->fd = ps;
	break;
      }
   }

   applyopts(xfd->fd, opts, PH_FD);
   applyopts(xfd->fd, opts, PH_PASTSOCKET);
   applyopts(xfd->fd, opts, PH_CONNECTED);
   if ((result = _xio_openlate(xfd, opts)) < 0)
      return result;

   /* set the env vars describing the local and remote sockets */
   if (la != NULL)  xiosetsockaddrenv("SOCK", la, las, proto);
   if (pa != NULL)  xiosetsockaddrenv("PEER", pa, pas, proto);

   return 0;
}
Beispiel #8
0
static
int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts,
		      int xioflags, xiofile_t *xxfd, unsigned groups,
		      int abstract, int dummy2, int dummy3) {
   /* we expect the form: filename */
   const char *name;
   xiosingle_t *xfd = &xxfd->stream;
   int pf = PF_UNIX;
   int socktype = SOCK_DGRAM;
   int protocol = 0;
   union sockaddr_union us;
   socklen_t uslen;
   bool tight = true;
   bool opt_unlink_early = false;
   bool opt_unlink_close = true;
   int result;

   if (argc != 2) {
      Error2("%s: wrong number of parameters (%d instead of 1)",
	     argv[0], argc-1);
      return STAT_NORETRY;
   }

   name = argv[1];
   retropt_socket_pf(opts, &pf);
   retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight);
   uslen = xiosetunix(pf, &us.un, name, abstract, tight);

#if 1	/*!!! why bind option? */
   retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, 1, 0, 0);
#endif

   if (!(ABSTRACT && abstract)) {
      /* only for non abstract because abstract do not work in file system */
      retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
      retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);

      if (opt_unlink_early) {
	 if (Unlink(name) < 0) {
	    if (errno == ENOENT) {
	       Warn2("unlink(\"%s\"): %s", name, strerror(errno));
	    } else {
	       Error2("unlink(\"%s\"): %s", name, strerror(errno));
	    }
	 }
      } else {
	 struct stat buf;
	 if (Lstat(name, &buf) == 0) {
	    Error1("\"%s\" exists", name);
	    return STAT_RETRYLATER;
	 }
      }
      if (opt_unlink_close) {
	 if ((xfd->unlink_close = strdup(name)) == NULL) {
	    Error1("strdup(\"%s\"): out of memory", name);
	 }
	 xfd->opt_unlink_close = true;
      }
   }
   applyopts_named(name, opts, PH_EARLY);	/* umask! */

   xfd->para.socket.la.soa.sa_family = pf;

   xfd->dtype = XIODATA_RECV;
   result = _xioopen_dgram_recv(xfd, xioflags, &us.soa, uslen,
				opts, pf, socktype, protocol, E_ERROR);
   return result;
}
Beispiel #9
0
static
int xioopen_unix_recvfrom(int argc, const char *argv[], struct opt *opts,
		     int xioflags, xiofile_t *xxfd, unsigned groups,
		     int abstract, int dummy2, int dummy3) {
   /* we expect the form: filename */
   const char *name;
   xiosingle_t *xfd = &xxfd->stream;
   int pf = PF_UNIX;
   int socktype = SOCK_DGRAM;
   int protocol = 0;
   struct sockaddr_un us;
   socklen_t uslen;
   bool tight = true;
   bool needbind = true;
   bool opt_unlink_early = false;
   bool opt_unlink_close = true;

   if (argc != 2) {
      Error2("%s: wrong number of parameters (%d instead of 1)",
	     argv[0], argc-1);
      return STAT_NORETRY;
   }

   name = argv[1];
   retropt_socket_pf(opts, &pf);
   retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight);
   uslen = xiosetunix(pf, &us, name, abstract, tight);

   xfd->howtoend = END_NONE;
   retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen,
		1, 0, 0);

   if (!(ABSTRACT && abstract)) {
      /* only for non abstract because abstract do not work in file system */
      retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
      retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
   }

   if (!(ABSTRACT && abstract)) {
      if (opt_unlink_early) {
	 if (Unlink(name) < 0) {
	    if (errno == ENOENT) {
	       Warn2("unlink(\"%s\"): %s", name, strerror(errno));
	    } else {
	       Error2("unlink(\"%s\"): %s", name, strerror(errno));
	    }
	 }
      } else {
	 struct stat buf;
	 if (Lstat(name, &buf) == 0) {
	    Error1("\"%s\" exists", name);
	    return STAT_RETRYLATER;
	 }
      }
      if (opt_unlink_close) {
	 if ((xfd->unlink_close = strdup(name)) == NULL) {
	    Error1("strdup(\"%s\"): out of memory", name);
	 }
	 xfd->opt_unlink_close = true;
      }

      /* trying to set user-early, perm-early etc. here is useless because
	 file system entry is available only past bind() call. */
   }
   applyopts_named(name, opts, PH_EARLY);	/* umask! */

   xfd->para.socket.la.soa.sa_family = pf;

   xfd->dtype = XIODATA_RECVFROM_ONE;

   /* this may fork */
   return
      _xioopen_dgram_recvfrom(xfd, xioflags,
			      needbind?(struct sockaddr *)&us:NULL, uslen,
			      opts, pf, socktype, protocol, E_ERROR);
}
Beispiel #10
0
/* open a named or unnamed pipe/fifo */
static int xioopen_fifo(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, unsigned groups, int dummy1, int dummy2, int dummy3) {
   const char *pipename = argv[1];
   int rw = (xioflags & XIO_ACCMODE);
#if HAVE_STAT64
   struct stat64 pipstat;
#else
   struct stat pipstat;
#endif /* !HAVE_STAT64 */
   bool opt_unlink_early = false;
   bool opt_unlink_close = true;
   mode_t mode = 0666;
   int result;

   if (argc == 1) {
      return xioopen_fifo_unnamed(fd, fd->stream.opts);
   }

   if (argc != 2) {
      Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1);
   }

   if (applyopts_single(&fd->stream, opts, PH_INIT) < 0)  return -1;
   applyopts(-1, opts, PH_INIT);

   retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
   applyopts_named(pipename, opts, PH_EARLY);	/* umask! */
   applyopts(-1, opts, PH_EARLY);

   if (opt_unlink_early) {
      if (Unlink(pipename) < 0) {
	 if (errno == ENOENT) {
	    Warn2("unlink(%s): %s", pipename, strerror(errno));
	 } else {
	    Error2("unlink(%s): %s", pipename, strerror(errno));
	    return STAT_RETRYLATER;
	 }
      }
   }

   retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
   retropt_modet(opts, OPT_PERM, &mode);
   if (applyopts_named(pipename, opts, PH_EARLY) < 0) {
      return STAT_RETRYLATER;
   }
   if (applyopts_named(pipename, opts, PH_PREOPEN) < 0) {
      return STAT_RETRYLATER;
   }
   if (
#if HAVE_STAT64
       Stat64(pipename, &pipstat) < 0
#else
       Stat(pipename, &pipstat) < 0
#endif /* !HAVE_STAT64 */
      ) {
      if (errno != ENOENT) {
	 Error3("stat(\"%s\", %p): %s", pipename, &pipstat, strerror(errno));
      } else {
	 Debug1("xioopen_fifo(\"%s\"): does not exist, creating fifo", pipename);
#if 0
	 result = Mknod(pipename, S_IFIFO|mode, 0);
	 if (result < 0) {
	    Error3("mknod(%s, %d, 0): %s", pipename, mode, strerror(errno));
	    return STAT_RETRYLATER;
	 }
#else
	 result = Mkfifo(pipename, mode);
	 if (result < 0) {
	    Error3("mkfifo(%s, %d): %s", pipename, mode, strerror(errno));
	    return STAT_RETRYLATER;
	 }
#endif
	 Notice2("created named pipe \"%s\" for %s", pipename, ddirection[rw]);
	 applyopts_named(pipename, opts, PH_ALL);

      }
      if (opt_unlink_close) {
	 if ((fd->stream.unlink_close = strdup(pipename)) == NULL) {
	    Error1("strdup(\"%s\"): out of memory", pipename);
	 }
	 fd->stream.opt_unlink_close = true;
      }
   } else {
      /* exists */
      Debug1("xioopen_fifo(\"%s\"): already exist, opening it", pipename);
      Notice3("opening %s \"%s\" for %s",
	      filetypenames[(pipstat.st_mode&S_IFMT)>>12],
	      pipename, ddirection[rw]);
      /*applyopts_early(pipename, opts);*/
      applyopts_named(pipename, opts, PH_EARLY);
   }

   if ((result = _xioopen_open(pipename, rw, opts)) < 0) {
      return result;
   }
   fd->stream.fd = result;

   applyopts_named(pipename, opts, PH_FD);
   applyopts(fd->stream.fd, opts, PH_FD);
   applyopts_cloexec(fd->stream.fd, opts);
   return _xio_openlate(&fd->stream, opts);
}
Beispiel #11
0
static int xioopen_pty(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, unsigned groups, int dummy1, int dummy2, int dummy3) {
   /* we expect the form: filename */
   int ptyfd = -1, ttyfd = -1;
#if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)
   bool useptmx = false;	/* use /dev/ptmx or equivalent */
#endif
#if HAVE_OPENPTY
   bool useopenpty = false;	/* try only openpty */
#endif	/* HAVE_OPENPTY */
   char ptyname[MAXPTYNAMELEN];
   char *tn = NULL;
   char *linkname = NULL;
   bool opt_unlink_close = true;	/* remove symlink afterwards */
   bool wait_slave = false;	/* true would be better for many platforms, but
				   some OSes cannot handle this, and for common
				   default behaviour as well as backward 
				   compatibility we choose "no" as default */
   struct timespec pollintv = { PTY_INTERVALL };

   if (argc != 1) {
      Error2("%s: wrong number of parameters (%d instead of 0)", argv[0], argc-1);
   }

   xfd->stream.howtoend = END_CLOSE;

   if (applyopts_single(&xfd->stream, opts, PH_INIT) < 0)  return -1;
   applyopts(-1, opts, PH_INIT);

   retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);

   /* trying to set user-early, perm-early etc. here might be useless because
      file system entry is eventually available only past pty creation */
   /* name not yet known; umask should not be handled with this function! */
   /* umask does not affect resulting mode, on Linux 2.4 */
   applyopts_named("", opts, PH_EARLY);	/* umask! */

#if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)
   retropt_bool(opts, OPT_PTMX, &useptmx);
#endif
#if HAVE_OPENPTY
   retropt_bool(opts, OPT_OPENPTY, &useopenpty);
#endif

#if (defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC))
#  if  HAVE_OPENPTY
   useopenpty = !useptmx;
#  else /* !HAVE_OPENPTY */
   useptmx = true;
#  endif /* !HAVE_OPENPTY */
#else
# if HAVE_OPENPTY
   useopenpty = true;
# endif /* HAVE_OPENPTY */
#endif /* ! (defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)) */

#if HAVE_POLL
   retropt_bool(opts, OPT_PTY_WAIT_SLAVE, &wait_slave);
   retropt_timespec(opts, OPT_PTY_INTERVALL, &pollintv);
#endif /* HAVE_POLL */

   if (applyopts_single(&xfd->stream, opts, PH_INIT) < 0)  return -1;
   applyopts2(-1, opts, PH_INIT, PH_EARLY);

   applyopts(-1, opts, PH_PREBIGEN);

#if defined(HAVE_DEV_PTMX)
#  define PTMX "/dev/ptmx"	/* Linux */
#elif HAVE_DEV_PTC
#  define PTMX "/dev/ptc"	/* AIX 4.3.3 */
#endif
#if HAVE_DEV_PTMX || HAVE_DEV_PTC
   if (useptmx) {
      if ((ptyfd = Open(PTMX, O_RDWR|O_NOCTTY, 0620)) < 0) {
	 Warn1("open(\""PTMX"\", O_RDWR|O_NOCTTY, 0620): %s",
	       strerror(errno));
	 /*!*/
      } else {
	 ;/*0 Info1("open(\""PTMX"\", O_RDWR|O_NOCTTY, 0620) -> %d", ptyfd);*/
      }
      if (ptyfd >= 0 && ttyfd < 0) {
	 /* we used PTMX before forking */
	 /*0 extern char *ptsname(int);*/
#if HAVE_GRANTPT	/* AIX, not Linux */
	 if (Grantpt(ptyfd)/*!*/ < 0) {
	    Warn2("grantpt(%d): %s", ptyfd, strerror(errno));
	 }
#endif /* HAVE_GRANTPT */
#if HAVE_UNLOCKPT
	 if (Unlockpt(ptyfd)/*!*/ < 0) {
	    Warn2("unlockpt(%d): %s", ptyfd, strerror(errno));
	 }
#endif /* HAVE_UNLOCKPT */
#if HAVE_PROTOTYPE_LIB_ptsname	/* AIX, not Linux */
	 if ((tn = Ptsname(ptyfd)) == NULL) {
	    Warn2("ptsname(%d): %s", ptyfd, strerror(errno));
	 } else {
	    Notice1("PTY is %s", tn);
	 }
#endif /* HAVE_PROTOTYPE_LIB_ptsname */
	 if (tn == NULL) {
	    if ((tn = Ttyname(ptyfd)) == NULL) {
	       Warn2("ttyname(%d): %s", ptyfd, strerror(errno));
	    }
	 }
	 ptyname[0] = '\0'; strncat(ptyname, tn, MAXPTYNAMELEN-1);
      }
   }
#endif /* HAVE_DEV_PTMX || HAVE_DEV_PTC */
#if HAVE_OPENPTY
   if (ptyfd < 0) {
      int result;
      if ((result = Openpty(&ptyfd, &ttyfd, ptyname, NULL, NULL)) < 0) {
	 Error4("openpty(%p, %p, %p, NULL, NULL): %s",
		&ptyfd, &ttyfd, ptyname, strerror(errno));
	 return -1;
      }
      Notice1("PTY is %s", ptyname);
   }
#endif /* HAVE_OPENPTY */

   if (!retropt_string(opts, OPT_SYMBOLIC_LINK, &linkname)) {
      if (Unlink(linkname) < 0 && errno != ENOENT) {
	 Error2("unlink(\"%s\"): %s", linkname, strerror(errno));
      }
      if (Symlink(ptyname, linkname) < 0) {
	 Error3("symlink(\"%s\", \"%s\"): %s",
		ptyname, linkname, strerror(errno));
      }
      if (opt_unlink_close) {
	 if ((xfd->stream.unlink_close = strdup(linkname)) == NULL) {
	    Error1("strdup(\"%s\"): out of memory", linkname);
	 }
	 xfd->stream.opt_unlink_close = true;
      }
   }

   applyopts_named(ptyname, opts, PH_PASTOPEN);
   applyopts_named(ptyname, opts, PH_FD);

   applyopts_cloexec(ptyfd, opts);/*!*/
   xfd->stream.dtype    = XIODATA_PTY;

   applyopts(ptyfd, opts, PH_FD);

   {
      /* special handling of user-late etc.; with standard behaviour (up to
	 1.7.1.1) they affected /dev/ptmx instead of /dev/pts/N */
      uid_t uid = -1, gid = -1;
      mode_t perm;

      bool dont;
      dont = retropt_uid(opts, OPT_USER_LATE, &uid);
      dont &= retropt_gid(opts, OPT_GROUP_LATE, &gid);

      if (!dont) {
	 if (Chown(ptyname, uid, gid) < 0) {
	    Error4("chown(\"%s\", %d, %d): %s",
		   ptyname, uid, gid, strerror(errno));
	 }
      }

      if (retropt_mode(opts, OPT_PERM_LATE, &perm) == 0) {
	 if (Chmod(ptyname, perm) < 0) {
	    Error3("chmod(\"%s\", %03o): %s",
		   ptyname, perm, strerror(errno));
	 }
      }

   }

   xfd->stream.fd = ptyfd;
   applyopts(ptyfd, opts, PH_LATE);
   if (applyopts_single(&xfd->stream, opts, PH_LATE) < 0)  return -1;

#if HAVE_POLL
   /* if you can and wish: */
   if (wait_slave) {
      /* try to wait until someone opens the slave side of the pty */
      /* we want to get a HUP (hangup) condition on the pty */
#if HAVE_DEV_PTMX || HAVE_DEV_PTC
      if (useptmx) {
	 ttyfd = Open(tn, O_RDWR|O_NOCTTY, 0620);
	 Close(ttyfd);
      }
#endif
#if HAVE_OPENPTY
      if (useopenpty) {
	 Close(ttyfd);
      }
#endif /* HAVE_OPENPTY */

      /* now we poll until the HUP vanishes - this indicates a slave conn. */
      while (true) {
	 struct pollfd ufd;
	 ufd.fd = ptyfd;
	 ufd.events = (POLLHUP);
	 if (Poll(&ufd, 1, 0) < 0) {
	    Error3("poll({%d, 0x%04hu,}, 1, 0): %s",
		   ufd.fd, ufd.events, strerror(errno));
	    /*! close something */
	    return -1;
	 }
	 if (!(ufd.revents & POLLHUP)) {
	    break;
	 }
	 Nanosleep(&pollintv, NULL);
	 continue;
      }
   }
#endif /* HAVE_POLL */

   return STAT_OK;
}