Sync usage with man page.
[netbsd-mini2440.git] / usr.sbin / puffs / mount_psshfs / psshfs.c
blobe621de69211d0ec0e4b455b0e3071512560bcf3d
1 /* $NetBSD: psshfs.c,v 1.57 2010/01/07 21:23:10 pooka Exp $ */
3 /*
4 * Copyright (c) 2006-2009 Antti Kantee. All Rights Reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
29 * psshfs: puffs sshfs
31 * psshfs implements sshfs functionality on top of puffs making it
32 * possible to mount a filesystme through the sftp service.
34 * psshfs can execute multiple operations in "parallel" by using the
35 * puffs_cc framework for continuations.
37 * Concurrency control is handled currently by vnode locking (this
38 * will change in the future). Context switch locations are easy to
39 * find by grepping for puffs_framebuf_enqueue_cc().
42 #include <sys/cdefs.h>
43 #ifndef lint
44 __RCSID("$NetBSD: psshfs.c,v 1.57 2010/01/07 21:23:10 pooka Exp $");
45 #endif /* !lint */
47 #include <sys/types.h>
48 #include <sys/wait.h>
50 #include <assert.h>
51 #include <err.h>
52 #include <errno.h>
53 #include <mntopts.h>
54 #include <paths.h>
55 #include <poll.h>
56 #include <puffs.h>
57 #include <signal.h>
58 #include <stdlib.h>
59 #include <util.h>
60 #include <unistd.h>
62 #include "psshfs.h"
64 static int pssh_connect(struct puffs_usermount *, int);
65 static void psshfs_loopfn(struct puffs_usermount *);
66 static void usage(void);
67 static void add_ssharg(char ***, int *, const char *);
68 static void psshfs_notify(struct puffs_usermount *, int, int);
70 #define SSH_PATH "/usr/bin/ssh"
72 unsigned int max_reads;
73 static int sighup;
75 static void
76 add_ssharg(char ***sshargs, int *nargs, const char *arg)
79 *sshargs = realloc(*sshargs, (*nargs + 2) * sizeof(char*));
80 if (!*sshargs)
81 err(1, "realloc");
82 (*sshargs)[(*nargs)++] = estrdup(arg);
83 (*sshargs)[*nargs] = NULL;
86 static void
87 usage()
90 fprintf(stderr, "usage: %s "
91 "[-ceprst] [-F configfile] [-O sshopt=value] [-o opts] "
92 "user@host:path mountpath\n",
93 getprogname());
94 exit(1);
97 static void
98 takehup(int sig)
101 sighup = 1;
105 main(int argc, char *argv[])
107 struct psshfs_ctx pctx;
108 struct puffs_usermount *pu;
109 struct puffs_ops *pops;
110 struct psshfs_node *root = &pctx.psn_root;
111 struct puffs_node *pn_root;
112 puffs_framev_fdnotify_fn notfn;
113 struct vattr *rva;
114 mntoptparse_t mp;
115 char **sshargs;
116 char *userhost;
117 char *hostpath;
118 int mntflags, pflags, ch;
119 int detach;
120 int exportfs, refreshival, numconnections;
121 int nargs;
123 setprogname(argv[0]);
125 if (argc < 3)
126 usage();
128 memset(&pctx, 0, sizeof(pctx));
129 mntflags = pflags = exportfs = nargs = 0;
130 numconnections = 1;
131 detach = 1;
132 refreshival = DEFAULTREFRESH;
133 notfn = puffs_framev_unmountonclose;
134 sshargs = NULL;
135 add_ssharg(&sshargs, &nargs, SSH_PATH);
136 add_ssharg(&sshargs, &nargs, "-axs");
137 add_ssharg(&sshargs, &nargs, "-oClearAllForwardings=yes");
139 while ((ch = getopt(argc, argv, "c:eF:g:o:O:pr:st:u:")) != -1) {
140 switch (ch) {
141 case 'c':
142 numconnections = atoi(optarg);
143 if (numconnections < 1 || numconnections > 2) {
144 fprintf(stderr, "%s: only 1 or 2 connections "
145 "permitted currently\n", getprogname());
146 usage();
147 /*NOTREACHED*/
149 break;
150 case 'e':
151 exportfs = 1;
152 break;
153 case 'F':
154 add_ssharg(&sshargs, &nargs, "-F");
155 add_ssharg(&sshargs, &nargs, optarg);
156 break;
157 case 'g':
158 pctx.domanglegid = 1;
159 pctx.manglegid = atoi(optarg);
160 if (pctx.manglegid == (gid_t)-1)
161 errx(1, "-1 not allowed for -g");
162 pctx.mygid = getegid();
163 break;
164 case 'O':
165 add_ssharg(&sshargs, &nargs, "-o");
166 add_ssharg(&sshargs, &nargs, optarg);
167 break;
168 case 'o':
169 mp = getmntopts(optarg, puffsmopts, &mntflags, &pflags);
170 if (mp == NULL)
171 err(1, "getmntopts");
172 freemntopts(mp);
173 break;
174 case 'p':
175 notfn = psshfs_notify;
176 break;
177 case 'r':
178 max_reads = atoi(optarg);
179 break;
180 case 's':
181 detach = 0;
182 break;
183 case 't':
184 refreshival = atoi(optarg);
185 if (refreshival < 0 && refreshival != -1)
186 errx(1, "invalid timeout %d", refreshival);
187 break;
188 case 'u':
189 pctx.domangleuid = 1;
190 pctx.mangleuid = atoi(optarg);
191 if (pctx.mangleuid == (uid_t)-1)
192 errx(1, "-1 not allowed for -u");
193 pctx.myuid = geteuid();
194 break;
195 default:
196 usage();
197 /*NOTREACHED*/
200 argc -= optind;
201 argv += optind;
203 if (pflags & PUFFS_FLAG_OPDUMP)
204 detach = 0;
205 pflags |= PUFFS_FLAG_BUILDPATH;
206 pflags |= PUFFS_KFLAG_WTCACHE | PUFFS_KFLAG_IAONDEMAND;
208 if (argc != 2)
209 usage();
211 PUFFSOP_INIT(pops);
213 PUFFSOP_SET(pops, psshfs, fs, unmount);
214 PUFFSOP_SETFSNOP(pops, sync); /* XXX */
215 PUFFSOP_SET(pops, psshfs, fs, statvfs);
216 PUFFSOP_SET(pops, psshfs, fs, nodetofh);
217 PUFFSOP_SET(pops, psshfs, fs, fhtonode);
219 PUFFSOP_SET(pops, psshfs, node, lookup);
220 PUFFSOP_SET(pops, psshfs, node, create);
221 PUFFSOP_SET(pops, psshfs, node, open);
222 PUFFSOP_SET(pops, psshfs, node, inactive);
223 PUFFSOP_SET(pops, psshfs, node, readdir);
224 PUFFSOP_SET(pops, psshfs, node, getattr);
225 PUFFSOP_SET(pops, psshfs, node, setattr);
226 PUFFSOP_SET(pops, psshfs, node, mkdir);
227 PUFFSOP_SET(pops, psshfs, node, remove);
228 PUFFSOP_SET(pops, psshfs, node, readlink);
229 PUFFSOP_SET(pops, psshfs, node, rmdir);
230 PUFFSOP_SET(pops, psshfs, node, symlink);
231 PUFFSOP_SET(pops, psshfs, node, rename);
232 PUFFSOP_SET(pops, psshfs, node, read);
233 PUFFSOP_SET(pops, psshfs, node, write);
234 PUFFSOP_SET(pops, psshfs, node, reclaim);
236 pu = puffs_init(pops, argv[0], "psshfs", &pctx, pflags);
237 if (pu == NULL)
238 err(1, "puffs_init");
240 pctx.mounttime = time(NULL);
241 pctx.refreshival = refreshival;
242 pctx.numconnections = numconnections;
244 userhost = argv[0];
245 hostpath = strchr(userhost, ':');
246 if (hostpath) {
247 *hostpath++ = '\0';
248 pctx.mountpath = hostpath;
249 } else
250 pctx.mountpath = ".";
252 add_ssharg(&sshargs, &nargs, argv[0]);
253 add_ssharg(&sshargs, &nargs, "sftp");
254 pctx.sshargs = sshargs;
256 pctx.nextino = 2;
257 memset(root, 0, sizeof(struct psshfs_node));
258 pn_root = puffs_pn_new(pu, root);
259 if (pn_root == NULL)
260 return errno;
261 puffs_setroot(pu, pn_root);
263 puffs_framev_init(pu, psbuf_read, psbuf_write, psbuf_cmp, NULL, notfn);
265 signal(SIGHUP, takehup);
266 puffs_ml_setloopfn(pu, psshfs_loopfn);
267 if (pssh_connect(pu, PSSHFD_META) == -1)
268 err(1, "can't connect meta");
269 if (puffs_framev_addfd(pu, pctx.sshfd,
270 PUFFS_FBIO_READ | PUFFS_FBIO_WRITE) == -1)
271 err(1, "framebuf addfd meta");
272 if (numconnections == 2) {
273 if (pssh_connect(pu, PSSHFD_DATA) == -1)
274 err(1, "can't connect data");
275 if (puffs_framev_addfd(pu, pctx.sshfd_data,
276 PUFFS_FBIO_READ | PUFFS_FBIO_WRITE) == -1)
277 err(1, "framebuf addfd data");
278 } else {
279 pctx.sshfd_data = pctx.sshfd;
282 if (exportfs)
283 puffs_setfhsize(pu, sizeof(struct psshfs_fid),
284 PUFFS_FHFLAG_NFSV2 | PUFFS_FHFLAG_NFSV3);
286 rva = &pn_root->pn_va;
287 rva->va_fileid = pctx.nextino++;
288 rva->va_nlink = 101; /* XXX */
290 if (detach)
291 if (puffs_daemon(pu, 1, 1) == -1)
292 err(1, "puffs_daemon");
294 if (puffs_mount(pu, argv[1], mntflags, puffs_getroot(pu)) == -1)
295 err(1, "puffs_mount");
296 if (puffs_setblockingmode(pu, PUFFSDEV_NONBLOCK) == -1)
297 err(1, "setblockingmode");
299 if (puffs_mainloop(pu) == -1)
300 err(1, "mainloop");
301 puffs_exit(pu, 1);
303 return 0;
306 #define RETRY_MAX 100
308 void
309 psshfs_notify(struct puffs_usermount *pu, int fd, int what)
311 struct psshfs_ctx *pctx = puffs_getspecific(pu);
312 int nretry, which, newfd, dummy;
314 if (fd == pctx->sshfd) {
315 which = PSSHFD_META;
316 } else {
317 assert(fd == pctx->sshfd_data);
318 which = PSSHFD_DATA;
321 if (puffs_getstate(pu) != PUFFS_STATE_RUNNING)
322 return;
324 if (what != (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE)) {
325 puffs_framev_removefd(pu, fd, ECONNRESET);
326 return;
328 close(fd);
330 /* deal with zmobies, beware of half-eaten brain */
331 while (waitpid(-1, &dummy, WNOHANG) > 0)
332 continue;
334 for (nretry = 0;;nretry++) {
335 if ((newfd = pssh_connect(pu, which)) == -1)
336 goto retry2;
338 if (puffs_framev_addfd(pu, newfd,
339 PUFFS_FBIO_READ | PUFFS_FBIO_WRITE) == -1)
340 goto retry1;
342 break;
343 retry1:
344 fprintf(stderr, "reconnect failed... ");
345 close(newfd);
346 retry2:
347 if (nretry < RETRY_MAX) {
348 fprintf(stderr, "retry (%d left)\n", RETRY_MAX-nretry);
349 sleep(nretry);
350 } else {
351 fprintf(stderr, "retry count exceeded, going south\n");
352 exit(1); /* XXXXXXX */
357 static int
358 pssh_connect(struct puffs_usermount *pu, int which)
360 struct psshfs_ctx *pctx = puffs_getspecific(pu);
361 char * const *sshargs = pctx->sshargs;
362 int fds[2];
363 pid_t pid;
364 int dnfd, x;
365 int *sshfd;
366 pid_t *sshpid;
368 if (which == PSSHFD_META) {
369 sshfd = &pctx->sshfd;
370 sshpid = &pctx->sshpid;
371 } else {
372 assert(which == PSSHFD_DATA);
373 sshfd = &pctx->sshfd_data;
374 sshpid = &pctx->sshpid_data;
377 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1)
378 return -1;
380 pid = fork();
381 switch (pid) {
382 case -1:
383 return -1;
384 /*NOTREACHED*/
385 case 0: /* child */
386 if (dup2(fds[0], STDIN_FILENO) == -1)
387 err(1, "child dup2");
388 if (dup2(fds[0], STDOUT_FILENO) == -1)
389 err(1, "child dup2");
390 close(fds[0]);
391 close(fds[1]);
393 dnfd = open(_PATH_DEVNULL, O_RDWR);
394 if (dnfd != -1)
395 dup2(dnfd, STDERR_FILENO);
397 execvp(sshargs[0], sshargs);
398 /*NOTREACHED*/
399 break;
400 default:
401 *sshpid = pid;
402 *sshfd = fds[1];
403 close(fds[0]);
404 break;
407 if (psshfs_handshake(pu, *sshfd) != 0)
408 errx(1, "psshfs_handshake %d", which);
409 x = 1;
410 if (ioctl(*sshfd, FIONBIO, &x) == -1)
411 err(1, "nonblocking descriptor %d", which);
413 return *sshfd;
416 static void *
417 invalone(struct puffs_usermount *pu, struct puffs_node *pn, void *arg)
419 struct psshfs_node *psn = pn->pn_data;
421 psn->attrread = 0;
422 psn->dentread = 0;
423 psn->slread = 0;
425 return NULL;
428 static void
429 psshfs_loopfn(struct puffs_usermount *pu)
432 if (sighup) {
433 puffs_pn_nodewalk(pu, invalone, NULL);
434 sighup = 0;