1
#ident "$Id: parse_sun.c,v 1.28 2005/04/05 12:42:42 raven Exp $"
2
/* ----------------------------------------------------------------------- *
4
* parse_sun.c - module for Linux automountd to parse a Sun-format
7
* Copyright 1997 Transmeta Corporation - All Rights Reserved
8
* Copyright 2000 Jeremy Fitzhardinge <jeremy@goop.org>
9
* Copyright 2004, 2005 Ian Kent <raven@themaw.net>
11
* This program is free software; you can redistribute it and/or modify
12
* it under the terms of the GNU General Public License as published by
13
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
14
* USA; either version 2 of the License, or (at your option) any later
15
* version; incorporated herein by reference.
17
* ----------------------------------------------------------------------- */
30
#include <sys/param.h>
31
#include <sys/socket.h>
32
#include <sys/types.h>
34
#include <sys/utsname.h>
35
#include <netinet/in.h>
38
#include "automount.h"
40
#define MODPREFIX "parse(sun): "
42
int parse_version = AUTOFS_PARSE_VERSION; /* Required by protocol */
44
static struct mount_mod *mount_nfs = NULL;
45
static int init_ctr = 0;
48
char *def; /* Define variable */
49
char *val; /* Value to replace with */
50
struct substvar *next;
53
struct parse_context {
54
char *optstr; /* Mount options */
55
struct substvar *subst; /* $-substitutions */
56
int slashify_colons; /* Change colons to slashes? */
63
struct multi_mnt *next;
67
char processor[65]; /* Not defined on Linux, so we make our own */
69
/* Predefined variables: tail of link chain */
70
static struct substvar
71
sv_arch = {"ARCH", un.machine, NULL },
72
sv_cpu = {"CPU", processor, &sv_arch},
73
sv_host = {"HOST", un.nodename, &sv_cpu},
74
sv_osname = {"OSNAME", un.sysname, &sv_host},
75
sv_osrel = {"OSREL", un.release, &sv_osname},
76
sv_osvers = {"OSVERS", un.version, &sv_osrel
79
/* Default context pattern */
81
static char *child_args = NULL;
82
static struct parse_context default_context = {
83
NULL, /* No mount options */
84
&sv_osvers, /* The substvar predefined variables */
85
1 /* Do slashify_colons */
88
/* Free all storage associated with this context */
89
static void kill_context(struct parse_context *ctxt)
91
struct substvar *sv, *nsv;
95
while (sv != &sv_osvers) {
108
/* Find the $-variable matching a certain string fragment */
109
static const struct substvar *findvar(const struct substvar *sv, const char *str, int len)
111
#ifdef ENABLE_EXT_ENV
112
/* holds one env var */
113
static struct substvar sv_env = { NULL, NULL, NULL };
114
static char *substvar_env;
119
if (!strncmp(str, sv->def, len) && sv->def[len] == '\0')
124
#ifdef ENABLE_EXT_ENV
125
/* builtin map failed, try the $ENV */
126
memcpy(etmp, str, len);
129
if ((substvar_env=getenv(etmp)) != NULL) {
130
sv_env.val = substvar_env;
139
* $- and &-expand a Sun-style map entry and return the length of the entry.
140
* If "dst" is NULL, just count the length.
142
int expandsunent(const char *src, char *dst, const char *key,
143
const struct substvar *svc, int slashify_colons)
145
const struct substvar *sv;
146
int len, l, seen_colons;
153
while ((ch = *src++)) {
166
p = strchr(++src, '}');
168
/* Ignore rest of string */
173
sv = findvar(svc, src, p - src);
177
strcpy(dst, sv->val);
185
while (isalnum(*p) || *p == '_')
187
sv = findvar(svc, src, p - src);
191
strcpy(dst, sv->val);
216
(seen_colons && slashify_colons) ? '/' : ':';
237
* Skip whitespace in a string; if we hit a #, consider the rest of the
240
const char *skipspace(const char *whence)
253
case '#': /* comment: skip to end of string */
254
while (*whence != '\0')
265
* Check a string to see if a colon appears before the next '/'.
267
int check_colon(const char *str)
269
char *ptr = (char *) str;
271
while (*ptr && *ptr != ':' && *ptr != '/') {
275
if (!*ptr || *ptr == '/')
281
/* Get the length of a chunk delimitered by whitespace */
282
int chunklen(const char *whence, int expect_colon)
287
for (; *whence; whence++, n++) {
302
/* Skip space or tab if we expect a colon */
325
* Compare str with pat. Return 0 if compare equal or
326
* str is an abbreviation of pat of no less than mchr characters.
328
int strmcmp(const char *str, const char *pat, int mchr)
332
while (*str == *pat) {
340
if (!*str && nchr > mchr)
346
int parse_init(int argc, const char *const *argv, void **context)
348
struct parse_context *ctxt;
355
/* Get processor information for predefined escapes */
359
/* uname -p is not defined on Linux. Make it the same as uname -m,
360
except make it return i386 on all x86 (x >= 3) */
361
strcpy(processor, un.machine);
362
if (processor[0] == 'i' && processor[1] >= '3' &&
363
!strcmp(processor + 2, "86"))
367
/* Set up context and escape chain */
369
if (!(ctxt = (struct parse_context *) malloc(sizeof(struct parse_context)))) {
370
crit(MODPREFIX "malloc: %m");
373
*context = (void *) ctxt;
375
*ctxt = default_context;
378
/* Look for options and capture, and create new defines if we need to */
380
for (i = 0; i < argc; i++) {
381
if (argv[i][0] == '-' &&
382
(argv[i][1] == 'D' || argv[i][1] == '-') ) {
383
switch (argv[i][1]) {
385
sv = malloc(sizeof(struct substvar));
387
error(MODPREFIX "malloc: %m");
391
sv->def = strdup(argv[i] + 2);
393
sv->def = strdup(argv[i]);
400
error(MODPREFIX "strdup: %m");
403
sv->val = strchr(sv->def, '=');
408
/* we use 5 for the "-D", the "=", and the null */
410
child_args = realloc(child_args, strlen(child_args) + strlen(sv->def) + strlen(sv->val) + 5);
411
strcat(child_args, ",");
413
else { /* No comma, so only +4 */
414
child_args = malloc(strlen(sv->def) + strlen(sv->val) + 4);
417
strcat(child_args, "-D");
418
strcat(child_args, sv->def);
419
strcat(child_args, "=");
420
strcat(child_args, sv->val);
421
sv->next = ctxt->subst;
427
if (!strncmp(argv[i] + 2, "no-", 3)) {
435
if (!strmcmp(xopt, "slashify-colons", 1))
436
ctxt->slashify_colons = bval;
438
error(MODPREFIX "unknown option: %s",
444
error(MODPREFIX "unknown option: %s", argv[i]);
448
int offset = (argv[i][0] == '-' ? 1 : 0);
449
len = strlen(argv[i] + offset);
452
(char *) realloc(ctxt->optstr, optlen + len + 2);
455
noptstr[optlen] = ',';
456
strcpy(noptstr + optlen + 1, argv[i] + offset);
459
noptstr = (char *) malloc(len + 1);
460
strcpy(noptstr, argv[i] + offset);
465
crit(MODPREFIX "%m");
468
ctxt->optstr = noptstr;
469
debug(MODPREFIX "init gathered options: %s",
474
/* We only need this once. NFS mounts are so common that we cache
477
if ((mount_nfs = open_mount("nfs", MODPREFIX))) {
489
static char *dequote(const char *str, int strlen)
491
char *ret = malloc(strlen + 1);
494
int origlen = strlen;
500
for (scp = str; strlen > 0 && *scp; scp++, strlen--) {
501
if (*scp == '\\' && !quote ) {
510
debug(MODPREFIX "dequote(\"%.*s\") -> %s", origlen, str, ret);
515
static const char *parse_options(const char *str, char **ret)
517
const char *cp = str;
526
*ret = dequote(cp, len = chunklen(cp, 0));
531
static char *concat_options(char *left, char *right)
535
if (left == NULL || *left == '\0') {
541
if (right == NULL || *right == '\0') {
546
ret = malloc(strlen(left) + strlen(right) + 2);
549
error(MODPREFIX "concat_options malloc: %m");
553
sprintf(ret, "%s,%s", left, right);
561
static int sun_mount(const char *root, const char *name, int namelen,
562
const char *loc, int loclen, const char *options)
564
char *fstype = "nfs"; /* Default filesystem type */
570
if (*options == '\0')
577
int len = strlen(options) + 1;
579
noptions = np = alloca(len);
582
/* Extract fstype= pseudo option */
583
for (comma = options; *comma != '\0';) {
586
while (*comma == ',')
591
while (*comma != '\0' && *comma != ',')
594
if (strncmp("fstype=", cp, 7) == 0) {
595
int typelen = comma - (cp + 7);
596
fstype = alloca(typelen + 1);
597
memcpy(fstype, cp + 7, typelen);
598
fstype[typelen] = '\0';
599
} else if (strncmp("strict", cp, 6) == 0) {
601
} else if (strncmp("nonstrict", cp, 9) == 0) {
604
memcpy(np, cp, comma - cp + 1);
605
np += comma - cp + 1;
609
if (np > noptions + len) {
610
warn(MODPREFIX "options string truncated");
619
if (child_args && !strcmp(fstype, "autofs")) {
623
noptions = alloca(strlen(child_args) + 1);
626
noptions = alloca(strlen(options) + strlen(child_args) + 2);
629
strcpy(noptions, options);
630
strcat(noptions, ",");
635
strcat(noptions, child_args);
638
error(MODPREFIX "alloca failed for options");
642
while (*name == '/') {
647
mountpoint = alloca(namelen + 1);
648
sprintf(mountpoint, "%.*s", namelen, name);
650
what = alloca(loclen + 1);
651
memcpy(what, loc, loclen);
654
if (!strcmp(fstype, "autofs") && strchr(loc, ':') == NULL) {
661
strcpy(mtype, "ldap:");
663
strcpy(mtype, "file:");
666
strcpy(mtype, "yp:");
669
what = alloca(loclen + mtype_len + 1);
670
memcpy(what, mtype, mtype_len);
671
memcpy(what + mtype_len, loc, loclen);
672
what[loclen + mtype_len] = '\0';
674
what = alloca(loclen + 1);
675
memcpy(what, loc, loclen);
680
"mounting root %s, mountpoint %s, what %s, fstype %s, options %s\n",
681
root, mountpoint, what, fstype, options);
683
if (!strcmp(fstype, "nfs")) {
684
rv = mount_nfs->mount_mount(root, mountpoint, strlen(mountpoint),
685
what, fstype, options, mount_nfs->context);
687
/* Generic mount routine */
688
rv = do_mount(root, mountpoint, strlen(mountpoint), what, fstype,
699
* Build list of mounts in shortest -> longest order.
700
* Pass in list head and return list head.
702
struct multi_mnt *multi_add_list(struct multi_mnt *list,
703
char *path, char *options, char *location)
705
struct multi_mnt *mmptr, *new, *old = NULL;
708
if (!path || !options || !location)
711
new = malloc(sizeof(struct multi_mnt));
716
new->options = options;
717
new->location = location;
722
if (plen <= strlen(mmptr->path))
732
return old ? list : new;
735
void multi_free_list(struct multi_mnt *list)
737
struct multi_mnt *next;
744
struct multi_mnt *this = next;
755
free(this->location);
762
* Scan map entry looking for evidence it has multiple key/mapent
765
static int check_is_multi(const char *mapent)
767
const char *p = (char *) mapent;
775
* After the first chunk there can be additional
776
* locations (possibly not multi) or possibly an
777
* options string if the first entry includes the
778
* optional '/' (is multi). Following this any
779
* path that begins with '/' indicates a mutil-mount
783
if (*p == '/' || *p == '-') {
795
* Expect either a path or location
796
* after which it's a multi mount.
798
p += chunklen(p, check_colon(p));
807
* [-options] location [location] ...
808
* [-options] [mountpoint [-options] location [location] ... ]...
810
int parse_mount(const char *root, const char *name,
811
int name_len, const char *mapent, void *context)
813
struct parse_context *ctxt = (struct parse_context *) context;
814
char *pmapent, *options;
819
mapent_len = expandsunent(mapent, NULL, name, ctxt->subst, ctxt->slashify_colons);
820
pmapent = alloca(mapent_len + 1);
822
error(MODPREFIX "alloca: %m");
825
pmapent[mapent_len] = '\0';
827
expandsunent(mapent, pmapent, name, ctxt->subst, ctxt->slashify_colons);
829
debug(MODPREFIX "expanded entry: %s", pmapent);
831
options = strdup(ctxt->optstr ? ctxt->optstr : "");
833
error(MODPREFIX "strdup: %m");
836
optlen = strlen(options);
838
p = skipspace(pmapent);
840
/* Deal with 0 or more options */
843
char *noptions = NULL;
845
p = parse_options(p, &noptions);
846
options = concat_options(options, noptions);
848
if (options == NULL) {
849
error(MODPREFIX "concat_options: %m");
856
debug(MODPREFIX "gathered options: %s", options);
858
if (check_is_multi(p)) {
859
struct multi_mnt *list, *head = NULL;
863
multi_root = alloca(strlen(root) + name_len + 2);
865
error(MODPREFIX "alloca: %m");
870
strcpy(multi_root, root);
871
strcat(multi_root, "/");
872
strcat(multi_root, name);
874
/* It's a multi-mount; deal with it */
876
char *myoptions = strdup(options);
879
if (myoptions == NULL) {
880
error(MODPREFIX "multi strdup: %m");
882
multi_free_list(head);
886
path = dequote(p, l = chunklen(p, 0));
888
error(MODPREFIX "out of memory");
891
multi_free_list(head);
898
/* Local options are appended to per-map options */
903
p = parse_options(p, &newopt);
904
myoptions = concat_options(myoptions, newopt);
906
if (myoptions == NULL) {
908
"multi concat_options: %m");
911
multi_free_list(head);
918
/* Skip over colon escape */
922
loc = dequote(p, l = chunklen(p, check_colon(p)));
924
error(MODPREFIX "out of memory");
928
multi_free_list(head);
935
while (*p && *p != '/') {
938
ent = dequote(p, l = chunklen(p, check_colon(p)));
940
error(MODPREFIX "out of memory");
944
multi_free_list(head);
948
loc = realloc(loc, strlen(loc) + l + 2);
950
error(MODPREFIX "out of memory");
955
multi_free_list(head);
969
head = multi_add_list(list, path, myoptions, loc);
975
multi_free_list(list);
983
"multimount: %.*s on %.*s with options %s",
984
strlen(list->location), list->location,
985
strlen(list->path), list->path, list->options);
987
rv = sun_mount(multi_root,
988
list->path, strlen(list->path),
989
list->location, strlen(list->location),
992
/* Convert non-strict failure into success */
995
debug("parse_mount: ignoring failure of non-strict mount");
1002
multi_free_list(head);
1007
/* Normal (non-multi) entries */
1013
p++; /* Sun escape for entries starting with / */
1015
loc = dequote(p, l = chunklen(p, check_colon(p)));
1017
error(MODPREFIX "out of memory");
1028
ent = dequote(p, l = chunklen(p, check_colon(p)));
1030
error(MODPREFIX "out of memory");
1035
loc = realloc(loc, strlen(loc) + l + 2);
1037
error(MODPREFIX "out of memory");
1052
loclen = strlen(loc);
1054
error(MODPREFIX "entry %s is empty!", name);
1060
debug(MODPREFIX "core of entry: options=%s, loc=%.*s",
1061
options, loclen, loc);
1063
rv = sun_mount(root, name, name_len, loc, loclen, options);
1064
/* non-strict failure to normal failure for ordinary mount */
1075
int parse_done(void *context)
1078
struct parse_context *ctxt = (struct parse_context *) context;
1080
if (--init_ctr == 0) {
1081
rv = close_mount(mount_nfs);