int platform_ssh_share(const char *pi_name, Conf *conf, Plug downplug, Plug upplug, Socket *sock, char **logtext, char **ds_err, char **us_err, int can_upstream, int can_downstream) { char *dirname, *lockname, *sockname, *err; int lockfd; Socket retsock; /* * Sort out what we're going to call the directory in which we * keep the socket. This has the side effect of potentially * creating its top-level containing dir and/or the salt file * within that, if they don't already exist. */ dirname = make_dirname(pi_name, logtext); if (!dirname) { return SHARE_NONE; } /* * Now make sure the subdirectory exists. */ if ((err = make_dir_and_check_ours(dirname)) != NULL) { *logtext = err; sfree(dirname); return SHARE_NONE; } /* * Acquire a lock on a file in that directory. */ lockname = dupcat(dirname, "/lock", (char *)NULL); lockfd = open(lockname, O_CREAT | O_RDWR | O_TRUNC, 0600); if (lockfd < 0) { *logtext = dupprintf("%s: open: %s", lockname, strerror(errno)); sfree(dirname); sfree(lockname); return SHARE_NONE; } if (flock(lockfd, LOCK_EX) < 0) { *logtext = dupprintf("%s: flock(LOCK_EX): %s", lockname, strerror(errno)); sfree(dirname); sfree(lockname); close(lockfd); return SHARE_NONE; } sockname = dupprintf("%s/socket", dirname); *logtext = NULL; if (can_downstream) { retsock = new_connection(unix_sock_addr(sockname), "", 0, 0, 1, 0, 0, downplug, conf); if (sk_socket_error(retsock) == NULL) { sfree(*logtext); *logtext = sockname; *sock = retsock; sfree(dirname); sfree(lockname); close(lockfd); return SHARE_DOWNSTREAM; } sfree(*ds_err); *ds_err = dupprintf("%s: %s", sockname, sk_socket_error(retsock)); sk_close(retsock); } if (can_upstream) { retsock = new_unix_listener(unix_sock_addr(sockname), upplug); if (sk_socket_error(retsock) == NULL) { sfree(*logtext); *logtext = sockname; *sock = retsock; sfree(dirname); sfree(lockname); close(lockfd); return SHARE_UPSTREAM; } sfree(*us_err); *us_err = dupprintf("%s: %s", sockname, sk_socket_error(retsock)); sk_close(retsock); } /* One of the above clauses ought to have happened. */ assert(*logtext || *ds_err || *us_err); sfree(dirname); sfree(lockname); sfree(sockname); close(lockfd); return SHARE_NONE; }
int platform_make_x11_server(Plug *plug, const char *progname, int mindisp, const char *screen_number_suffix, ptrlen authproto, ptrlen authdata, Socket **sockets, Conf *conf) { char *tmpdir; char *authfilename = NULL; strbuf *authfiledata = NULL; char *unix_path = NULL; SockAddr *a_tcp = NULL, *a_unix = NULL; int authfd; FILE *authfp; int displayno; authfiledata = strbuf_new_nm(); int nsockets = 0; /* * Look for a free TCP port to run our server on. */ for (displayno = mindisp;; displayno++) { const char *err; int tcp_port = displayno + 6000; int addrtype = ADDRTYPE_IPV4; sockets[nsockets] = new_listener( NULL, tcp_port, plug, false, conf, addrtype); err = sk_socket_error(sockets[nsockets]); if (!err) { char *hostname = get_hostname(); if (hostname) { char *canonicalname = NULL; a_tcp = name_lookup(hostname, tcp_port, &canonicalname, conf, addrtype, NULL, ""); sfree(canonicalname); } sfree(hostname); nsockets++; break; /* success! */ } else { sk_close(sockets[nsockets]); } if (!strcmp(err, strerror(EADDRINUSE))) /* yuck! */ goto out; } if (a_tcp) { x11_format_auth_for_authfile( BinarySink_UPCAST(authfiledata), a_tcp, displayno, authproto, authdata); } /* * Try to establish the Unix-domain analogue. That may or may not * work - file permissions in /tmp may prevent it, for example - * but it's worth a try, and we don't consider it a fatal error if * it doesn't work. */ unix_path = dupprintf("/tmp/.X11-unix/X%d", displayno); a_unix = unix_sock_addr(unix_path); sockets[nsockets] = new_unix_listener(a_unix, plug); if (!sk_socket_error(sockets[nsockets])) { x11_format_auth_for_authfile( BinarySink_UPCAST(authfiledata), a_unix, displayno, authproto, authdata); nsockets++; } else { sk_close(sockets[nsockets]); sfree(unix_path); unix_path = NULL; } /* * Decide where the authority data will be written. */ tmpdir = getenv("TMPDIR"); if (!tmpdir || !*tmpdir) tmpdir = "/tmp"; authfilename = dupcat(tmpdir, "/", progname, "-Xauthority-XXXXXX", NULL); { int oldumask = umask(077); authfd = mkstemp(authfilename); umask(oldumask); } if (authfd < 0) { while (nsockets-- > 0) sk_close(sockets[nsockets]); goto out; } /* * Spawn a subprocess which will try to reliably delete our * auth file when we terminate, in case we die unexpectedly. */ { int cleanup_pipe[2]; pid_t pid; /* Don't worry if pipe or fork fails; it's not _that_ critical. */ if (!pipe(cleanup_pipe)) { if ((pid = fork()) == 0) { int buf[1024]; /* * Our parent process holds the writing end of * this pipe, and writes nothing to it. Hence, * we expect read() to return EOF as soon as * that process terminates. */ close(0); close(1); close(2); setpgid(0, 0); close(cleanup_pipe[1]); close(authfd); while (read(cleanup_pipe[0], buf, sizeof(buf)) > 0); unlink(authfilename); if (unix_path) unlink(unix_path); _exit(0); } else if (pid < 0) { close(cleanup_pipe[0]); close(cleanup_pipe[1]); } else { close(cleanup_pipe[0]); cloexec(cleanup_pipe[1]); } } } authfp = fdopen(authfd, "wb"); fwrite(authfiledata->u, 1, authfiledata->len, authfp); fclose(authfp); { char *display = dupprintf(":%d%s", displayno, screen_number_suffix); conf_set_str_str(conf, CONF_environmt, "DISPLAY", display); sfree(display); } conf_set_str_str(conf, CONF_environmt, "XAUTHORITY", authfilename); /* * FIXME: return at least the DISPLAY and XAUTHORITY env settings, * and perhaps also the display number */ out: if (a_tcp) sk_addr_free(a_tcp); /* a_unix doesn't need freeing, because new_unix_listener took it over */ sfree(authfilename); strbuf_free(authfiledata); sfree(unix_path); return nsockets; }