dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / fs.d / autofs / auto_subr.c
blob9433e398d6b855ce266c669d54110fe95a541588
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
26 #include <ctype.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <locale.h>
31 #include <syslog.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <stdarg.h>
35 #include <dirent.h>
36 #include <limits.h>
37 #include <thread.h>
38 #include <sys/param.h>
39 #include <sys/time.h>
40 #include <sys/vfs.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <sys/mnttab.h>
44 #include <sys/mntent.h>
45 #include <sys/mount.h>
46 #include <sys/signal.h>
47 #include <sys/utsname.h>
48 #include <sys/systeminfo.h>
49 #include <sys/tiuser.h>
50 #include <sys/utsname.h>
51 #include <rpc/rpc.h>
52 #include <rpcsvc/nfs_prot.h>
53 #include <rpcsvc/daemon_utils.h>
54 #include <assert.h>
55 #include "automount.h"
56 #include <deflt.h>
57 #include <zone.h>
58 #include <priv.h>
59 #include <fcntl.h>
60 #include <libshare.h>
61 #include <libscf.h>
62 #include "smfcfg.h"
64 static char *check_hier(char *);
65 static int arch(char *, size_t, bool_t);
66 static int cpu(char *, size_t);
67 static int natisa(char *, size_t);
68 static int platform(char *, size_t);
70 struct mntlist *current_mounts;
72 static bool_t nodirect_map = FALSE;
74 void
75 dirinit(char *mntpnt, char *map, char *opts, int direct, char **stack,
76 char ***stkptr)
78 struct autodir *dir;
79 char *p;
81 if (strcmp(map, "-null") == 0) {
82 if (strcmp(mntpnt, "/-") == 0)
83 nodirect_map = TRUE;
84 goto enter;
87 p = mntpnt + (strlen(mntpnt) - 1);
88 if (*p == '/')
89 *p = '\0'; /* trim trailing / */
90 if (*mntpnt != '/') {
91 pr_msg("dir %s must start with '/'", mntpnt);
92 return;
94 if (p = check_hier(mntpnt)) {
95 pr_msg("hierarchical mountpoint: %s and %s",
96 p, mntpnt);
97 return;
101 * If it's a direct map then call dirinit
102 * for every map entry.
104 if ((strcmp(mntpnt, "/-") == 0) && !(nodirect_map)) {
105 (void) loaddirect_map(map, map, opts, stack, stkptr);
106 return;
109 enter:
110 dir = (struct autodir *)malloc(sizeof (*dir));
111 if (dir == NULL)
112 goto alloc_failed;
113 dir->dir_name = strdup(mntpnt);
114 if (dir->dir_name == NULL)
115 goto alloc_failed;
116 dir->dir_map = strdup(map);
117 if (dir->dir_map == NULL)
118 goto alloc_failed;
119 dir->dir_opts = strdup(opts);
120 if (dir->dir_opts == NULL)
121 goto alloc_failed;
122 dir->dir_direct = direct;
123 dir->dir_remount = 0;
124 dir->dir_next = NULL;
127 * Append to dir chain
129 if (dir_head == NULL)
130 dir_head = dir;
131 else
132 dir_tail->dir_next = dir;
134 dir->dir_prev = dir_tail;
135 dir_tail = dir;
137 return;
139 alloc_failed:
140 if (dir != NULL) {
141 free(dir->dir_opts);
142 free(dir->dir_map);
143 free(dir->dir_name);
144 free(dir);
146 pr_msg("dirinit: memory allocation failed");
150 * Check whether the mount point is a
151 * subdirectory or a parent directory
152 * of any previously mounted automount
153 * mount point.
155 static char *
156 check_hier(mntpnt)
157 char *mntpnt;
159 register struct autodir *dir;
160 register char *p, *q;
162 for (dir = dir_head; dir; dir = dir->dir_next) {
163 p = dir->dir_name;
164 q = mntpnt;
165 for (; *p == *q; p++, q++)
166 if (*p == '\0')
167 break;
168 if (*p == '/' && *q == '\0')
169 return (dir->dir_name);
170 if (*p == '\0' && *q == '/')
171 return (dir->dir_name);
172 if (*p == '\0' && *q == '\0')
173 return (NULL);
175 return (NULL); /* it's not a subdir or parent */
179 * Gets the next token from the string "p" and copies it into "w". The "wq" is
180 * a quote vector for "w" and is derived from "pq", which is a quote vector for
181 * "p". Delim is the character to be used as a delimiter for the scan. A space
182 * means "whitespace". The call to getword must provide buffers w and wq of size
183 * at least wordsz. getword() will pass strings of maximum length (wordsz-1),
184 * since it needs to null terminate the string.
185 * Returns 0 on ok and -1 on error.
188 getword(char *w, char *wq, char **p, char **pq, char delim, int wordsz)
190 char *tmp = w;
191 char *tmpq = wq;
192 int count = wordsz;
194 if (wordsz <= 0) {
195 if (verbose)
196 syslog(LOG_ERR,
197 "getword: input word size %d must be > 0", wordsz);
198 return (-1);
201 while ((delim == ' ' ? isspace(**p) : **p == delim) && **pq == ' ')
202 (*p)++, (*pq)++;
204 while (**p &&
205 !((delim == ' ' ? isspace(**p) : **p == delim) &&
206 **pq == ' ')) {
207 if (--count <= 0) {
208 *tmp = '\0';
209 *tmpq = '\0';
210 syslog(LOG_ERR,
211 "maximum word length (%d) exceeded", wordsz);
212 return (-1);
214 *w++ = *(*p)++;
215 *wq++ = *(*pq)++;
217 *w = '\0';
218 *wq = '\0';
220 return (0);
224 * get_line attempts to get a line from the map, upto LINESZ. A line in
225 * the map is a concatenation of lines if the continuation symbol '\'
226 * is used at the end of the line. Returns line on success, a NULL on
227 * EOF, and an empty string on lines > linesz.
229 char *
230 get_line(FILE *fp, char *map, char *line, int linesz)
232 register char *p = line;
233 register int len;
234 int excess = 0;
236 *p = '\0';
238 for (;;) {
239 if (fgets(p, linesz - (p-line), fp) == NULL) {
240 return (*line ? line : NULL); /* EOF */
243 len = strlen(line);
244 if (len <= 0) {
245 p = line;
246 continue;
248 p = &line[len - 1];
251 * Is input line too long?
253 if (*p != '\n') {
254 excess = 1;
256 * Perhaps last char read was '\'. Reinsert it
257 * into the stream to ease the parsing when we
258 * read the rest of the line to discard.
260 (void) ungetc(*p, fp);
261 break;
263 trim:
264 /* trim trailing white space */
265 while (p >= line && isspace(*(uchar_t *)p))
266 *p-- = '\0';
267 if (p < line) { /* empty line */
268 p = line;
269 continue;
272 if (*p == '\\') { /* continuation */
273 *p = '\0';
274 continue;
278 * Ignore comments. Comments start with '#'
279 * which must be preceded by a whitespace, unless
280 * if '#' is the first character in the line.
282 p = line;
283 while (p = strchr(p, '#')) {
284 if (p == line || isspace(*(p-1))) {
285 *p-- = '\0';
286 goto trim;
288 p++;
290 break;
292 if (excess) {
293 int c;
296 * discard rest of line and return an empty string.
297 * done to set the stream to the correct place when
298 * we are done with this line.
300 while ((c = getc(fp)) != EOF) {
301 *p = c;
302 if (*p == '\n') /* end of the long line */
303 break;
304 else if (*p == '\\') { /* continuation */
305 if (getc(fp) == EOF) /* ignore next char */
306 break;
309 syslog(LOG_ERR,
310 "map %s: line too long (max %d chars)",
311 map, linesz-1);
312 *line = '\0';
315 return (line);
319 * Gets the retry=n entry from opts.
320 * Returns 0 if retry=n is not present in option string,
321 * retry=n is invalid, or when option string is NULL.
324 get_retry(char *opts)
326 int retry = 0;
327 char buf[MAXOPTSLEN];
328 char *p, *pb, *lasts;
330 if (opts == NULL)
331 return (retry);
333 (void) strcpy(buf, opts);
334 pb = buf;
335 while (p = (char *)strtok_r(pb, ",", &lasts)) {
336 pb = NULL;
337 if (strncmp(p, "retry=", 6) == 0)
338 retry = atoi(p+6);
340 return (retry > 0 ? retry : 0);
344 * Returns zero if "opt" is found in mnt->mnt_opts, setting
345 * *sval to whatever follows the equal sign after "opt".
346 * str_opt allocates a string long enough to store the value of
347 * "opt" plus a terminating null character and returns it as *sval.
348 * It is the responsability of the caller to deallocate *sval.
349 * *sval will be equal to NULL upon return if either "opt=" is not found,
350 * or "opt=" has no value associated with it.
352 * stropt will return -1 on error.
355 str_opt(struct mnttab *mnt, char *opt, char **sval)
357 char *str, *comma;
360 * is "opt" in the options field?
362 if (str = hasmntopt(mnt, opt)) {
363 str += strlen(opt);
364 if (*str++ != '=' ||
365 (*str == ',' || *str == '\0')) {
366 syslog(LOG_ERR, "Bad option field");
367 return (-1);
369 comma = strchr(str, ',');
370 if (comma != NULL)
371 *comma = '\0';
372 *sval = strdup(str);
373 if (comma != NULL)
374 *comma = ',';
375 if (*sval == NULL)
376 return (-1);
377 } else
378 *sval = NULL;
380 return (0);
384 * Performs text expansions in the string "pline".
385 * "plineq" is the quote vector for "pline".
386 * An identifier prefixed by "$" is replaced by the
387 * corresponding environment variable string. A "&"
388 * is replaced by the key string for the map entry.
390 * This routine will return an error (non-zero) if *size* would be
391 * exceeded after expansion, indicating that the macro_expand failed.
392 * This is to prevent writing past the end of pline and plineq.
393 * Both pline and plineq are left untouched in such error case.
396 macro_expand(key, pline, plineq, size)
397 char *key, *pline, *plineq;
398 int size;
400 register char *p, *q;
401 register char *bp, *bq;
402 register char *s;
403 char buffp[LINESZ], buffq[LINESZ];
404 char namebuf[64], *pn;
405 int expand = 0;
406 struct utsname name;
407 char procbuf[SYS_NMLN];
408 char isaname[64];
410 p = pline; q = plineq;
411 bp = buffp; bq = buffq;
413 while (*p) {
414 if (*p == '&' && *q == ' ') { /* insert key */
416 * make sure we don't overflow buffer
418 if ((int)((bp - buffp) + strlen(key)) < size) {
419 for (s = key; *s; s++) {
420 *bp++ = *s;
421 *bq++ = ' ';
423 expand++;
424 p++; q++;
425 continue;
426 } else {
428 * line too long...
430 return (1);
434 if (*p == '$' && *q == ' ') { /* insert env var */
435 p++; q++;
436 pn = namebuf;
437 if (*p == '{') {
438 p++; q++;
439 while (*p && *p != '}') {
440 *pn++ = *p++;
441 q++;
443 if (*p) {
444 p++; q++;
446 } else {
447 while (*p && (*p == '_' || isalnum(*p))) {
448 *pn++ = *p++;
449 q++;
452 *pn = '\0';
454 s = getenv(namebuf);
455 if (!s) {
456 /* not found in env */
457 if (strcmp(namebuf, "ARCH") == 0) {
458 if (arch(procbuf, sizeof (procbuf),
459 FALSE))
460 s = procbuf;
461 } else if (strcmp(namebuf, "CPU") == 0) {
462 if (cpu(procbuf, sizeof (procbuf)))
463 s = procbuf;
464 } else if (strcmp(namebuf, "HOST") == 0) {
465 (void) uname(&name);
466 s = name.nodename;
467 } else if (strcmp(namebuf, "KARCH") == 0) {
468 if (arch(procbuf, sizeof (procbuf),
469 TRUE))
470 s = procbuf;
471 } else if (strcmp(namebuf, "OSREL") == 0) {
472 (void) uname(&name);
473 s = name.release;
474 } else if (strcmp(namebuf, "OSNAME") == 0) {
475 (void) uname(&name);
476 s = name.sysname;
477 } else if (strcmp(namebuf, "OSVERS") == 0) {
478 (void) uname(&name);
479 s = name.version;
480 } else if (strcmp(namebuf, "NATISA") == 0) {
481 if (natisa(isaname, sizeof (isaname)))
482 s = isaname;
483 } else if (strcmp(namebuf, "PLATFORM") == 0) {
484 if (platform(procbuf, sizeof (procbuf)))
485 s = procbuf;
489 if (s) {
490 if ((int)((bp - buffp) + strlen(s)) < size) {
491 while (*s) {
492 *bp++ = *s++;
493 *bq++ = ' ';
495 } else {
497 * line too long...
499 return (1);
502 expand++;
503 continue;
506 * Since buffp needs to be null terminated, we need to
507 * check that there's still room in the buffer to
508 * place at least two more characters, *p and the
509 * terminating null.
511 if (bp - buffp == size - 1) {
513 * There was not enough room for at least two more
514 * characters, return with an error.
516 return (1);
519 * The total number of characters so far better be less
520 * than the size of buffer passed in.
522 *bp++ = *p++;
523 *bq++ = *q++;
526 if (!expand)
527 return (0);
528 *bp = '\0';
529 *bq = '\0';
531 * We know buffp/buffq will fit in pline/plineq since we
532 * processed at most size characters.
534 (void) strcpy(pline, buffp);
535 (void) strcpy(plineq, buffq);
537 return (0);
541 * Removes backslashes, quotes and brackets from the string "str"
542 * and returns the quoting information in "qbuf". Character is
543 * considered escaped when it is
545 * preceded with '\' e.g. \a
546 * within quotes e.g. "string"
547 * a ':' in brackets e.g. [an:ip:6::ad::d:re:s:s]
549 * original str: 'the "brown" f\ox said: [fe80::20a:e4ff:fe35:8b0d]'
550 * unquoted str: 'the brown fox said: [fe80::20a:e4ff:fe35:8b0d]'
551 * and the qbuf: ' ^^^^^ ^ ^^ ^ ^ ^ '
553 void
554 unquote(str, qbuf)
555 char *str, *qbuf;
557 register int escaped, inquote, inbracket, quoted;
558 register char *ip, *bp, *qp;
559 char buf[LINESZ];
561 escaped = inquote = inbracket = quoted = 0;
563 for (ip = str, bp = buf, qp = qbuf; *ip; ip++) {
564 if (!escaped) {
565 if (*ip == '\\') {
566 escaped = 1;
567 quoted++;
568 continue;
569 } else
570 if (*ip == '"') {
571 inquote = !inquote;
572 quoted++;
573 continue;
574 } else
575 if (*ip == '[') {
576 inbracket++;
577 quoted++;
578 } else
579 if (*ip == ']') {
580 if (inbracket > 0) inbracket--;
584 *bp++ = *ip;
585 *qp++ = (inquote || escaped) ? '^'
586 : ((inbracket && (*ip == ':')) ? '^' : ' ');
587 escaped = 0;
590 *bp = '\0';
591 *qp = '\0';
593 if (quoted)
594 (void) strcpy(str, buf);
598 * If str is enclosed in [brackets], trim them off.
600 void
601 unbracket(s)
602 char **s;
604 char *b = *s + strlen(*s) - 1;
606 if (*b == ']')
607 *b = '\0';
608 if (**s == '[')
609 (*s)++;
613 * Removes trailing spaces from string "s".
615 void
616 trim(s)
617 char *s;
619 char *p = &s[strlen(s) - 1];
621 while (p >= s && isspace(*(uchar_t *)p))
622 *p-- = '\0';
626 * try to allocate memory using malloc, if malloc fails, then flush the
627 * rddir caches, and retry. If the second allocation after the readdir
628 * caches have been flushed fails too, then return NULL to indicate
629 * memory could not be allocated.
631 char *
632 auto_rddir_malloc(unsigned nbytes)
634 char *p;
635 int again = 0;
637 if ((p = malloc(nbytes)) == NULL) {
639 * No memory, free rddir caches and try again
641 mutex_lock(&cleanup_lock);
642 cond_signal(&cleanup_start_cv);
643 if (cond_wait(&cleanup_done_cv, &cleanup_lock)) {
644 mutex_unlock(&cleanup_lock);
645 syslog(LOG_ERR, "auto_rddir_malloc interrupted\n");
646 } else {
647 mutex_unlock(&cleanup_lock);
648 again = 1;
652 if (again)
653 p = malloc(nbytes);
655 return (p);
659 * try to strdup a string, if it fails, then flush the rddir caches,
660 * and retry. If the second strdup fails, return NULL to indicate failure.
662 char *
663 auto_rddir_strdup(const char *s1)
665 char *s2;
666 int again = 0;
668 if ((s2 = strdup(s1)) == NULL) {
670 * No memory, free rddir caches and try again
672 mutex_lock(&cleanup_lock);
673 cond_signal(&cleanup_start_cv);
674 if (cond_wait(&cleanup_done_cv, &cleanup_lock)) {
675 mutex_unlock(&cleanup_lock);
676 syslog(LOG_ERR, "auto_rddir_strdup interrupted\n");
677 } else {
678 mutex_unlock(&cleanup_lock);
679 again = 1;
683 if (again)
684 s2 = strdup(s1);
686 return (s2);
690 * Returns a pointer to the entry corresponding to 'name' if found,
691 * otherwise it returns NULL.
693 struct dir_entry *
694 btree_lookup(struct dir_entry *head, char *name)
696 register struct dir_entry *p;
697 register int direction;
699 for (p = head; p != NULL; ) {
700 direction = strcmp(name, p->name);
701 if (direction == 0)
702 return (p);
703 if (direction > 0)
704 p = p->right;
705 else p = p->left;
707 return (NULL);
711 * Add entry to binary tree
712 * Duplicate entries are not added
714 void
715 btree_enter(struct dir_entry **head, struct dir_entry *ent)
717 register struct dir_entry *p, *prev = NULL;
718 register int direction;
720 ent->right = ent->left = NULL;
721 if (*head == NULL) {
722 *head = ent;
723 return;
726 for (p = *head; p != NULL; ) {
727 prev = p;
728 direction = strcmp(ent->name, p->name);
729 if (direction == 0) {
731 * entry already in btree
733 return;
735 if (direction > 0)
736 p = p->right;
737 else p = p->left;
739 assert(prev != NULL);
740 if (direction > 0)
741 prev->right = ent;
742 else prev->left = ent;
746 * If entry doesn't exist already, add it to the linear list
747 * after '*last' and to the binary tree list.
748 * If '*last == NULL' then the list is walked till the end.
749 * *last is always set to the new element after successful completion.
750 * if entry already exists '*last' is only updated if not previously
751 * provided.
754 add_dir_entry(char *name, struct dir_entry **list, struct dir_entry **last)
756 struct dir_entry *e, *l;
758 if ((*list != NULL) && (*last == NULL)) {
760 * walk the list to find last element
762 for (l = *list; l != NULL; l = l->next)
763 *last = l;
766 if (btree_lookup(*list, name) == NULL) {
768 * not a duplicate, add it to list
770 /* LINTED pointer alignment */
771 e = (struct dir_entry *)
772 auto_rddir_malloc(sizeof (struct dir_entry));
773 if (e == NULL)
774 return (ENOMEM);
775 (void) memset((char *)e, 0, sizeof (*e));
776 e->name = auto_rddir_strdup(name);
777 if (e->name == NULL) {
778 free(e);
779 return (ENOMEM);
781 e->next = NULL;
782 if (*list == NULL) {
784 * list is empty
786 *list = *last = e;
787 } else {
789 * append to end of list
791 assert(*last != NULL);
792 (*last)->next = e;
793 *last = e;
796 * add to binary tree
798 btree_enter(list, e);
800 return (0);
804 * Print trace output.
805 * Like fprintf(stderr, fmt, ...) except that if "id" is nonzero, the output
806 * is preceeded by the ID of the calling thread.
808 #define FMT_BUFSIZ 1024
810 void
811 trace_prt(int id, char *fmt, ...)
813 va_list args;
815 char buf[FMT_BUFSIZ];
817 if (id) {
818 (void) sprintf(buf, "t%u\t%s", thr_self(), fmt);
819 fmt = buf;
821 va_start(args, fmt);
822 (void) vfprintf(stderr, fmt, args);
823 va_end(args);
827 * Extract the isalist(5) for userland from the kernel.
829 static char *
830 isalist(void)
832 char *buf;
833 size_t bufsize = BUFSIZ; /* wild guess */
834 long ret;
836 buf = malloc(bufsize);
837 do {
838 ret = sysinfo(SI_ISALIST, buf, bufsize);
839 if (ret == -1l)
840 return (NULL);
841 if (ret > bufsize) {
842 bufsize = ret;
843 buf = realloc(buf, bufsize);
844 } else
845 break;
846 } while (buf != NULL);
848 return (buf);
852 * Classify isa's as to bitness of the corresponding ABIs.
853 * isa's which have no "official" system ABI are returned
854 * unrecognised i.e. zero bits.
856 static int
857 bitness(char *isaname)
859 if (strcmp(isaname, "sparc") == 0 ||
860 strcmp(isaname, "i386") == 0)
861 return (32);
863 if (strcmp(isaname, "sparcv9") == 0 ||
864 strcmp(isaname, "amd64") == 0)
865 return (64);
867 return (0);
871 * Determine the application architecture (derived from uname -m) to expand
872 * the $ARCH and $KARCH macros.
874 * We need to substitute "sun4" for "sun4u", "sun4v", ... for backward
875 * compatibility. When kflag is set (like arch -k), the unmodifed value is
876 * returned instead.
878 static int
879 arch(char *buf, size_t bufsize, bool_t karch)
881 long ret;
883 ret = sysinfo(SI_MACHINE, buf, bufsize);
884 if (ret == -1L)
885 return (0);
886 if (!karch && strncmp(buf, "sun4", 4) == 0)
887 (void) strlcpy(buf, "sun4", bufsize);
888 return (1);
892 * Determine the basic ISA (uname -p) to expand the $CPU macro.
894 static int
895 cpu(char *buf, size_t bufsize)
897 long ret;
899 ret = sysinfo(SI_ARCHITECTURE, buf, bufsize);
900 if (ret == -1L)
901 return (0);
902 else
903 return (1);
907 * Find the left-most element in the isalist that matches our idea of a
908 * system ABI.
910 * On machines with only one ABI, this is usually the same as uname -p.
912 static int
913 natisa(char *buf, size_t bufsize)
915 int bits;
916 char *isa, *list;
917 char *lasts;
919 if ((list = isalist()) == NULL)
920 return (0);
922 for (isa = strtok_r(list, " ", &lasts);
923 isa; isa = strtok_r(0, " ", &lasts))
924 if ((bits = bitness(isa)) != 0)
925 break; /* ignore "extension" architectures */
927 if (isa == 0 || bits == 0) {
928 free(list);
929 return (0); /* can't figure it out :( */
932 (void) strncpy(buf, isa, bufsize);
933 free(list);
935 return (1);
939 * Determine the platform (uname -i) to expand the $PLATFORM macro.
941 static int
942 platform(char *buf, size_t bufsize)
944 long ret;
946 ret = sysinfo(SI_PLATFORM, buf, bufsize);
947 if (ret == -1L)
948 return (0);
949 else
950 return (1);
954 * Set environment variables
956 void
957 put_automountd_env(void)
959 char defval[PATH_MAX], *p, *a, *c;
960 int ret = 0, bufsz = PATH_MAX;
962 ret = autofs_smf_get_prop("environment", defval, DEFAULT_INSTANCE,
963 SCF_TYPE_ASTRING, AUTOMOUNTD, &bufsz);
964 if (ret == SA_OK) {
965 a = c = defval;
966 if (*a == '\0')
967 return;
969 * Environment variables can have more than one value
970 * seperated by a comma and there can be multiple
971 * environment variables. * a=b\,c,d=e. For multiple
972 * valued environment variable, values are seperated
973 * with an escape character.
975 while ((p = strchr(c, ',')) != NULL) {
976 if (*(p - 1) == '\\') {
977 c = p + 1;
978 continue;
980 *p = '\0';
981 if ((c = strchr(a, '=')) != NULL)
982 putenv(strdup(a));
983 a = c = p + 1;
985 if (*a != '\0') {
986 if ((c = strchr(a, '=')) != NULL)
987 putenv(strdup(a));