Merge branch 'vendor/EXPAT'
[dragonfly.git] / contrib / amd / amd / autil.c
1 /*
2  * Copyright (c) 1997-1999 Erez Zadok
3  * Copyright (c) 1990 Jan-Simon Pendry
4  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5  * Copyright (c) 1990 The Regents of the University of California.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Jan-Simon Pendry at Imperial College, London.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgment:
21  *      This product includes software developed by the University of
22  *      California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  *
39  *      %W% (Berkeley) %G%
40  *
41  * $Id: autil.c,v 1.3 1999/01/10 21:53:44 ezk Exp $
42  *
43  */
44
45 /*
46  * utilities specified to amd, taken out of the older amd/util.c.
47  */
48
49 #ifdef HAVE_CONFIG_H
50 # include <config.h>
51 #endif /* HAVE_CONFIG_H */
52 #include <am_defs.h>
53 #include <amd.h>
54
55 int NumChild = 0;               /* number of children of primary amd */
56 static char invalid_keys[] = "\"'!;@ \t\n";
57
58 #ifdef HAVE_TRANSPORT_TYPE_TLI
59 # define PARENT_USLEEP_TIME     100000 /* 0.1 seconds */
60 #endif /* HAVE_TRANSPORT_TYPE_TLI */
61
62
63 char *
64 strealloc(char *p, char *s)
65 {
66   int len = strlen(s) + 1;
67
68   p = (char *) xrealloc((voidp) p, len);
69
70   strcpy(p, s);
71 #ifdef DEBUG_MEM
72   malloc_verify();
73 #endif /* DEBUG_MEM */
74   return p;
75 }
76
77
78 char **
79 strsplit(char *s, int ch, int qc)
80 {
81   char **ivec;
82   int ic = 0;
83   int done = 0;
84
85   ivec = (char **) xmalloc((ic + 1) * sizeof(char *));
86
87   while (!done) {
88     char *v;
89
90     /*
91      * skip to split char
92      */
93     while (*s && (ch == ' ' ? (isascii(*s) && isspace((int)*s)) : *s == ch))
94       *s++ = '\0';
95
96     /*
97      * End of string?
98      */
99     if (!*s)
100       break;
101
102     /*
103      * remember start of string
104      */
105     v = s;
106
107     /*
108      * skip to split char
109      */
110     while (*s && !(ch == ' ' ? (isascii(*s) && isspace((int)*s)) : *s == ch)) {
111       if (*s++ == qc) {
112         /*
113          * Skip past string.
114          */
115         s++;
116         while (*s && *s != qc)
117           s++;
118         if (*s == qc)
119           s++;
120       }
121     }
122
123     if (!*s)
124       done = 1;
125     *s++ = '\0';
126
127     /*
128      * save string in new ivec slot
129      */
130     ivec[ic++] = v;
131     ivec = (char **) xrealloc((voidp) ivec, (ic + 1) * sizeof(char *));
132 #ifdef DEBUG
133     amuDebug(D_STR)
134       plog(XLOG_DEBUG, "strsplit saved \"%s\"", v);
135 #endif /* DEBUG */
136   }
137
138 #ifdef DEBUG
139   amuDebug(D_STR)
140     plog(XLOG_DEBUG, "strsplit saved a total of %d strings", ic);
141 #endif /* DEBUG */
142
143   ivec[ic] = 0;
144
145   return ivec;
146 }
147
148
149 /*
150  * Strip off the trailing part of a domain
151  * to produce a short-form domain relative
152  * to the local host domain.
153  * Note that this has no effect if the domain
154  * names do not have the same number of
155  * components.  If that restriction proves
156  * to be a problem then the loop needs recoding
157  * to skip from right to left and do partial
158  * matches along the way -- ie more expensive.
159  */
160 static void
161 domain_strip(char *otherdom, char *localdom)
162 {
163   char *p1, *p2;
164
165   if ((p1 = strchr(otherdom, '.')) &&
166       (p2 = strchr(localdom, '.')) &&
167       STREQ(p1 + 1, p2 + 1))
168     *p1 = '\0';
169 }
170
171
172 /*
173  * Normalize a host name
174  */
175 void
176 host_normalize(char **chp)
177 {
178   /*
179    * Normalize hosts is used to resolve host name aliases
180    * and replace them with the standard-form name.
181    * Invoked with "-n" command line option.
182    */
183   if (gopt.flags & CFM_NORMALIZE_HOSTNAMES) {
184     struct hostent *hp;
185     clock_valid = 0;
186     hp = gethostbyname(*chp);
187     if (hp && hp->h_addrtype == AF_INET) {
188 #ifdef DEBUG
189       dlog("Hostname %s normalized to %s", *chp, hp->h_name);
190 #endif /* DEBUG */
191       *chp = strealloc(*chp, (char *) hp->h_name);
192     }
193   }
194   domain_strip(*chp, hostd);
195 }
196
197
198
199 /*
200  * Keys are not allowed to contain " ' ! or ; to avoid
201  * problems with macro expansions.
202  */
203 int
204 valid_key(char *key)
205 {
206   while (*key)
207     if (strchr(invalid_keys, *key++))
208       return FALSE;
209   return TRUE;
210 }
211
212
213 void
214 forcibly_timeout_mp(am_node *mp)
215 {
216   mntfs *mf = mp->am_mnt;
217   /*
218    * Arrange to timeout this node
219    */
220   if (mf && ((mp->am_flags & AMF_ROOT) ||
221              (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)))) {
222     if (!(mf->mf_flags & MFF_UNMOUNTING))
223       plog(XLOG_WARNING, "ignoring timeout request for active node %s", mp->am_path);
224   } else {
225     plog(XLOG_INFO, "\"%s\" forcibly timed out", mp->am_path);
226     mp->am_flags &= ~AMF_NOTIMEOUT;
227     mp->am_ttl = clocktime();
228     reschedule_timeout_mp();
229   }
230 }
231
232
233 void
234 mf_mounted(mntfs *mf)
235 {
236   int quoted;
237   int wasmounted = mf->mf_flags & MFF_MOUNTED;
238
239   if (!wasmounted) {
240     /*
241      * If this is a freshly mounted
242      * filesystem then update the
243      * mntfs structure...
244      */
245     mf->mf_flags |= MFF_MOUNTED;
246     mf->mf_error = 0;
247
248     /*
249      * Do mounted callback
250      */
251     if (mf->mf_ops->mounted) {
252       (*mf->mf_ops->mounted) (mf);
253     }
254     mf->mf_fo = 0;
255   }
256
257   /*
258    * Log message
259    */
260   quoted = strchr(mf->mf_info, ' ') != 0;
261   plog(XLOG_INFO, "%s%s%s %s fstype %s on %s",
262        quoted ? "\"" : "",
263        mf->mf_info,
264        quoted ? "\"" : "",
265        wasmounted ? "referenced" : "mounted",
266        mf->mf_ops->fs_type, mf->mf_mount);
267 }
268
269
270 void
271 am_mounted(am_node *mp)
272 {
273   mntfs *mf = mp->am_mnt;
274
275   mf_mounted(mf);
276
277   /*
278    * Patch up path for direct mounts
279    */
280   if (mp->am_parent && mp->am_parent->am_mnt->mf_ops == &amfs_direct_ops)
281     mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", ".");
282
283   /*
284    * Check whether this mount should be cached permanently
285    */
286   if (mf->mf_ops->fs_flags & FS_NOTIMEOUT) {
287     mp->am_flags |= AMF_NOTIMEOUT;
288   } else if (mf->mf_mount[1] == '\0' && mf->mf_mount[0] == '/') {
289     mp->am_flags |= AMF_NOTIMEOUT;
290   } else {
291     mntent_t mnt;
292     if (mf->mf_mopts) {
293       mnt.mnt_opts = mf->mf_mopts;
294       if (hasmntopt(&mnt, "nounmount"))
295         mp->am_flags |= AMF_NOTIMEOUT;
296       if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0)
297         mp->am_timeo = gopt.am_timeo;
298     }
299   }
300
301   /*
302    * If this node is a symlink then
303    * compute the length of the returned string.
304    */
305   if (mp->am_fattr.na_type == NFLNK)
306     mp->am_fattr.na_size = strlen(mp->am_link ? mp->am_link : mp->am_mnt->mf_mount);
307
308   /*
309    * Record mount time
310    */
311   mp->am_fattr.na_mtime.nt_seconds = mp->am_stats.s_mtime = clocktime();
312   new_ttl(mp);
313
314   /*
315    * Update mtime of parent node
316    */
317   if (mp->am_parent && mp->am_parent->am_mnt)
318     mp->am_parent->am_fattr.na_mtime.nt_seconds = mp->am_stats.s_mtime;
319
320   /*
321    * Now, if we can, do a reply to our NFS client here
322    * to speed things up.
323    */
324   quick_reply(mp, 0);
325
326   /*
327    * Update stats
328    */
329   amd_stats.d_mok++;
330 }
331
332
333 int
334 mount_node(am_node *mp)
335 {
336   mntfs *mf = mp->am_mnt;
337   int error = 0;
338
339   mf->mf_flags |= MFF_MOUNTING;
340   error = (*mf->mf_ops->mount_fs) (mp);
341
342   mf = mp->am_mnt;
343   if (error >= 0)
344     mf->mf_flags &= ~MFF_MOUNTING;
345   if (!error && !(mf->mf_ops->fs_flags & FS_MBACKGROUND)) {
346     /* ...but see ifs_mount */
347     am_mounted(mp);
348   }
349
350   return error;
351 }
352
353
354 void
355 am_unmounted(am_node *mp)
356 {
357   mntfs *mf = mp->am_mnt;
358
359   if (!foreground)              /* firewall - should never happen */
360     return;
361
362   /*
363    * Do unmounted callback
364    */
365   if (mf->mf_ops->umounted)
366     (*mf->mf_ops->umounted) (mp);
367
368   /*
369    * Update mtime of parent node
370    */
371   if (mp->am_parent && mp->am_parent->am_mnt)
372     mp->am_parent->am_fattr.na_mtime.nt_seconds = clocktime();
373
374   free_map(mp);
375 }
376
377
378 /*
379  * Fork the automounter
380  *
381  * TODO: Need a better strategy for handling errors
382  */
383 static int
384 dofork(void)
385 {
386   int pid;
387
388 top:
389   pid = fork();
390
391   if (pid < 0) {                /* fork error, retry in 1 second */
392     sleep(1);
393     goto top;
394   }
395   if (pid == 0) {               /* child process (foreground==false) */
396     am_set_mypid();
397     foreground = 0;
398   } else {                      /* parent process, has one more child */
399     NumChild++;
400   }
401
402   return pid;
403 }
404
405
406 int
407 background(void)
408 {
409   int pid = dofork();
410
411   if (pid == 0) {
412 #ifdef DEBUG
413     dlog("backgrounded");
414 #endif /* DEBUG */
415     foreground = 0;
416   }
417   return pid;
418 }