xref: /netbsd-src/usr.sbin/puffs/mount_psshfs/psshfs.c (revision 18b2a948140148e99361999b7cecf0ef5c7fdfb4)
1*18b2a948Smsaitoh /*	$NetBSD: psshfs.c,v 1.67 2021/12/05 08:11:39 msaitoh Exp $	*/
2c3ef8ea5Spooka 
3c3ef8ea5Spooka /*
4dac06153Spooka  * Copyright (c) 2006-2009  Antti Kantee.  All Rights Reserved.
5c3ef8ea5Spooka  *
6c3ef8ea5Spooka  * Redistribution and use in source and binary forms, with or without
7c3ef8ea5Spooka  * modification, are permitted provided that the following conditions
8c3ef8ea5Spooka  * are met:
9c3ef8ea5Spooka  * 1. Redistributions of source code must retain the above copyright
10c3ef8ea5Spooka  *    notice, this list of conditions and the following disclaimer.
11c3ef8ea5Spooka  * 2. Redistributions in binary form must reproduce the above copyright
12c3ef8ea5Spooka  *    notice, this list of conditions and the following disclaimer in the
13c3ef8ea5Spooka  *    documentation and/or other materials provided with the distribution.
14c3ef8ea5Spooka  *
15c3ef8ea5Spooka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16c3ef8ea5Spooka  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17c3ef8ea5Spooka  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18c3ef8ea5Spooka  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19c3ef8ea5Spooka  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20c3ef8ea5Spooka  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21c3ef8ea5Spooka  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22c3ef8ea5Spooka  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23c3ef8ea5Spooka  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24c3ef8ea5Spooka  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25c3ef8ea5Spooka  * SUCH DAMAGE.
26c3ef8ea5Spooka  */
27c3ef8ea5Spooka 
28c3ef8ea5Spooka /*
29c3ef8ea5Spooka  * psshfs: puffs sshfs
30c3ef8ea5Spooka  *
31c3ef8ea5Spooka  * psshfs implements sshfs functionality on top of puffs making it
32*18b2a948Smsaitoh  * possible to mount a filesystem through the sftp service.
33c3ef8ea5Spooka  *
34c3ef8ea5Spooka  * psshfs can execute multiple operations in "parallel" by using the
35c3ef8ea5Spooka  * puffs_cc framework for continuations.
36c3ef8ea5Spooka  *
37c3ef8ea5Spooka  * Concurrency control is handled currently by vnode locking (this
38c3ef8ea5Spooka  * will change in the future).  Context switch locations are easy to
390e7bdfc1Spooka  * find by grepping for puffs_framebuf_enqueue_cc().
40c3ef8ea5Spooka  */
41c3ef8ea5Spooka 
42c3ef8ea5Spooka #include <sys/cdefs.h>
43c3ef8ea5Spooka #ifndef lint
44*18b2a948Smsaitoh __RCSID("$NetBSD: psshfs.c,v 1.67 2021/12/05 08:11:39 msaitoh Exp $");
45c3ef8ea5Spooka #endif /* !lint */
46c3ef8ea5Spooka 
47c3ef8ea5Spooka #include <sys/types.h>
48d6c25c2bSpooka #include <sys/wait.h>
494f048a36Schristos #include <sys/socket.h>
50c3ef8ea5Spooka 
514f048a36Schristos #include <stdio.h>
52c3ef8ea5Spooka #include <assert.h>
53c3ef8ea5Spooka #include <err.h>
54c3ef8ea5Spooka #include <errno.h>
55a67e4ce3Spooka #include <mntopts.h>
56464b354eSpooka #include <paths.h>
57c3ef8ea5Spooka #include <poll.h>
58c3ef8ea5Spooka #include <puffs.h>
59c3ef8ea5Spooka #include <signal.h>
60c3ef8ea5Spooka #include <stdlib.h>
61c3ef8ea5Spooka #include <util.h>
62c3ef8ea5Spooka #include <unistd.h>
63c3ef8ea5Spooka 
64c3ef8ea5Spooka #include "psshfs.h"
65c3ef8ea5Spooka 
668ff91531Spooka static int	pssh_connect(struct puffs_usermount *, int);
67c5afd8dcSpooka static void	psshfs_loopfn(struct puffs_usermount *);
68bec77c5fSjoerg __dead static void	usage(void);
693c644b8aSjakllsch static char *	cleanhostname(char *);
703c644b8aSjakllsch static char *	colon(char *);
71de048bdaSpooka static void	add_ssharg(char ***, int *, const char *);
72f423bfeeSpooka static void	psshfs_notify(struct puffs_usermount *, int, int);
73c3ef8ea5Spooka 
74c3ef8ea5Spooka #define SSH_PATH "/usr/bin/ssh"
75c3ef8ea5Spooka 
768543b6baSpooka unsigned int max_reads;
77c5afd8dcSpooka static int sighup;
788543b6baSpooka 
793c644b8aSjakllsch static char *
cleanhostname(char * host)803c644b8aSjakllsch cleanhostname(char *host)
813c644b8aSjakllsch {
823c644b8aSjakllsch 	if (*host == '[' && host[strlen(host) - 1] == ']') {
833c644b8aSjakllsch 		host[strlen(host) - 1] = '\0';
843c644b8aSjakllsch 		return (host + 1);
853c644b8aSjakllsch 	} else
863c644b8aSjakllsch 		return host;
873c644b8aSjakllsch }
883c644b8aSjakllsch 
893c644b8aSjakllsch static char *
colon(char * cp)903c644b8aSjakllsch colon(char *cp)
913c644b8aSjakllsch {
923c644b8aSjakllsch 	int flag = 0;
933c644b8aSjakllsch 
943c644b8aSjakllsch 	if (*cp == '[')
953c644b8aSjakllsch 		flag = 1;
963c644b8aSjakllsch 
973c644b8aSjakllsch 	for (; *cp; ++cp) {
983c644b8aSjakllsch 		if (*cp == '@' && *(cp+1) == '[')
993c644b8aSjakllsch 			flag = 1;
1003c644b8aSjakllsch 		if (*cp == ']' && *(cp+1) == ':' && flag)
1013c644b8aSjakllsch 			return (cp+1);
1023c644b8aSjakllsch 		if (*cp == ':' && !flag)
1033c644b8aSjakllsch 			return (cp);
1043c644b8aSjakllsch 		if (*cp == '/')
1053c644b8aSjakllsch 			return NULL;
1063c644b8aSjakllsch 	}
1073c644b8aSjakllsch 	return NULL;
1083c644b8aSjakllsch }
1093c644b8aSjakllsch 
110a67e4ce3Spooka static void
add_ssharg(char *** sshargs,int * nargs,const char * arg)111de048bdaSpooka add_ssharg(char ***sshargs, int *nargs, const char *arg)
112edbbe0d8Stnn {
113edbbe0d8Stnn 
114edbbe0d8Stnn 	*sshargs = realloc(*sshargs, (*nargs + 2) * sizeof(char*));
115edbbe0d8Stnn 	if (!*sshargs)
116edbbe0d8Stnn 		err(1, "realloc");
117de048bdaSpooka 	(*sshargs)[(*nargs)++] = estrdup(arg);
118edbbe0d8Stnn 	(*sshargs)[*nargs] = NULL;
119edbbe0d8Stnn }
120edbbe0d8Stnn 
121edbbe0d8Stnn static void
usage(void)122bec77c5fSjoerg usage(void)
123a67e4ce3Spooka {
124a67e4ce3Spooka 
1253c246dfdSpooka 	fprintf(stderr, "usage: %s "
1268ff91531Spooka 	    "[-ceprst] [-F configfile] [-O sshopt=value] [-o opts] "
127d96be1e1Sjmmv 	    "user@host:path mountpath\n",
128c921b474Spooka 	    getprogname());
1293c246dfdSpooka 	exit(1);
130a67e4ce3Spooka }
131a67e4ce3Spooka 
132c5afd8dcSpooka static void
takehup(int sig)133c5afd8dcSpooka takehup(int sig)
134c5afd8dcSpooka {
135c5afd8dcSpooka 
136c5afd8dcSpooka 	sighup = 1;
137c5afd8dcSpooka }
138c5afd8dcSpooka 
139c3ef8ea5Spooka int
main(int argc,char * argv[])140c3ef8ea5Spooka main(int argc, char *argv[])
141c3ef8ea5Spooka {
142c3ef8ea5Spooka 	struct psshfs_ctx pctx;
143c3ef8ea5Spooka 	struct puffs_usermount *pu;
144c3ef8ea5Spooka 	struct puffs_ops *pops;
145f423bfeeSpooka 	struct psshfs_node *root = &pctx.psn_root;
146f423bfeeSpooka 	struct puffs_node *pn_root;
147f423bfeeSpooka 	puffs_framev_fdnotify_fn notfn;
148f423bfeeSpooka 	struct vattr *rva;
149a67e4ce3Spooka 	mntoptparse_t mp;
150edbbe0d8Stnn 	char **sshargs;
1513c644b8aSjakllsch 	char *user;
1523c644b8aSjakllsch 	char *host;
1533c644b8aSjakllsch 	char *path;
1544b0f2948Spooka 	int mntflags, pflags, ch;
1554b0f2948Spooka 	int detach;
1568ff91531Spooka 	int exportfs, refreshival, numconnections;
1578ff91531Spooka 	int nargs;
158c3ef8ea5Spooka 
159c3ef8ea5Spooka 	setprogname(argv[0]);
160ef38ca99Spooka 	puffs_unmountonsignal(SIGINT, true);
161ef38ca99Spooka 	puffs_unmountonsignal(SIGTERM, true);
162c3ef8ea5Spooka 
163a67e4ce3Spooka 	if (argc < 3)
164a67e4ce3Spooka 		usage();
165a67e4ce3Spooka 
166c4291c19Spooka 	memset(&pctx, 0, sizeof(pctx));
1674b0f2948Spooka 	mntflags = pflags = exportfs = nargs = 0;
1688ff91531Spooka 	numconnections = 1;
1694b0f2948Spooka 	detach = 1;
170a56f46a5Spooka 	refreshival = DEFAULTREFRESH;
171f423bfeeSpooka 	notfn = puffs_framev_unmountonclose;
172edbbe0d8Stnn 	sshargs = NULL;
173edbbe0d8Stnn 	add_ssharg(&sshargs, &nargs, SSH_PATH);
174edbbe0d8Stnn 	add_ssharg(&sshargs, &nargs, "-axs");
175edbbe0d8Stnn 	add_ssharg(&sshargs, &nargs, "-oClearAllForwardings=yes");
176edbbe0d8Stnn 
177c4291c19Spooka 	while ((ch = getopt(argc, argv, "c:eF:g:o:O:pr:st:u:")) != -1) {
178a67e4ce3Spooka 		switch (ch) {
1798ff91531Spooka 		case 'c':
1808ff91531Spooka 			numconnections = atoi(optarg);
1818ff91531Spooka 			if (numconnections < 1 || numconnections > 2) {
1828ff91531Spooka 				fprintf(stderr, "%s: only 1 or 2 connections "
1838ff91531Spooka 				    "permitted currently\n", getprogname());
1848ff91531Spooka 				usage();
1858ff91531Spooka 				/*NOTREACHED*/
1868ff91531Spooka 			}
1878ff91531Spooka 			break;
188bf4e539fSpooka 		case 'e':
189b173dac6Spooka 			exportfs = 1;
190bf4e539fSpooka 			break;
191d96be1e1Sjmmv 		case 'F':
192d96be1e1Sjmmv 			add_ssharg(&sshargs, &nargs, "-F");
193d96be1e1Sjmmv 			add_ssharg(&sshargs, &nargs, optarg);
194d96be1e1Sjmmv 			break;
195c4291c19Spooka 		case 'g':
196c4291c19Spooka 			pctx.domanglegid = 1;
197c4291c19Spooka 			pctx.manglegid = atoi(optarg);
198754f939dSpooka 			if (pctx.manglegid == (gid_t)-1)
199eac396bbSpooka 				errx(1, "-1 not allowed for -g");
200c4291c19Spooka 			pctx.mygid = getegid();
201c4291c19Spooka 			break;
202edbbe0d8Stnn 		case 'O':
203098bf956Stnn 			add_ssharg(&sshargs, &nargs, "-o");
204098bf956Stnn 			add_ssharg(&sshargs, &nargs, optarg);
205edbbe0d8Stnn 			break;
206a67e4ce3Spooka 		case 'o':
207a67e4ce3Spooka 			mp = getmntopts(optarg, puffsmopts, &mntflags, &pflags);
208a67e4ce3Spooka 			if (mp == NULL)
209a67e4ce3Spooka 				err(1, "getmntopts");
210a67e4ce3Spooka 			freemntopts(mp);
211a67e4ce3Spooka 			break;
212f423bfeeSpooka 		case 'p':
213f423bfeeSpooka 			notfn = psshfs_notify;
214f423bfeeSpooka 			break;
2158543b6baSpooka 		case 'r':
2168543b6baSpooka 			max_reads = atoi(optarg);
2178543b6baSpooka 			break;
218c921b474Spooka 		case 's':
2194b0f2948Spooka 			detach = 0;
220c921b474Spooka 			break;
221a56f46a5Spooka 		case 't':
222a56f46a5Spooka 			refreshival = atoi(optarg);
2237dc67449Spooka 			if (refreshival < 0 && refreshival != -1)
2247dc67449Spooka 				errx(1, "invalid timeout %d", refreshival);
225a56f46a5Spooka 			break;
226c4291c19Spooka 		case 'u':
227c4291c19Spooka 			pctx.domangleuid = 1;
228c4291c19Spooka 			pctx.mangleuid = atoi(optarg);
229754f939dSpooka 			if (pctx.mangleuid == (uid_t)-1)
230eac396bbSpooka 				errx(1, "-1 not allowed for -u");
231c4291c19Spooka 			pctx.myuid = geteuid();
232c4291c19Spooka 			break;
233a67e4ce3Spooka 		default:
234a67e4ce3Spooka 			usage();
235a67e4ce3Spooka 			/*NOTREACHED*/
236a67e4ce3Spooka 		}
237a67e4ce3Spooka 	}
238a67e4ce3Spooka 	argc -= optind;
239a67e4ce3Spooka 	argv += optind;
240a67e4ce3Spooka 
241c921b474Spooka 	if (pflags & PUFFS_FLAG_OPDUMP)
2424b0f2948Spooka 		detach = 0;
243dca252d8Spooka 	pflags |= PUFFS_FLAG_BUILDPATH;
244dca252d8Spooka 	pflags |= PUFFS_KFLAG_WTCACHE | PUFFS_KFLAG_IAONDEMAND;
245c921b474Spooka 
246a67e4ce3Spooka 	if (argc != 2)
247a67e4ce3Spooka 		usage();
248c3ef8ea5Spooka 
249c3ef8ea5Spooka 	PUFFSOP_INIT(pops);
250c3ef8ea5Spooka 
251c3ef8ea5Spooka 	PUFFSOP_SET(pops, psshfs, fs, unmount);
252c3ef8ea5Spooka 	PUFFSOP_SETFSNOP(pops, sync); /* XXX */
25372d8b8b6Spooka 	PUFFSOP_SET(pops, psshfs, fs, statvfs);
254bf4e539fSpooka 	PUFFSOP_SET(pops, psshfs, fs, nodetofh);
255bf4e539fSpooka 	PUFFSOP_SET(pops, psshfs, fs, fhtonode);
256c3ef8ea5Spooka 
257c3ef8ea5Spooka 	PUFFSOP_SET(pops, psshfs, node, lookup);
258c3ef8ea5Spooka 	PUFFSOP_SET(pops, psshfs, node, create);
259ded3a0bfSpooka 	PUFFSOP_SET(pops, psshfs, node, open);
260ded3a0bfSpooka 	PUFFSOP_SET(pops, psshfs, node, inactive);
261c3ef8ea5Spooka 	PUFFSOP_SET(pops, psshfs, node, readdir);
262c3ef8ea5Spooka 	PUFFSOP_SET(pops, psshfs, node, getattr);
263c3ef8ea5Spooka 	PUFFSOP_SET(pops, psshfs, node, setattr);
264c3ef8ea5Spooka 	PUFFSOP_SET(pops, psshfs, node, mkdir);
265c3ef8ea5Spooka 	PUFFSOP_SET(pops, psshfs, node, remove);
266c3ef8ea5Spooka 	PUFFSOP_SET(pops, psshfs, node, readlink);
267c3ef8ea5Spooka 	PUFFSOP_SET(pops, psshfs, node, rmdir);
268c3ef8ea5Spooka 	PUFFSOP_SET(pops, psshfs, node, symlink);
269c3ef8ea5Spooka 	PUFFSOP_SET(pops, psshfs, node, rename);
270c3ef8ea5Spooka 	PUFFSOP_SET(pops, psshfs, node, read);
271c3ef8ea5Spooka 	PUFFSOP_SET(pops, psshfs, node, write);
272c3ef8ea5Spooka 	PUFFSOP_SET(pops, psshfs, node, reclaim);
273c3ef8ea5Spooka 
27408db7d75Spooka 	pu = puffs_init(pops, argv[0], "psshfs", &pctx, pflags);
27508db7d75Spooka 	if (pu == NULL)
27608db7d75Spooka 		err(1, "puffs_init");
27708db7d75Spooka 
278bf4e539fSpooka 	pctx.mounttime = time(NULL);
279a56f46a5Spooka 	pctx.refreshival = refreshival;
2808ff91531Spooka 	pctx.numconnections = numconnections;
281c3ef8ea5Spooka 
2823c644b8aSjakllsch 	user = strdup(argv[0]);
2833c644b8aSjakllsch 	if ((host = strrchr(user, '@')) == NULL) {
2843c644b8aSjakllsch 		host = user;
2853c644b8aSjakllsch 	} else {
2863c644b8aSjakllsch 		*host++ = '\0';		/* break at the '@' */
2873c644b8aSjakllsch 		if (user[0] == '\0') {
2883c644b8aSjakllsch 			fprintf(stderr, "Missing username\n");
2893c644b8aSjakllsch 			usage();
2903c644b8aSjakllsch 		}
2913c644b8aSjakllsch 		add_ssharg(&sshargs, &nargs, "-l");
2923c644b8aSjakllsch 		add_ssharg(&sshargs, &nargs, user);
2933c644b8aSjakllsch 	}
294c3ef8ea5Spooka 
2953c644b8aSjakllsch 	if ((path = colon(host)) != NULL) {
2963c644b8aSjakllsch 		*path++ = '\0';		/* break at the ':' */
2973c644b8aSjakllsch 		pctx.mountpath = path;
2983c644b8aSjakllsch 	} else {
2993c644b8aSjakllsch 		pctx.mountpath = ".";
3003c644b8aSjakllsch 	}
3013c644b8aSjakllsch 
3023c644b8aSjakllsch 	host = cleanhostname(host);
3033c644b8aSjakllsch 	if (host[0] == '\0') {
3043c644b8aSjakllsch 		fprintf(stderr, "Missing hostname\n");
3053c644b8aSjakllsch 		usage();
3063c644b8aSjakllsch 	}
3073c644b8aSjakllsch 
3083c644b8aSjakllsch 	add_ssharg(&sshargs, &nargs, host);
309edbbe0d8Stnn 	add_ssharg(&sshargs, &nargs, "sftp");
310f423bfeeSpooka 	pctx.sshargs = sshargs;
311f423bfeeSpooka 
312f423bfeeSpooka 	pctx.nextino = 2;
313f423bfeeSpooka 	memset(root, 0, sizeof(struct psshfs_node));
314f4368f51Sriastradh 	TAILQ_INIT(&root->pw);
315f423bfeeSpooka 	pn_root = puffs_pn_new(pu, root);
316f423bfeeSpooka 	if (pn_root == NULL)
317f423bfeeSpooka 		return errno;
318f423bfeeSpooka 	puffs_setroot(pu, pn_root);
319c3ef8ea5Spooka 
3208ff91531Spooka 	puffs_framev_init(pu, psbuf_read, psbuf_write, psbuf_cmp, NULL, notfn);
3218ff91531Spooka 
322c5afd8dcSpooka 	signal(SIGHUP, takehup);
323c5afd8dcSpooka 	puffs_ml_setloopfn(pu, psshfs_loopfn);
3248ff91531Spooka 	if (pssh_connect(pu, PSSHFD_META) == -1)
3258ff91531Spooka 		err(1, "can't connect meta");
3268ff91531Spooka 	if (puffs_framev_addfd(pu, pctx.sshfd,
3278ff91531Spooka 	    PUFFS_FBIO_READ | PUFFS_FBIO_WRITE) == -1)
3288ff91531Spooka 		err(1, "framebuf addfd meta");
3298ff91531Spooka 	if (numconnections == 2) {
3308ff91531Spooka 		if (pssh_connect(pu, PSSHFD_DATA) == -1)
3318ff91531Spooka 			err(1, "can't connect data");
3328ff91531Spooka 		if (puffs_framev_addfd(pu, pctx.sshfd_data,
3338ff91531Spooka 		    PUFFS_FBIO_READ | PUFFS_FBIO_WRITE) == -1)
3348ff91531Spooka 			err(1, "framebuf addfd data");
3358ff91531Spooka 	} else {
3368ff91531Spooka 		pctx.sshfd_data = pctx.sshfd;
3378ff91531Spooka 	}
338c921b474Spooka 
339b173dac6Spooka 	if (exportfs)
340b173dac6Spooka 		puffs_setfhsize(pu, sizeof(struct psshfs_fid),
341b173dac6Spooka 		    PUFFS_FHFLAG_NFSV2 | PUFFS_FHFLAG_NFSV3);
342b173dac6Spooka 
343f423bfeeSpooka 	rva = &pn_root->pn_va;
344f423bfeeSpooka 	rva->va_fileid = pctx.nextino++;
345046f5845Spooka 
346046f5845Spooka 	/*
347046f5845Spooka 	 * For root link count, just guess something ridiculously high.
348d317936dSpooka 	 * Guessing too high has no known adverse effects, but fts(3)
349046f5845Spooka 	 * doesn't like too low values.  This guess will be replaced
350046f5845Spooka 	 * with the real value when readdir is first called for
351046f5845Spooka 	 * the root directory.
352046f5845Spooka 	 */
353046f5845Spooka 	rva->va_nlink = 8811;
354f423bfeeSpooka 
3554b0f2948Spooka 	if (detach)
3564462e945Spooka 		if (puffs_daemon(pu, 1, 1) == -1)
3574462e945Spooka 			err(1, "puffs_daemon");
3584b0f2948Spooka 
359ec865a5bSpooka 	if (puffs_mount(pu, argv[1], mntflags, puffs_getroot(pu)) == -1)
360ec865a5bSpooka 		err(1, "puffs_mount");
361a02fe51bSpooka 	if (puffs_setblockingmode(pu, PUFFSDEV_NONBLOCK) == -1)
362a02fe51bSpooka 		err(1, "setblockingmode");
363a02fe51bSpooka 
3644b0f2948Spooka 	if (puffs_mainloop(pu) == -1)
365ec865a5bSpooka 		err(1, "mainloop");
36684c098feSpooka 	puffs_exit(pu, 1);
367ec865a5bSpooka 
3684b0f2948Spooka 	return 0;
369c3ef8ea5Spooka }
370c3ef8ea5Spooka 
371f423bfeeSpooka #define RETRY_MAX 100
372f423bfeeSpooka 
373f423bfeeSpooka void
psshfs_notify(struct puffs_usermount * pu,int fd,int what)374f423bfeeSpooka psshfs_notify(struct puffs_usermount *pu, int fd, int what)
375c3ef8ea5Spooka {
376f423bfeeSpooka 	struct psshfs_ctx *pctx = puffs_getspecific(pu);
377d6c25c2bSpooka 	int nretry, which, newfd, dummy;
3788ff91531Spooka 
3798ff91531Spooka 	if (fd == pctx->sshfd) {
3808ff91531Spooka 		which = PSSHFD_META;
3818ff91531Spooka 	} else {
3828ff91531Spooka 		assert(fd == pctx->sshfd_data);
3838ff91531Spooka 		which = PSSHFD_DATA;
3848ff91531Spooka 	}
385f423bfeeSpooka 
386f423bfeeSpooka 	if (puffs_getstate(pu) != PUFFS_STATE_RUNNING)
387f423bfeeSpooka 		return;
388f423bfeeSpooka 
389f423bfeeSpooka 	if (what != (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE)) {
3908ff91531Spooka 		puffs_framev_removefd(pu, fd, ECONNRESET);
391f423bfeeSpooka 		return;
392f423bfeeSpooka 	}
3938ff91531Spooka 	close(fd);
394f423bfeeSpooka 
395d6c25c2bSpooka 	/* deal with zmobies, beware of half-eaten brain */
396d6c25c2bSpooka 	while (waitpid(-1, &dummy, WNOHANG) > 0)
397d6c25c2bSpooka 		continue;
398d6c25c2bSpooka 
399f423bfeeSpooka 	for (nretry = 0;;nretry++) {
4008ff91531Spooka 		if ((newfd = pssh_connect(pu, which)) == -1)
401f423bfeeSpooka 			goto retry2;
402f423bfeeSpooka 
4038ff91531Spooka 		if (puffs_framev_addfd(pu, newfd,
404f423bfeeSpooka 		    PUFFS_FBIO_READ | PUFFS_FBIO_WRITE) == -1)
405f423bfeeSpooka 			goto retry1;
406f423bfeeSpooka 
407f423bfeeSpooka 		break;
408f423bfeeSpooka  retry1:
409f423bfeeSpooka 		fprintf(stderr, "reconnect failed... ");
4108ff91531Spooka 		close(newfd);
411f423bfeeSpooka  retry2:
412f423bfeeSpooka 		if (nretry < RETRY_MAX) {
413b5cb6a6cSpooka 			fprintf(stderr, "retry (%d left)\n", RETRY_MAX-nretry);
414f423bfeeSpooka 			sleep(nretry);
415f423bfeeSpooka 		} else {
416f423bfeeSpooka 			fprintf(stderr, "retry count exceeded, going south\n");
417f423bfeeSpooka 			exit(1); /* XXXXXXX */
418f423bfeeSpooka 		}
419f423bfeeSpooka 	}
420f423bfeeSpooka }
421f423bfeeSpooka 
422f423bfeeSpooka static int
pssh_connect(struct puffs_usermount * pu,int which)4238ff91531Spooka pssh_connect(struct puffs_usermount *pu, int which)
424f423bfeeSpooka {
4258ff91531Spooka 	struct psshfs_ctx *pctx = puffs_getspecific(pu);
426de048bdaSpooka 	char * const *sshargs = pctx->sshargs;
427c3ef8ea5Spooka 	int fds[2];
428c3ef8ea5Spooka 	pid_t pid;
4298ff91531Spooka 	int dnfd, x;
4308ff91531Spooka 	int *sshfd;
4318ff91531Spooka 	pid_t *sshpid;
4328ff91531Spooka 
4338ff91531Spooka 	if (which == PSSHFD_META) {
4348ff91531Spooka 		sshfd = &pctx->sshfd;
4358ff91531Spooka 		sshpid = &pctx->sshpid;
4368ff91531Spooka 	} else {
4378ff91531Spooka 		assert(which == PSSHFD_DATA);
4388ff91531Spooka 		sshfd = &pctx->sshfd_data;
4398ff91531Spooka 		sshpid = &pctx->sshpid_data;
4408ff91531Spooka 	}
441c3ef8ea5Spooka 
442c3ef8ea5Spooka 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1)
443f423bfeeSpooka 		return -1;
444c3ef8ea5Spooka 
445c3ef8ea5Spooka 	pid = fork();
446c3ef8ea5Spooka 	switch (pid) {
447c3ef8ea5Spooka 	case -1:
448f423bfeeSpooka 		return -1;
449c3ef8ea5Spooka 		/*NOTREACHED*/
450c3ef8ea5Spooka 	case 0: /* child */
451c3ef8ea5Spooka 		if (dup2(fds[0], STDIN_FILENO) == -1)
452c3ef8ea5Spooka 			err(1, "child dup2");
453c3ef8ea5Spooka 		if (dup2(fds[0], STDOUT_FILENO) == -1)
454c3ef8ea5Spooka 			err(1, "child dup2");
455c3ef8ea5Spooka 		close(fds[0]);
456c3ef8ea5Spooka 		close(fds[1]);
457464b354eSpooka 
458464b354eSpooka 		dnfd = open(_PATH_DEVNULL, O_RDWR);
459464b354eSpooka 		if (dnfd != -1)
460464b354eSpooka 			dup2(dnfd, STDERR_FILENO);
461464b354eSpooka 
462c3ef8ea5Spooka 		execvp(sshargs[0], sshargs);
4638ff91531Spooka 		/*NOTREACHED*/
464c3ef8ea5Spooka 		break;
465c3ef8ea5Spooka 	default:
4668ff91531Spooka 		*sshpid = pid;
4678ff91531Spooka 		*sshfd = fds[1];
468c3ef8ea5Spooka 		close(fds[0]);
469c3ef8ea5Spooka 		break;
470c3ef8ea5Spooka 	}
471f423bfeeSpooka 
4728ff91531Spooka 	if (psshfs_handshake(pu, *sshfd) != 0)
473080506bcSpooka 		errx(1, "handshake failed, server does not support sftp?");
4748ff91531Spooka 	x = 1;
4758ff91531Spooka 	if (ioctl(*sshfd, FIONBIO, &x) == -1)
4768ff91531Spooka 		err(1, "nonblocking descriptor %d", which);
4778ff91531Spooka 
4788ff91531Spooka 	return *sshfd;
479c3ef8ea5Spooka }
480c5afd8dcSpooka 
481c5afd8dcSpooka static void *
invalone(struct puffs_usermount * pu,struct puffs_node * pn,void * arg)482c5afd8dcSpooka invalone(struct puffs_usermount *pu, struct puffs_node *pn, void *arg)
483c5afd8dcSpooka {
484c5afd8dcSpooka 	struct psshfs_node *psn = pn->pn_data;
485c5afd8dcSpooka 
486c5afd8dcSpooka 	psn->attrread = 0;
487c5afd8dcSpooka 	psn->dentread = 0;
488b0106b7eSpooka 	psn->slread = 0;
489c5afd8dcSpooka 
490c5afd8dcSpooka 	return NULL;
491c5afd8dcSpooka }
492c5afd8dcSpooka 
493c5afd8dcSpooka static void
psshfs_loopfn(struct puffs_usermount * pu)494c5afd8dcSpooka psshfs_loopfn(struct puffs_usermount *pu)
495c5afd8dcSpooka {
496c5afd8dcSpooka 
497c5afd8dcSpooka 	if (sighup) {
498c5afd8dcSpooka 		puffs_pn_nodewalk(pu, invalone, NULL);
499c5afd8dcSpooka 		sighup = 0;
500c5afd8dcSpooka 	}
501c5afd8dcSpooka }
502