]> git.decadent.org.uk Git - nfs-utils.git/blob - support/nfs/exports.c
ea96400d79ca73f9292855a06d593c9ae3eea716
[nfs-utils.git] / support / nfs / exports.c
1 /*
2  * support/nfs/export.c
3  *
4  * Parse the exports file. Derived from the unfsd implementation.
5  *
6  * Authors:     Donald J. Becker, <becker@super.org>
7  *              Rick Sladkey, <jrs@world.std.com>
8  *              Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
9  *              Olaf Kirch, <okir@monad.swb.de>
10  *              Alexander O. Yuriev, <alex@bach.cis.temple.edu>
11  *
12  *              This software maybe be used for any purpose provided
13  *              the above copyright notice is retained.  It is supplied
14  *              as is, with no warranty expressed or implied.
15  */
16
17 #ifdef HAVE_CONFIG_H
18 #include <config.h>
19 #endif
20
21 #include <sys/param.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <ctype.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include "nfslib.h"
29 #include "exportfs.h"
30 #include "xmalloc.h"
31 #include "xlog.h"
32 #include "xio.h"
33 #include "pseudoflavors.h"
34
35 #define EXPORT_DEFAULT_FLAGS    \
36   (NFSEXP_READONLY|NFSEXP_ROOTSQUASH|NFSEXP_GATHERED_WRITES|NFSEXP_NOSUBTREECHECK)
37
38 struct flav_info flav_map[] = {
39         { "krb5",       RPC_AUTH_GSS_KRB5       },
40         { "krb5i",      RPC_AUTH_GSS_KRB5I      },
41         { "krb5p",      RPC_AUTH_GSS_KRB5P      },
42         { "lipkey",     RPC_AUTH_GSS_LKEY       },
43         { "lipkey-i",   RPC_AUTH_GSS_LKEYI      },
44         { "lipkey-p",   RPC_AUTH_GSS_LKEYP      },
45         { "spkm3",      RPC_AUTH_GSS_SPKM       },
46         { "spkm3i",     RPC_AUTH_GSS_SPKMI      },
47         { "spkm3p",     RPC_AUTH_GSS_SPKMP      },
48         { "unix",       AUTH_UNIX               },
49         { "sys",        AUTH_SYS                },
50         { "null",       AUTH_NULL               },
51         { "none",       AUTH_NONE               },
52 };
53
54 const int flav_map_size = sizeof(flav_map)/sizeof(flav_map[0]);
55
56 int export_errno;
57
58 static char     *efname = NULL;
59 static XFILE    *efp = NULL;
60 static int      first;
61 static int      has_default_opts, has_default_subtree_opts;
62 static int      *squids = NULL, nsquids = 0,
63                 *sqgids = NULL, nsqgids = 0;
64
65 static int      getexport(char *exp, int len);
66 static int      getpath(char *path, int len);
67 static int      parseopts(char *cp, struct exportent *ep, int warn, int *had_subtree_opt_ptr);
68 static int      parsesquash(char *list, int **idp, int *lenp, char **ep);
69 static int      parsenum(char **cpp);
70 static void     freesquash(void);
71 static void     syntaxerr(char *msg);
72
73 void
74 setexportent(char *fname, char *type)
75 {
76         if (efp)
77                 endexportent();
78         if (!fname)
79                 fname = _PATH_EXPORTS;
80         if (!(efp = xfopen(fname, type)))
81                 xlog(L_ERROR, "can't open %s for %sing",
82                                 fname, strcmp(type, "r")? "writ" : "read");
83         efname = strdup(fname);
84         first = 1;
85 }
86
87 struct exportent *
88 getexportent(int fromkernel, int fromexports)
89 {
90         static struct exportent ee, def_ee;
91         char            exp[512], *hostname;
92         char            rpath[MAXPATHLEN+1];
93         char            *opt, *sp;
94         int             ok;
95
96         if (!efp)
97                 return NULL;
98
99         freesquash();
100
101         if (first || (ok = getexport(exp, sizeof(exp))) == 0) {
102                 has_default_opts = 0;
103                 has_default_subtree_opts = 0;
104         
105                 def_ee.e_flags = EXPORT_DEFAULT_FLAGS;
106                 /* some kernels assume the default is sync rather than
107                  * async.  More recent kernels always report one or other,
108                  * but this test makes sure we assume same as kernel
109                  * Ditto for wgather
110                  */
111                 if (fromkernel) {
112                         def_ee.e_flags &= ~NFSEXP_ASYNC;
113                         def_ee.e_flags &= ~NFSEXP_GATHERED_WRITES;
114                 }
115                 def_ee.e_anonuid = 65534;
116                 def_ee.e_anongid = 65534;
117                 def_ee.e_squids = NULL;
118                 def_ee.e_sqgids = NULL;
119                 def_ee.e_mountpoint = NULL;
120                 def_ee.e_fslocmethod = FSLOC_NONE;
121                 def_ee.e_fslocdata = NULL;
122                 def_ee.e_nsquids = 0;
123                 def_ee.e_nsqgids = 0;
124
125                 ok = getpath(def_ee.e_path, sizeof(def_ee.e_path));
126                 if (ok <= 0)
127                         return NULL;
128
129                 strncpy (def_ee.m_path, def_ee.e_path, sizeof (def_ee.m_path) - 1);
130                 def_ee.m_path [sizeof (def_ee.m_path) - 1] = '\0';
131                 ok = getexport(exp, sizeof(exp));
132         }
133         if (ok < 0) {
134                 xlog(L_ERROR, "expected client(options...)");
135                 export_errno = EINVAL;
136                 return NULL;
137         }
138         first = 0;
139                 
140         /* Check for default options */
141         if (exp[0] == '-') {
142                 if (parseopts(exp + 1, &def_ee, 0, &has_default_subtree_opts) < 0)
143                         return NULL;
144                 
145                 has_default_opts = 1;
146
147                 ok = getexport(exp, sizeof(exp));
148                 if (ok < 0) {
149                         xlog(L_ERROR, "expected client(options...)");
150                         export_errno = EINVAL;
151                         return NULL;
152                 }
153         }
154
155         ee = def_ee;
156
157         /* Check for default client */
158         if (ok == 0)
159                 exp[0] = '\0';
160
161         hostname = exp;
162         if ((opt = strchr(exp, '(')) != NULL) {
163                 if (opt == exp) {
164                         xlog(L_WARNING, "No host name given with %s %s, suggest *%s to avoid warning", ee.e_path, exp, exp);
165                         hostname = "*";
166                 }
167                 *opt++ = '\0';
168                 if (!(sp = strchr(opt, ')')) || sp[1] != '\0') {
169                         syntaxerr("bad option list");
170                         export_errno = EINVAL;
171                         return NULL;
172                 }
173                 *sp = '\0';
174         } else {
175                 if (!has_default_opts)
176                         xlog(L_WARNING, "No options for %s %s: suggest %s(sync) to avoid warning", ee.e_path, exp, exp);
177         }
178         if (strlen(hostname) >= sizeof(ee.e_hostname)) {
179                 syntaxerr("client name too long");
180                 export_errno = EINVAL;
181                 return NULL;
182         }
183         strncpy(ee.e_hostname, hostname, sizeof (ee.e_hostname) - 1);
184         ee.e_hostname[sizeof (ee.e_hostname) - 1] = '\0';
185
186         if (parseopts(opt, &ee, fromexports && !has_default_subtree_opts, NULL) < 0)
187                 return NULL;
188
189         /* resolve symlinks */
190         if (realpath(ee.e_path, rpath) != NULL) {
191                 rpath[sizeof (rpath) - 1] = '\0';
192                 strncpy(ee.e_path, rpath, sizeof (ee.e_path) - 1);
193                 ee.e_path[sizeof (ee.e_path) - 1] = '\0';
194                 strncpy (ee.m_path, ee.e_path, sizeof (ee.m_path) - 1);
195                 ee.m_path [sizeof (ee.m_path) - 1] = '\0';
196         }
197
198         return &ee;
199 }
200
201 void
202 putexportent(struct exportent *ep)
203 {
204         FILE    *fp;
205         int     *id, i;
206         char    *esc=ep->e_path;
207
208         if (!efp)
209                 return;
210
211         fp = efp->x_fp;
212         for (i=0; esc[i]; i++)
213                 if (iscntrl(esc[i]) || esc[i] == '"' || esc[i] == '\\' || esc[i] == '#' || isspace(esc[i]))
214                         fprintf(fp, "\\%03o", esc[i]);
215                 else
216                         fprintf(fp, "%c", esc[i]);
217
218         fprintf(fp, "\t%s(", ep->e_hostname);
219         fprintf(fp, "%s,", (ep->e_flags & NFSEXP_READONLY)? "ro" : "rw");
220         fprintf(fp, "%ssync,", (ep->e_flags & NFSEXP_ASYNC)? "a" : "");
221         fprintf(fp, "%swdelay,", (ep->e_flags & NFSEXP_GATHERED_WRITES)?
222                                 "" : "no_");
223         fprintf(fp, "%shide,", (ep->e_flags & NFSEXP_NOHIDE)?
224                                 "no" : "");
225         fprintf(fp, "%scrossmnt,", (ep->e_flags & NFSEXP_CROSSMOUNT)?
226                                 "" : "no");
227         fprintf(fp, "%ssecure,", (ep->e_flags & NFSEXP_INSECURE_PORT)?
228                                 "in" : "");
229         fprintf(fp, "%sroot_squash,", (ep->e_flags & NFSEXP_ROOTSQUASH)?
230                                 "" : "no_");
231         fprintf(fp, "%sall_squash,", (ep->e_flags & NFSEXP_ALLSQUASH)?
232                                 "" : "no_");
233         fprintf(fp, "%ssubtree_check,", (ep->e_flags & NFSEXP_NOSUBTREECHECK)?
234                 "no_" : "");
235         fprintf(fp, "%ssecure_locks,", (ep->e_flags & NFSEXP_NOAUTHNLM)?
236                 "in" : "");
237         fprintf(fp, "%sacl,", (ep->e_flags & NFSEXP_NOACL)?
238                 "no_" : "");
239         if (ep->e_flags & NFSEXP_FSID) {
240                 fprintf(fp, "fsid=%d,", ep->e_fsid);
241         }
242         if (ep->e_uuid)
243                 fprintf(fp, "fsid=%s,", ep->e_uuid);
244         if (ep->e_mountpoint)
245                 fprintf(fp, "mountpoint%s%s,",
246                         ep->e_mountpoint[0]?"=":"", ep->e_mountpoint);
247         switch (ep->e_fslocmethod) {
248         case FSLOC_NONE:
249                 break;
250         case FSLOC_REFER:
251                 fprintf(fp, "refer=%s,", ep->e_fslocdata);
252                 break;
253         case FSLOC_REPLICA:
254                 fprintf(fp, "replicas=%s,", ep->e_fslocdata);
255                 break;
256 #ifdef DEBUG
257         case FSLOC_STUB:
258                 fprintf(fp, "fsloc=stub,");
259                 break;
260 #endif
261         default:
262                 xlog(L_ERROR, "unknown fsloc method for %s:%s",
263                      ep->e_hostname, ep->e_path);
264         }
265         if ((id = ep->e_squids) != NULL) {
266                 fprintf(fp, "squash_uids=");
267                 for (i = 0; i < ep->e_nsquids; i += 2)
268                         if (id[i] != id[i+1])
269                                 fprintf(fp, "%d-%d,", id[i], id[i+1]);
270                         else
271                                 fprintf(fp, "%d,", id[i]);
272         }
273         if ((id = ep->e_sqgids) != NULL) {
274                 fprintf(fp, "squash_gids=");
275                 for (i = 0; i < ep->e_nsquids; i += 2)
276                         if (id[i] != id[i+1])
277                                 fprintf(fp, "%d-%d,", id[i], id[i+1]);
278                         else
279                                 fprintf(fp, "%d,", id[i]);
280         }
281         fprintf(fp, "anonuid=%d,anongid=%d)\n", ep->e_anonuid, ep->e_anongid);
282 }
283
284 void
285 endexportent(void)
286 {
287         if (efp)
288                 xfclose(efp);
289         efp = NULL;
290         if (efname)
291                 free(efname);
292         efname = NULL;
293         freesquash();
294 }
295
296 void
297 dupexportent(struct exportent *dst, struct exportent *src)
298 {
299         int     n;
300
301         *dst = *src;
302         if ((n = src->e_nsquids) != 0) {
303                 dst->e_squids = (int *) xmalloc(n * sizeof(int));
304                 memcpy(dst->e_squids, src->e_squids, n * sizeof(int));
305         }
306         if ((n = src->e_nsqgids) != 0) {
307                 dst->e_sqgids = (int *) xmalloc(n * sizeof(int));
308                 memcpy(dst->e_sqgids, src->e_sqgids, n * sizeof(int));
309         }
310         if (src->e_mountpoint)
311                 dst->e_mountpoint = strdup(src->e_mountpoint);
312         if (src->e_fslocdata)
313                 dst->e_fslocdata = strdup(src->e_fslocdata);
314 }
315
316 struct exportent *
317 mkexportent(char *hname, char *path, char *options)
318 {
319         static struct exportent ee;
320
321         ee.e_flags = EXPORT_DEFAULT_FLAGS;
322         ee.e_anonuid = 65534;
323         ee.e_anongid = 65534;
324         ee.e_squids = NULL;
325         ee.e_sqgids = NULL;
326         ee.e_mountpoint = NULL;
327         ee.e_fslocmethod = FSLOC_NONE;
328         ee.e_fslocdata = NULL;
329         ee.e_nsquids = 0;
330         ee.e_nsqgids = 0;
331         ee.e_uuid = NULL;
332
333         if (strlen(hname) >= sizeof(ee.e_hostname)) {
334                 xlog(L_WARNING, "client name %s too long", hname);
335                 return NULL;
336         }
337         strncpy(ee.e_hostname, hname, sizeof (ee.e_hostname) - 1);
338         ee.e_hostname[sizeof (ee.e_hostname) - 1] = '\0';
339         if (strlen(path) >= sizeof(ee.e_path)) {
340                 xlog(L_WARNING, "path name %s too long", path);
341                 return NULL;
342         }
343         strncpy(ee.e_path, path, sizeof (ee.e_path));
344         ee.e_path[sizeof (ee.e_path) - 1] = '\0';
345         strncpy (ee.m_path, ee.e_path, sizeof (ee.m_path) - 1);
346         ee.m_path [sizeof (ee.m_path) - 1] = '\0';
347         if (parseopts(options, &ee, 0, NULL) < 0)
348                 return NULL;
349         return &ee;
350 }
351
352 int
353 updateexportent(struct exportent *eep, char *options)
354 {
355         if (parseopts(options, eep, 0, NULL) < 0)
356                 return 0;
357         return 1;
358 }
359
360
361 static int valid_uuid(char *uuid)
362 {
363         /* must have 32 hex digits */
364         int cnt;
365         for (cnt = 0 ; *uuid; uuid++)
366                 if (isxdigit(*uuid))
367                         cnt++;
368         return cnt == 32;
369 }
370
371 /*
372  * Parse option string pointed to by cp and set mount options accordingly.
373  */
374 static int
375 parseopts(char *cp, struct exportent *ep, int warn, int *had_subtree_opt_ptr)
376 {
377         int     had_subtree_opt = 0;
378         char    *flname = efname?efname:"command line";
379         int     flline = efp?efp->x_line:0;
380
381         squids = ep->e_squids; nsquids = ep->e_nsquids;
382         sqgids = ep->e_sqgids; nsqgids = ep->e_nsqgids;
383
384         if (!cp)
385                 goto out;
386
387         while (isblank(*cp))
388                 cp++;
389
390         while (*cp) {
391                 char *opt = strdup(cp);
392                 char *optstart = cp;
393                 while (*cp && *cp != ',')
394                         cp++;
395                 if (*cp) {
396                         opt[cp-optstart] = '\0';
397                         cp++;
398                 }
399
400                 /* process keyword */
401                 if (strcmp(opt, "ro") == 0)
402                         ep->e_flags |= NFSEXP_READONLY;
403                 else if (strcmp(opt, "rw") == 0)
404                         ep->e_flags &= ~NFSEXP_READONLY;
405                 else if (!strcmp(opt, "secure"))
406                         ep->e_flags &= ~NFSEXP_INSECURE_PORT;
407                 else if (!strcmp(opt, "insecure"))
408                         ep->e_flags |= NFSEXP_INSECURE_PORT;
409                 else if (!strcmp(opt, "sync"))
410                         ep->e_flags &= ~NFSEXP_ASYNC;
411                 else if (!strcmp(opt, "async"))
412                         ep->e_flags |= NFSEXP_ASYNC;
413                 else if (!strcmp(opt, "nohide"))
414                         ep->e_flags |= NFSEXP_NOHIDE;
415                 else if (!strcmp(opt, "hide"))
416                         ep->e_flags &= ~NFSEXP_NOHIDE;
417                 else if (!strcmp(opt, "crossmnt"))
418                         ep->e_flags |= NFSEXP_CROSSMOUNT;
419                 else if (!strcmp(opt, "nocrossmnt"))
420                         ep->e_flags &= ~NFSEXP_CROSSMOUNT;
421                 else if (!strcmp(opt, "wdelay"))
422                         ep->e_flags |= NFSEXP_GATHERED_WRITES;
423                 else if (!strcmp(opt, "no_wdelay"))
424                         ep->e_flags &= ~NFSEXP_GATHERED_WRITES;
425                 else if (strcmp(opt, "root_squash") == 0)
426                         ep->e_flags |= NFSEXP_ROOTSQUASH;
427                 else if (!strcmp(opt, "no_root_squash"))
428                         ep->e_flags &= ~NFSEXP_ROOTSQUASH;
429                 else if (strcmp(opt, "all_squash") == 0)
430                         ep->e_flags |= NFSEXP_ALLSQUASH;
431                 else if (strcmp(opt, "no_all_squash") == 0)
432                         ep->e_flags &= ~NFSEXP_ALLSQUASH;
433                 else if (strcmp(opt, "subtree_check") == 0) {
434                         had_subtree_opt = 1;
435                         ep->e_flags &= ~NFSEXP_NOSUBTREECHECK;
436                 } else if (strcmp(opt, "no_subtree_check") == 0) {
437                         had_subtree_opt = 1;
438                         ep->e_flags |= NFSEXP_NOSUBTREECHECK;
439                 } else if (strcmp(opt, "auth_nlm") == 0)
440                         ep->e_flags &= ~NFSEXP_NOAUTHNLM;
441                 else if (strcmp(opt, "no_auth_nlm") == 0)
442                         ep->e_flags |= NFSEXP_NOAUTHNLM;
443                 else if (strcmp(opt, "secure_locks") == 0)
444                         ep->e_flags &= ~NFSEXP_NOAUTHNLM;
445                 else if (strcmp(opt, "insecure_locks") == 0)
446                         ep->e_flags |= NFSEXP_NOAUTHNLM;
447                 else if (strcmp(opt, "acl") == 0)
448                         ep->e_flags &= ~NFSEXP_NOACL;
449                 else if (strcmp(opt, "no_acl") == 0)
450                         ep->e_flags |= NFSEXP_NOACL;
451                 else if (strncmp(opt, "anonuid=", 8) == 0) {
452                         char *oe;
453                         ep->e_anonuid = strtol(opt+8, &oe, 10);
454                         if (opt[8]=='\0' || *oe != '\0') {
455                                 xlog(L_ERROR, "%s: %d: bad anonuid \"%s\"\n",
456                                      flname, flline, opt);      
457 bad_option:
458                                 free(opt);
459                                 export_errno = EINVAL;
460                                 return -1;
461                         }
462                 } else if (strncmp(opt, "anongid=", 8) == 0) {
463                         char *oe;
464                         ep->e_anongid = strtol(opt+8, &oe, 10);
465                         if (opt[8]=='\0' || *oe != '\0') {
466                                 xlog(L_ERROR, "%s: %d: bad anongid \"%s\"\n",
467                                      flname, flline, opt);      
468                                 goto bad_option;
469                         }
470                 } else if (strncmp(opt, "squash_uids=", 12) == 0) {
471                         if (parsesquash(opt+12, &squids, &nsquids, &cp) < 0) {
472                                 goto bad_option;
473                         }
474                 } else if (strncmp(opt, "squash_gids=", 12) == 0) {
475                         if (parsesquash(opt+12, &sqgids, &nsqgids, &cp) < 0) {
476                                 goto bad_option;
477                         }
478                 } else if (strncmp(opt, "fsid=", 5) == 0) {
479                         char *oe;
480                         if (strcmp(opt+5, "root") == 0) {
481                                 ep->e_fsid = 0;
482                                 ep->e_flags |= NFSEXP_FSID;
483                         } else {
484                                 ep->e_fsid = strtoul(opt+5, &oe, 0);
485                                 if (opt[5]!='\0' && *oe == '\0') 
486                                         ep->e_flags |= NFSEXP_FSID;
487                                 else if (valid_uuid(opt+5))
488                                         ep->e_uuid = strdup(opt+7);
489                                 else {
490                                         xlog(L_ERROR, "%s: %d: bad fsid \"%s\"\n",
491                                              flname, flline, opt);      
492                                         goto bad_option;
493                                 }
494                         }
495                 } else if (strcmp(opt, "mountpoint")==0 ||
496                            strcmp(opt, "mp") == 0 ||
497                            strncmp(opt, "mountpoint=", 11)==0 ||
498                            strncmp(opt, "mp=", 3) == 0) {
499                         char * mp = strchr(opt, '=');
500                         if (mp)
501                                 ep->e_mountpoint = strdup(mp+1);
502                         else
503                                 ep->e_mountpoint = strdup("");
504 #ifdef DEBUG
505                 } else if (strncmp(opt, "fsloc=", 6) == 0) {
506                         if (strcmp(opt+6, "stub") == 0)
507                                 ep->e_fslocmethod = FSLOC_STUB;
508                         else {
509                                 xlog(L_ERROR, "%s:%d: bad option %s\n",
510                                      flname, flline, opt);
511                                 goto bad_option;
512                         }
513 #endif
514                 } else if (strncmp(opt, "refer=", 6) == 0) {
515                         ep->e_fslocmethod = FSLOC_REFER;
516                         ep->e_fslocdata = strdup(opt+6);
517                 } else if (strncmp(opt, "replicas=", 9) == 0) {
518                         ep->e_fslocmethod = FSLOC_REPLICA;
519                         ep->e_fslocdata = strdup(opt+9);
520                 } else {
521                         xlog(L_ERROR, "%s:%d: unknown keyword \"%s\"\n",
522                                         flname, flline, opt);
523                         ep->e_flags |= NFSEXP_ALLSQUASH | NFSEXP_READONLY;
524                         goto bad_option;
525                 }
526                 free(opt);
527                 while (isblank(*cp))
528                         cp++;
529         }
530
531         ep->e_squids = squids;
532         ep->e_sqgids = sqgids;
533         ep->e_nsquids = nsquids;
534         ep->e_nsqgids = nsqgids;
535
536 out:
537         if (warn && !had_subtree_opt)
538                 xlog(L_WARNING, "%s [%d]: Neither 'subtree_check' or 'no_subtree_check' specified for export \"%s:%s\".\n"
539                                 "  Assuming default behaviour ('no_subtree_check').\n"
540                                 "  NOTE: this default has changed since nfs-utils version 1.0.x\n",
541
542                                 flname, flline,
543                                 ep->e_hostname, ep->e_path);
544         if (had_subtree_opt_ptr)
545                 *had_subtree_opt_ptr = had_subtree_opt;
546
547         return 1;
548 }
549
550 static int
551 parsesquash(char *list, int **idp, int *lenp, char **ep)
552 {
553         char    *cp = list;
554         int     id0, id1;
555         int     len = *lenp;
556         int     *id = *idp;
557
558         if (**ep)
559             *--(*ep) = ',';
560
561         do {
562                 id0 = parsenum(&cp);
563                 if (*cp == '-') {
564                         cp++;
565                         id1 = parsenum(&cp);
566                 } else {
567                         id1 = id0;
568                 }
569                 if (id0 == -1 || id1 == -1) {
570                         syntaxerr("uid/gid -1 not permitted");
571                         return -1;
572                 }
573                 if ((len % 8) == 0)
574                         id = (int *) xrealloc(id, (len + 8) * sizeof(*id));
575                 id[len++] = id0;
576                 id[len++] = id1;
577                 if (!*cp || *cp == ')' || (*cp == ',' && !isdigit(cp[1])))
578                         break;
579                 if (*cp != ',') {
580                         syntaxerr("bad uid/gid list");
581                         return -1;
582                 }
583                 cp++;
584         } while(1);
585
586         if (**ep == ',') (*ep)++;
587
588         *lenp = len;
589         *idp = id;
590         return 1;
591 }
592
593 static void
594 freesquash(void)
595 {
596         if (squids) {
597                 xfree (squids);
598                 squids = NULL;
599                 nsquids = 0;
600         }
601         if (sqgids) {
602                 xfree (sqgids);
603                 sqgids = NULL;
604                 nsqgids = 0;
605         }
606 }
607
608 static int
609 parsenum(char **cpp)
610 {
611         char    *cp = *cpp, c;
612         int     num = 0;
613
614         if (**cpp == '-')
615                 (*cpp)++;
616         while (isdigit(**cpp))
617                 (*cpp)++;
618         c = **cpp; **cpp = '\0'; num = atoi(cp); **cpp = c;
619         return num;
620 }
621
622 static int
623 getpath(char *path, int len)
624 {
625         xskip(efp, " \t\n");
626         return xgettok(efp, 0, path, len);
627 }
628
629 static int
630 getexport(char *exp, int len)
631 {
632         int     ok;
633
634         xskip(efp, " \t");
635         if ((ok = xgettok(efp, 0, exp, len)) < 0)
636                 xlog(L_ERROR, "%s:%d: syntax error",
637                         efname?"command line":efname, efp->x_line);
638         return ok;
639 }
640
641 static void
642 syntaxerr(char *msg)
643 {
644         xlog(L_ERROR, "%s:%d: syntax error: %s",
645                         efname, efp?efp->x_line:0, msg);
646 }
647