/* * Removes port entries that no longer have devices * backing them * Schedules an update the sacadm (portmon) database */ static void rm_dangling_port(char *devname) { char *portstr; int portnum; devfsadm_print(PORT_MID, "%s:rm_stale_port: %s\n", modname, devname); if ((portstr = strrchr(devname, (int)'/')) == NULL) { devfsadm_errprint("%s: invalid name: %s\n", modname, devname); return; } portstr++; /* * mark for removal from sacadm database */ if ((portnum = parse_portno(portstr)) != -1) pma[portnum].flags |= PORT_REMOVED; devfsadm_rm_all(devname); }
static gpgrt_ssize_t fun_writer (void *cookie_arg, const void *buffer, size_t size) { struct fun_cookie_s *cookie = cookie_arg; /* FIXME: Use only estream with a callback for socket writing. This avoids the ugly mix of fd and estream code. */ /* Note that we always try to reconnect to the socket but print error messages only the first time an error occurred. If RUNNING_DETACHED is set we don't fall back to stderr and even do not print any error messages. This is needed because detached processes often close stderr and by writing to file descriptor 2 we might send the log message to a file not intended for logging (e.g. a pipe or network connection). */ if (cookie->want_socket && cookie->fd == -1) { #ifdef WITH_IPV6 struct sockaddr_in6 srvr_addr_in6; #endif struct sockaddr_in srvr_addr_in; #ifndef HAVE_W32_SYSTEM struct sockaddr_un srvr_addr_un; #endif size_t addrlen; struct sockaddr *srvr_addr = NULL; unsigned short port = 0; int af = AF_LOCAL; int pf = PF_LOCAL; const char *name = cookie->name; /* Not yet open or meanwhile closed due to an error. */ cookie->is_socket = 0; /* Check whether this is a TCP socket or a local socket. */ if (!strncmp (name, "tcp://", 6) && name[6]) { name += 6; af = AF_INET; pf = PF_INET; } #ifndef HAVE_W32_SYSTEM else if (!strncmp (name, "socket://", 9) && name[9]) name += 9; #endif if (af == AF_LOCAL) { #ifdef HAVE_W32_SYSTEM addrlen = 0; #else memset (&srvr_addr, 0, sizeof srvr_addr); srvr_addr_un.sun_family = af; strncpy (srvr_addr_un.sun_path, name, sizeof (srvr_addr_un.sun_path)-1); srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path)-1] = 0; srvr_addr = (struct sockaddr *)&srvr_addr_un; addrlen = SUN_LEN (&srvr_addr_un); #endif } else { char *addrstr, *p; #ifdef HAVE_INET_PTON void *addrbuf = NULL; #endif /*HAVE_INET_PTON*/ addrstr = xtrymalloc (strlen (name) + 1); if (!addrstr) addrlen = 0; /* This indicates an error. */ else if (*name == '[') { /* Check for IPv6 literal address. */ strcpy (addrstr, name+1); p = strchr (addrstr, ']'); if (!p || p[1] != ':' || !parse_portno (p+2, &port)) { gpg_err_set_errno (EINVAL); addrlen = 0; } else { *p = 0; #ifdef WITH_IPV6 af = AF_INET6; pf = PF_INET6; memset (&srvr_addr_in6, 0, sizeof srvr_addr_in6); srvr_addr_in6.sin6_family = af; srvr_addr_in6.sin6_port = htons (port); #ifdef HAVE_INET_PTON addrbuf = &srvr_addr_in6.sin6_addr; #endif /*HAVE_INET_PTON*/ srvr_addr = (struct sockaddr *)&srvr_addr_in6; addrlen = sizeof srvr_addr_in6; #else gpg_err_set_errno (EAFNOSUPPORT); addrlen = 0; #endif } } else { /* Check for IPv4 literal address. */ strcpy (addrstr, name); p = strchr (addrstr, ':'); if (!p || !parse_portno (p+1, &port)) { gpg_err_set_errno (EINVAL); addrlen = 0; } else { *p = 0; memset (&srvr_addr_in, 0, sizeof srvr_addr_in); srvr_addr_in.sin_family = af; srvr_addr_in.sin_port = htons (port); #ifdef HAVE_INET_PTON addrbuf = &srvr_addr_in.sin_addr; #endif /*HAVE_INET_PTON*/ srvr_addr = (struct sockaddr *)&srvr_addr_in; addrlen = sizeof srvr_addr_in; } } if (addrlen) { #ifdef HAVE_INET_PTON if (inet_pton (af, addrstr, addrbuf) != 1) addrlen = 0; #else /*!HAVE_INET_PTON*/ /* We need to use the old function. If we are here v6 support isn't enabled anyway and thus we can do fine without. Note that Windows has a compatible inet_pton function named inetPton, but only since Vista. */ srvr_addr_in.sin_addr.s_addr = inet_addr (addrstr); if (srvr_addr_in.sin_addr.s_addr == INADDR_NONE) addrlen = 0; #endif /*!HAVE_INET_PTON*/ } xfree (addrstr); } cookie->fd = addrlen? socket (pf, SOCK_STREAM, 0) : -1; if (cookie->fd == -1) { if (!cookie->quiet && !running_detached && isatty (es_fileno (es_stderr))) es_fprintf (es_stderr, "failed to create socket for logging: %s\n", strerror(errno)); } else { if (connect (cookie->fd, srvr_addr, addrlen) == -1) { if (!cookie->quiet && !running_detached && isatty (es_fileno (es_stderr))) es_fprintf (es_stderr, "can't connect to '%s': %s\n", cookie->name, strerror(errno)); sock_close (cookie->fd); cookie->fd = -1; } } if (cookie->fd == -1) { if (!running_detached) { /* Due to all the problems with apps not running detached but being called with stderr closed or used for a different purposes, it does not make sense to switch to stderr. We therefore disable it. */ if (!cookie->quiet) { /* fputs ("switching logging to stderr\n", stderr);*/ cookie->quiet = 1; } cookie->fd = -1; /*fileno (stderr);*/ } } else /* Connection has been established. */ { cookie->quiet = 0; cookie->is_socket = 1; } } log_socket = cookie->fd; if (cookie->fd != -1) { #ifdef HAVE_W32CE_SYSTEM if (cookie->use_writefile) { DWORD nwritten; WriteFile ((HANDLE)cookie->fd, buffer, size, &nwritten, NULL); return (gpgrt_ssize_t)size; /* Okay. */ } #endif if (!writen (cookie->fd, buffer, size, cookie->is_socket)) return (gpgrt_ssize_t)size; /* Okay. */ } if (!running_detached && cookie->fd != -1 && isatty (es_fileno (es_stderr))) { if (*cookie->name) es_fprintf (es_stderr, "error writing to '%s': %s\n", cookie->name, strerror(errno)); else es_fprintf (es_stderr, "error writing to file descriptor %d: %s\n", cookie->fd, strerror(errno)); } if (cookie->is_socket && cookie->fd != -1) { sock_close (cookie->fd); cookie->fd = -1; log_socket = -1; } return (gpgrt_ssize_t)size; }
/* Make a connection to the Unix domain socket NAME and return a new Assuan context in CTX. SERVER_PID is currently not used but may become handy in the future. Defined flag bits are: ASSUAN_SOCKET_CONNECT_FDPASSING sendmsg and recvmsg are used. NAME must either start with a slash and optional with a drive prefix ("c:") or use one of these URL schemata: file://<fname> This is the same as the default just with an explicit schemata. assuan://<ipaddr>:<port> assuan://[<ip6addr>]:<port> Connect using TCP to PORT of the server with the numerical IPADDR. Note that '[' and ']' are literal characters. */ gpg_error_t assuan_socket_connect (assuan_context_t ctx, const char *name, pid_t server_pid, unsigned int flags) { gpg_error_t err = 0; assuan_fd_t fd; #ifdef WITH_IPV6 struct sockaddr_in6 srvr_addr_in6; #endif struct sockaddr_un srvr_addr_un; struct sockaddr_in srvr_addr_in; struct sockaddr *srvr_addr = NULL; uint16_t port = 0; size_t len = 0; const char *s; int af = AF_LOCAL; int pf = PF_LOCAL; TRACE2 (ctx, ASSUAN_LOG_CTX, "assuan_socket_connect", ctx, "name=%s, flags=0x%x", name ? name : "(null)", flags); if (!ctx || !name) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!strncmp (name, "file://", 7) && name[7]) name += 7; else if (!strncmp (name, "assuan://", 9) && name[9]) { name += 9; af = AF_INET; pf = PF_INET; } else /* Default. */ { /* We require that the name starts with a slash if no URL schemata is used. To make things easier we allow an optional drive prefix. */ s = name; if (*s && s[1] == ':') s += 2; if (*s != DIRSEP_C && *s != '/') return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); } if (af == AF_LOCAL) { if (strlen (name)+1 >= sizeof srvr_addr_un.sun_path) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); memset (&srvr_addr_un, 0, sizeof srvr_addr_un); srvr_addr_un.sun_family = AF_LOCAL; strncpy (srvr_addr_un.sun_path, name, sizeof (srvr_addr_un.sun_path) - 1); srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path) - 1] = 0; len = SUN_LEN (&srvr_addr_un); srvr_addr = (struct sockaddr *)&srvr_addr_un; } else { char *addrstr, *p; #ifdef HAVE_INET_PTON void *addrbuf = NULL; #endif addrstr = _assuan_malloc (ctx, strlen (name) + 1); if (!addrstr) return _assuan_error (ctx, gpg_err_code_from_syserror ()); if (*name == '[') { strcpy (addrstr, name+1); p = strchr (addrstr, ']'); if (!p || p[1] != ':' || !parse_portno (p+2, &port)) err = _assuan_error (ctx, GPG_ERR_BAD_URI); else { *p = 0; #ifdef WITH_IPV6 af = AF_INET6; pf = PF_INET6; memset (&srvr_addr_in6, 0, sizeof srvr_addr_in6); srvr_addr_in6.sin6_family = af; srvr_addr_in6.sin6_port = htons (port); #ifdef HAVE_INET_PTON addrbuf = &srvr_addr_in6.sin6_addr; #endif srvr_addr = (struct sockaddr *)&srvr_addr_in6; len = sizeof srvr_addr_in6; #else err = _assuan_error (ctx, GPG_ERR_EAFNOSUPPORT); #endif } } else { strcpy (addrstr, name); p = strchr (addrstr, ':'); if (!p || !parse_portno (p+1, &port)) err = _assuan_error (ctx, GPG_ERR_BAD_URI); else { *p = 0; memset (&srvr_addr_in, 0, sizeof srvr_addr_in); srvr_addr_in.sin_family = af; srvr_addr_in.sin_port = htons (port); #ifdef HAVE_INET_PTON addrbuf = &srvr_addr_in.sin_addr; #endif srvr_addr = (struct sockaddr *)&srvr_addr_in; len = sizeof srvr_addr_in; } } if (!err) { #ifdef HAVE_INET_PTON switch (inet_pton (af, addrstr, addrbuf)) { case 1: break; case 0: err = _assuan_error (ctx, GPG_ERR_BAD_URI); break; default: err = _assuan_error (ctx, gpg_err_code_from_syserror ()); } #else /*!HAVE_INET_PTON*/ /* We need to use the old function. If we are here v6 support isn't enabled anyway and thus we can do fine without. Note that Windows as a compatible inet_pton function named inetPton, but only since Vista. */ srvr_addr_in.sin_addr.s_addr = inet_addr (addrstr); if (srvr_addr_in.sin_addr.s_addr == INADDR_NONE) err = _assuan_error (ctx, GPG_ERR_BAD_URI); #endif /*!HAVE_INET_PTON*/ } _assuan_free (ctx, addrstr); if (err) return err; } fd = _assuan_sock_new (ctx, pf, SOCK_STREAM, 0); if (fd == ASSUAN_INVALID_FD) { err = _assuan_error (ctx, gpg_err_code_from_syserror ()); TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't create socket: %s", strerror (errno)); return err; } if (_assuan_sock_connect (ctx, fd, srvr_addr, len) == -1) { TRACE2 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't connect to `%s': %s\n", name, strerror (errno)); _assuan_close (ctx, fd); return _assuan_error (ctx, GPG_ERR_ASS_CONNECT_FAILED); } err = _assuan_connect_finalize (ctx, fd, flags); if (err) _assuan_reset (ctx); return err; }
/* * Determine which port monitor entries already exist by invoking pmadm(1m) * to list all configured 'ttymon' port monitor entries. * Do not explicitly report errors from executing pmadm(1m) or sacadm(1m) * commands to remain compatible with the ports(1m) implementation. */ static int load_ttymondb(void) { char cmdline[CMDLEN]; char cmdbuf[PMTAB_MAXLINE+1]; int sac_exitval; FILE *fs_popen; char *portname; /* pointer to a tty name */ int portnum; char *ptr; char *error_msg = "%s: failed to load port monitor database\n"; (void) strcpy(cmdline, "/usr/sbin/pmadm -L -t ttymon"); fs_popen = popen(cmdline, "r"); if (fs_popen == NULL) { devfsadm_print(VERBOSE_MID, error_msg, modname); return (DEVFSADM_FAILURE); } while (fgets(cmdbuf, PMTAB_MAXLINE, fs_popen) != NULL) { if ((portname = pmtab_parse_portname(cmdbuf)) == NULL) { devfsadm_print(VERBOSE_MID, "load_ttymondb: failed to parse portname\n"); devfsadm_print(VERBOSE_MID, "load_ttymondb: buffer \"%s\"\n", cmdbuf); goto load_failed; } devfsadm_print(PORT_MID, "%s:load_ttymondb: port %s ", modname, portname); /* * skip onboard ports * There is no reliable way to determine if we * should start a port monitor on these lines. */ if ((portnum = parse_portno(portname)) == -1) { devfsadm_print(PORT_MID, "ignored\n"); continue; } /* * the first field of the pmadm output is * the port monitor name for this entry */ if ((ptr = strchr(cmdbuf, PMTAB_SEPR)) == NULL) { devfsadm_print(VERBOSE_MID, "load_ttymondb: no portmon tag\n"); goto load_failed; } *ptr = MN_NULLCHAR; if ((pma[portnum].pm_tag = strdup(cmdbuf)) == NULL) { devfsadm_errprint("load_ttymondb: failed strdup\n"); goto load_failed; } pma[portnum].flags |= PM_HAS_ENTRY; pma[PM_SLOT(portnum)].flags |= HAS_PORT_MON; devfsadm_print(PORT_MID, "present\n"); } (void) pclose(fs_popen); return (DEVFSADM_SUCCESS); load_failed: /* * failed to load the port monitor database */ devfsadm_print(VERBOSE_MID, error_msg, modname); sac_exitval = SAC_EXITVAL(pclose(fs_popen)); if (sac_exitval != 0) { devfsadm_print(VERBOSE_MID, "pmadm: (%s) %s\n", SAC_EID(sac_exitval), SAC_EMSG(sac_exitval)); } return (DEVFSADM_FAILURE); }
/* * Called for all serial devices that are NOT onboard * Creates links of the form "/dev/term/[0..n]" * Schedules an update the sacadm (portmon). */ static int serial_port_create(di_minor_t minor, di_node_t node) { char l_path[MAXPATHLEN], p_path[MAXPATHLEN]; char *devfspath, *buf, *minor_name; int port_num; devfspath = di_devfs_path(node); if (devfspath == NULL) { devfsadm_errprint("%s: di_devfs_path() failed\n", modname); return (DEVFSADM_CONTINUE); } if ((minor_name = di_minor_name(minor)) == NULL) { devfsadm_errprint("%s: NULL minor name\n\t%s\n", modname, devfspath); di_devfs_path_free(devfspath); return (DEVFSADM_CONTINUE); } /* * verify dialout ports do not come in on this nodetype */ if (is_dialout(minor_name)) { devfsadm_errprint("%s: dialout device\n\t%s:%s\n", modname, devfspath, minor_name); di_devfs_path_free(devfspath); return (DEVFSADM_CONTINUE); } /* * add the minor name to the physical path so we can * enum the port# and create the the link. */ (void) strcpy(p_path, devfspath); (void) strcat(p_path, ":"); (void) strcat(p_path, minor_name); di_devfs_path_free(devfspath); if (devfsadm_enumerate_int(p_path, 0, &buf, port_rules, 1)) { devfsadm_errprint("%s:serial_port_create:" " enumerate_int() failed\n\t%s\n", modname, p_path); return (DEVFSADM_CONTINUE); } (void) strcpy(l_path, "term/"); (void) strcat(l_path, buf); (void) devfsadm_mklink(l_path, node, minor, 0); /* * update the portmon database if this port falls within * the valid range of ports. */ if ((port_num = parse_portno(buf)) != -1) { pma[port_num].flags |= HAS_PORT_DEVICE; } free(buf); return (DEVFSADM_CONTINUE); }