/share/examples/autofs/driver/autodriver.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 538 lines · 449 code · 38 blank · 51 comment · 118 complexity · 8414de70939d0685d299f2826a19a236 MD5 · raw file

  1. /*
  2. * Copyright (c) 2004 Alfred Perlstein <alfred@FreeBSD.org>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. *
  14. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  15. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  17. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  18. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  19. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  20. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  21. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  22. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  23. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  24. * SUCH DAMAGE.
  25. *
  26. * $Id: autodriver.c,v 1.9 2004/09/08 08:12:21 bright Exp $
  27. * $FreeBSD$
  28. */
  29. #include <ctype.h>
  30. #include <err.h>
  31. #include <errno.h>
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #include <unistd.h>
  36. #include <fcntl.h>
  37. #include <sys/dirent.h>
  38. #include <sys/types.h>
  39. #include <sys/param.h>
  40. #include <sys/mount.h>
  41. #include <sys/poll.h>
  42. #include <sys/stat.h>
  43. #include <libautofs.h>
  44. struct autoentry {
  45. char *ae_mnt; /* autofs mountpoint. */
  46. char *ae_path; /* path under mount. */
  47. char *ae_type; /* fs to be mounted type. */
  48. char *ae_opts; /* options passed to mount. */
  49. char *ae_rpath; /* remote path */
  50. char *ae_free; /* freeme! */
  51. char *ae_fullpath; /* full path to mount */
  52. int ae_line; /* line it came from in the conf. */
  53. int ae_indirect; /* is this an indirect mount? */
  54. int ae_direct; /* is this a direct mount? */
  55. int ae_browse; /* browseable? */
  56. struct autoentry *ae_next; /* next. */
  57. };
  58. struct autoentry *entries;
  59. const char *mount_prog = "mount";
  60. const char *fstype = "autofs";
  61. void *xmalloc(size_t);
  62. void *xcalloc(size_t number, size_t size);
  63. void parsetab(void);
  64. void populate_tab(void);
  65. void doreq(autoh_t, autoreq_t);
  66. void dotheneedful(autoh_t);
  67. void eventloop(void);
  68. int poll_handles(autoh_t *array, int cnt);
  69. int mount_indirect(struct autofs_userreq *req, struct autoentry *ent);
  70. int mount_direct(struct autofs_userreq *req, struct autoentry *ent);
  71. int mount_browse(struct autofs_userreq *req, struct autoentry *ent);
  72. #define DSTR(s) sizeof(s) - 1, s
  73. struct dirent dumbents[] = {
  74. {50, sizeof(struct dirent), DT_DIR, DSTR("one") },
  75. {51, sizeof(struct dirent), DT_DIR, DSTR(".") },
  76. {52, sizeof(struct dirent), DT_DIR, DSTR("..") },
  77. {50, sizeof(struct dirent), DT_DIR, DSTR("two") },
  78. };
  79. void *
  80. xmalloc(size_t size)
  81. {
  82. void *ret;
  83. ret = malloc(size);
  84. if (ret == NULL)
  85. err(1, "malloc %d", (int) size);
  86. return (ret);
  87. }
  88. void *
  89. xcalloc(size_t number, size_t size)
  90. {
  91. void *ret;
  92. ret = calloc(number, size);
  93. if (ret == NULL)
  94. err(1, "calloc %d %d", (int)number, (int)size);
  95. return (ret);
  96. }
  97. void
  98. parsetab(void)
  99. {
  100. FILE *fp;
  101. const char *tab;
  102. char *cp, *p, *line, *opt;
  103. size_t len;
  104. struct autoentry *ent;
  105. int i, lineno, x, gotopt;
  106. const char *expecting = "expecting 'direct', 'indirect' or 'browse'";
  107. const char *tabfiles[] = {
  108. "/etc/autotab", "/usr/local/etc/autotab", "./autotab", NULL
  109. };
  110. lineno = 0;
  111. for (i = 0; (tab = tabfiles[i]) != NULL; i++) {
  112. tab = tabfiles[i];
  113. fp = fopen(tab, "r");
  114. if (fp == NULL)
  115. warn("fopen %s", tab);
  116. if (fp != NULL)
  117. break;
  118. }
  119. if (fp == NULL) {
  120. err(1, "no config file available.");
  121. }
  122. fprintf(stderr, "using config file: %s\n", tab);
  123. while ((cp = fgetln(fp, &len)) != NULL) {
  124. lineno++;
  125. while (len > 0 && isspace(cp[len - 1]))
  126. len--;
  127. line = xmalloc(len + 1);
  128. bcopy(cp, line, len);
  129. line[len] = '\0';
  130. cp = line;
  131. if ((cp = strchr(line, '#')) != NULL)
  132. *cp = '\0';
  133. cp = line;
  134. while (isspace(*cp))
  135. cp++;
  136. if (*cp == '\0') {
  137. free(line);
  138. continue;
  139. }
  140. ent = xcalloc(1, sizeof(*ent));
  141. if ((p = strsep(&cp, " \t")) == NULL)
  142. goto bad;
  143. ent->ae_mnt = p;
  144. if ((p = strsep(&cp, " \t")) == NULL)
  145. goto bad;
  146. ent->ae_path = p;
  147. if ((p = strsep(&cp, " \t")) == NULL)
  148. goto bad;
  149. ent->ae_type = p;
  150. if ((p = strsep(&cp, " \t")) == NULL)
  151. goto bad;
  152. ent->ae_opts = p;
  153. if ((p = strsep(&cp, " \t")) == NULL)
  154. goto bad;
  155. ent->ae_rpath = p;
  156. if ((p = strsep(&cp, " \t")) == NULL)
  157. goto bad;
  158. gotopt = 0;
  159. opt = p;
  160. while ((p = strsep(&opt, ",")) != NULL) {
  161. if (strcmp(p, "indirect") == 0) {
  162. ent->ae_indirect = 1;
  163. gotopt = 1;
  164. } else if (strcmp(p, "direct") == 0) {
  165. ent->ae_direct = 1;
  166. gotopt = 1;
  167. } else if (strcmp(p, "browse") == 0) {
  168. ent->ae_browse = 1;
  169. gotopt = 1;
  170. } else {
  171. warnx("unreconized option '%s', %s",
  172. p, expecting);
  173. goto bad2;
  174. }
  175. }
  176. if (!gotopt) {
  177. warnx("no options specified %s", expecting);
  178. goto bad2;
  179. }
  180. if (ent->ae_direct && ent->ae_indirect) {
  181. warnx("direct and indirect are mutually exclusive");
  182. goto bad2;
  183. }
  184. x = asprintf(&ent->ae_fullpath, "%s/%s",
  185. ent->ae_mnt, ent->ae_path);
  186. if (x == -1)
  187. err(1, "asprintf");
  188. if (strlen(ent->ae_fullpath) + 1 > PATH_MAX) {
  189. warnx("Error in file %s, line %d, "
  190. "mountpath (%s) exceeds PATH_MAX (%d)",
  191. tab, lineno, ent->ae_fullpath, PATH_MAX);
  192. goto bad2;
  193. }
  194. ent->ae_line = lineno;
  195. ent->ae_free = line;
  196. ent->ae_next = entries;
  197. entries = ent;
  198. continue;
  199. bad:
  200. warnx("Parse error in file %s, line %d", tab, lineno);
  201. bad2:
  202. free(ent->ae_fullpath);
  203. free(line);
  204. free(ent);
  205. }
  206. if (ferror(fp))
  207. err(1, "error with file %s", tab);
  208. }
  209. void
  210. populate_tab(void)
  211. {
  212. struct autoentry *ent;
  213. char *path, *cmd;
  214. int error;
  215. autoh_t ah;
  216. path = cmd = NULL;
  217. for (ent = entries; ent != NULL; ent = ent->ae_next) {
  218. free(path);
  219. free(cmd);
  220. error = asprintf(&path, "%s/%s", ent->ae_mnt, ent->ae_path);
  221. if (error == -1)
  222. err(1, "asprintf");
  223. error = asprintf(&cmd, "mkdir -p %s", path);
  224. if (error == -1)
  225. err(1, "asprintf");
  226. error = system(cmd);
  227. if (error) {
  228. warn("system: %s", cmd);
  229. continue;
  230. }
  231. if (autoh_get(ent->ae_mnt, &ah)) {
  232. warn("autoh_get %s", path);
  233. continue;
  234. }
  235. error = autoh_togglepath(ah, AUTO_MOUNTER, getpid(), path);
  236. if (error) {
  237. err(1, "AUTO_MOUNTER %s", path);
  238. continue;
  239. }
  240. if (ent->ae_browse) {
  241. error = autoh_togglepath(ah, AUTO_BROWSE, getpid(),
  242. path);
  243. if (error)
  244. err(1, "AUTO_BROWSE %s", path);
  245. }
  246. if (ent->ae_direct) {
  247. error = autoh_togglepath(ah, AUTO_DIRECT, getpid(),
  248. path);
  249. if (error)
  250. err(1, "AUTO_DIRECT %s", path);
  251. }
  252. if (ent->ae_indirect) {
  253. error = autoh_togglepath(ah, AUTO_INDIRECT, getpid(),
  254. path);
  255. if (error)
  256. err(1, "AUTO_INDIRECT %s", path);
  257. }
  258. autoh_free(ah);
  259. }
  260. free(path);
  261. free(cmd);
  262. }
  263. /*
  264. * Process an autofs request, scan the list of entries in the config
  265. * looking for our node, if found mount it.
  266. */
  267. void
  268. doreq(autoh_t ah, autoreq_t req)
  269. {
  270. struct autoentry *ent;
  271. int error;
  272. int mcmp;
  273. int xid;
  274. const char *mnt;
  275. mnt = autoh_mp(ah);
  276. autoreq_seterrno(req, 0);
  277. for (ent = entries; ent != NULL; ent = ent->ae_next) {
  278. fprintf(stderr, "comparing {%s,%s} to {%s,%s}\n",
  279. mnt, ent->ae_mnt, autoreq_getpath(req), ent->ae_path);
  280. fprintf(stderr, "comparing {%d,%d} to {%d,%d}\n",
  281. (int)strlen(mnt),
  282. (int)strlen(ent->ae_mnt),
  283. (int)strlen(autoreq_getpath(req)),
  284. (int)strlen(ent->ae_path));
  285. autoreq_getxid(req, &xid);
  286. fprintf(stderr, "req xid %d\n", xid);
  287. if ((mcmp = strcmp(mnt, ent->ae_mnt)) != 0) {
  288. fprintf(stderr, "mcmp = %d\n", mcmp);
  289. continue;
  290. }
  291. if (mount_direct(req, ent))
  292. goto serve;
  293. if (mount_indirect(req, ent))
  294. goto serve;
  295. if (mount_browse(req, ent))
  296. goto serve;
  297. }
  298. fprintf(stderr, "no entry found...\n");
  299. autoreq_seterrno(req, ENOENT);
  300. serve:
  301. error = autoreq_serv(ah, req);
  302. if (error == -1) {
  303. warn("AUTOFS_CTL_SERVREQ");
  304. }
  305. }
  306. int
  307. mount_indirect(req, ent)
  308. struct autofs_userreq *req;
  309. struct autoentry *ent;
  310. {
  311. struct stat sb;
  312. char *path, *cmd;
  313. int error, x;
  314. if (ent->ae_indirect != 1) {
  315. fprintf(stderr, "not indirect.\n");
  316. return (0);
  317. }
  318. fprintf(stderr, "indirect mount...\n");
  319. /*
  320. * handle lookups, fake all stat(2) requests... this is bad,
  321. * but we're a driver so we don't care...
  322. * If we don't care about the type of request, then just return.
  323. */
  324. switch (autoreq_getop(req)) {
  325. case AUTOREQ_OP_LOOKUP:
  326. break;
  327. case AUTOREQ_OP_STAT:
  328. fprintf(stderr, "stat\n");
  329. return (1);
  330. default:
  331. fprintf(stderr, "unknown\n");
  332. return (0);
  333. }
  334. if (stat(ent->ae_fullpath, &sb))
  335. return (0);
  336. if (sb.st_ino != autoreq_getdirino(req)) {
  337. fprintf(stderr, "st_ino %d != dirino %d\n",
  338. (int)sb.st_ino, (int)autoreq_getdirino(req));
  339. return (0);
  340. }
  341. x = asprintf(&path, "%s/%s", ent->ae_fullpath, autoreq_getpath(req));
  342. if (x > PATH_MAX) {
  343. autoreq_seterrno(req, ENAMETOOLONG);
  344. return (1);
  345. }
  346. if (mkdir(path, 0555) == -1)
  347. warn("mkdir %s", path);
  348. error = asprintf(&cmd, "%s -t %s -o %s %s/%s %s", mount_prog,
  349. ent->ae_type, ent->ae_opts, ent->ae_rpath, autoreq_getpath(req), path);
  350. fprintf(stderr, "running:\n\t%s\n", cmd);
  351. error = system(cmd);
  352. fprintf(stderr, "error = %d\n", error);
  353. free(cmd);
  354. if (error) {
  355. if (rmdir(path) == -1)
  356. warn("rmdir %s", path);
  357. autoreq_seterrno(req, ENOENT);
  358. } else {
  359. if (stat(path, &sb) != -1)
  360. autoreq_setino(req, sb.st_ino);
  361. /* XXX !!! */
  362. /* req->au_flags = 1; */
  363. }
  364. free(path);
  365. return (1);
  366. }
  367. int
  368. mount_direct(req, ent)
  369. struct autofs_userreq *req;
  370. struct autoentry *ent;
  371. {
  372. struct stat sb;
  373. char *cmd;
  374. int error;
  375. if (ent->ae_direct != 1) {
  376. fprintf(stderr, "not direct.\n");
  377. return (0);
  378. }
  379. fprintf(stderr, "direct mount...\n");
  380. /*
  381. * handle lookups, fake all stat(2) requests... this is bad,
  382. * but we're a driver so we don't care...
  383. * If we don't care about the type of request, then just return.
  384. */
  385. switch (autoreq_getop(req)) {
  386. case AUTOREQ_OP_LOOKUP:
  387. break;
  388. case AUTOREQ_OP_STAT:
  389. return (1);
  390. default:
  391. return (0);
  392. }
  393. if (stat(ent->ae_fullpath, &sb))
  394. return (0);
  395. if (sb.st_ino != autoreq_getino(req))
  396. return (0);
  397. error = asprintf(&cmd, "%s -t %s -o %s %s %s", mount_prog,
  398. ent->ae_type, ent->ae_opts, ent->ae_rpath, ent->ae_fullpath);
  399. if (error == -1)
  400. err(1, "asprintf");
  401. fprintf(stderr, "running:\n\t%s\n", cmd);
  402. error = system(cmd);
  403. fprintf(stderr, "error = %d\n", error);
  404. free(cmd);
  405. if (error) {
  406. autoreq_seterrno(req, ENOENT);
  407. return (1);
  408. }
  409. /* XXX: fix ONLIST in kernel */
  410. /* req->au_flags = 1; */
  411. return (1);
  412. }
  413. int
  414. mount_browse(req, ent)
  415. struct autofs_userreq *req;
  416. struct autoentry *ent;
  417. {
  418. off_t off;
  419. if (ent->ae_browse != 1)
  420. return (0);
  421. if (autoreq_getop(req) != AUTOREQ_OP_READDIR)
  422. return (0);
  423. autoreq_getoffset(req, &off);
  424. if (off < sizeof(dumbents))
  425. autoreq_setaux(req, dumbents, sizeof(dumbents));
  426. fprintf(stderr, "mount_browse: offset %d, size %d\n",
  427. (int)off, (int)sizeof(dumbents));
  428. autoreq_seteof(req, 1);
  429. return (1);
  430. }
  431. /*
  432. * Ask the filesystem passed in if it has a pending request.
  433. * if so process them.
  434. */
  435. void
  436. dotheneedful(autoh_t ah)
  437. {
  438. int cnt, i;
  439. autoreq_t *reqs;
  440. if (autoreq_get(ah, &reqs, &cnt))
  441. err(1, "autoreq_get");
  442. for (i = 0; i < cnt; i++) {
  443. fprintf(stderr, "processing request for '%s' '%s'\n",
  444. autoh_mp(ah), autoreq_getpath(reqs[i]));
  445. doreq(ah, reqs[i]);
  446. }
  447. free(reqs);
  448. }
  449. int
  450. poll_handles(autoh_t *array, int cnt)
  451. {
  452. int i, saved_errno, x;
  453. static struct pollfd *pfd = NULL;
  454. pfd = reallocf(pfd, cnt * sizeof(*pfd));
  455. if (pfd == NULL)
  456. return (-1);
  457. for (i = 0; i < cnt; i++) {
  458. pfd[i].fd = autoh_fd(array[i]);
  459. pfd[i].events = POLLPRI;
  460. pfd[i].revents = 0;
  461. }
  462. fprintf(stderr, "start polling...\n");
  463. x = poll(pfd, cnt, 10000);
  464. saved_errno = errno;
  465. fprintf(stderr, "done polling...\n");
  466. errno = saved_errno;
  467. if (x == -1)
  468. return (-1);
  469. /* at least one fs is ready... */
  470. if (x > 0)
  471. return (0);
  472. return (0);
  473. }
  474. void
  475. eventloop(void)
  476. {
  477. autoh_t *array;
  478. int cnt, i;
  479. fprintf(stderr, "starting event loop...\n");
  480. for ( ;; ) {
  481. if (autoh_getall(&array, &cnt))
  482. err(1, "autoh_getall");
  483. if (poll_handles(array, cnt))
  484. err(1, "poll_handles");
  485. for (i = 0; i < cnt; i++) {
  486. dotheneedful(array[i]);
  487. }
  488. autoh_freeall(array);
  489. }
  490. }
  491. int
  492. main(int argc __unused, char **argv __unused)
  493. {
  494. if (getuid() != 0)
  495. errx(1, "autodriver needs to be run as root to work.");
  496. parsetab();
  497. populate_tab();
  498. eventloop();
  499. return (0);
  500. }