1 /*
2 * Copyright (c) 1990 Jan-Simon Pendry
3 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
4 * Copyright (c) 1990, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Jan-Simon Pendry at Imperial College, London.
9 *
10 * %sccs.include.redist.c%
11 *
12 * @(#)util.c 8.1 (Berkeley) 06/06/93
13 *
14 * $Id: util.c,v 5.2.2.2 1992/03/07 17:52:06 jsp Exp $
15 *
16 */
17
18 /*
19 * Utils
20 */
21
22 #include "am.h"
23 #include <ctype.h>
24 #include <sys/stat.h>
25 #include <netdb.h>
26
27
strnsave(str,len)28 char *strnsave(str, len)
29 Const char *str;
30 int len;
31 {
32 char *sp = (char *) xmalloc(len+1);
33
34 bcopy(str, sp, len);
35 sp[len] = 0;
36
37 return sp;
38 }
39
strdup(s)40 char *strdup(s)
41 Const char *s;
42 {
43 return strnsave(s, strlen(s));
44 }
45
46 /*
47 * Concatenate three strings and store in buffer pointed to
48 * by p, making p large enough to hold the strings
49 */
str3cat(p,s1,s2,s3)50 char *str3cat(p, s1, s2, s3)
51 char *p;
52 char *s1;
53 char *s2;
54 char *s3;
55 {
56 int l1 = strlen(s1);
57 int l2 = strlen(s2);
58 int l3 = strlen(s3);
59 p = (char *) xrealloc(p, l1 + l2 + l3 + 1);
60 bcopy(s1, p, l1);
61 bcopy(s2, p + l1, l2);
62 bcopy(s3, p + l1 + l2, l3 + 1);
63 return p;
64 }
65
strealloc(p,s)66 char *strealloc(p, s)
67 char *p;
68 char *s;
69 {
70 int len = strlen(s) + 1;
71
72 p = (char *) xrealloc((voidp) p, len);
73
74 strcpy(p, s);
75 #ifdef DEBUG_MEM
76 malloc_verify();
77 #endif /* DEBUG_MEM */
78 return p;
79 }
80
81 char **strsplit P((char *s, int ch, int qc));
strsplit(s,ch,qc)82 char **strsplit(s, ch, qc)
83 char *s;
84 int ch;
85 int qc;
86 {
87 char **ivec;
88 int ic = 0;
89 int done = 0;
90
91 ivec = (char **) xmalloc((ic+1)*sizeof(char *));
92
93 while (!done) {
94 char *v;
95 /*
96 * skip to split char
97 */
98 while (*s && (ch == ' ' ? (isascii(*s) && isspace(*s)) : *s == ch))
99 *s++ = '\0';
100
101 /*
102 * End of string?
103 */
104 if (!*s)
105 break;
106
107 /*
108 * remember start of string
109 */
110 v = s;
111
112 /*
113 * skip to split char
114 */
115 while (*s && !(ch == ' ' ? (isascii(*s) && isspace(*s)) : *s == ch)) {
116 if (*s++ == qc) {
117 /*
118 * Skip past string.
119 */
120 s++;
121 while (*s && *s != qc)
122 s++;
123 if (*s == qc)
124 s++;
125 }
126 }
127
128 if (!*s)
129 done = 1;
130 *s++ = '\0';
131
132 /*
133 * save string in new ivec slot
134 */
135 ivec[ic++] = v;
136 ivec = (char **) xrealloc((voidp) ivec, (ic+1)*sizeof(char *));
137 #ifdef DEBUG
138 Debug(D_STR)
139 plog(XLOG_DEBUG, "strsplit saved \"%s\"", v);
140 #endif /* DEBUG */
141 }
142
143 #ifdef DEBUG
144 Debug(D_STR)
145 plog(XLOG_DEBUG, "strsplit saved a total of %d strings", ic);
146 #endif /* DEBUG */
147
148 ivec[ic] = 0;
149
150 return ivec;
151 }
152
153 /*
154 * Strip off the trailing part of a domain
155 * to produce a short-form domain relative
156 * to the local host domain.
157 * Note that this has no effect if the domain
158 * names do not have the same number of
159 * components. If that restriction proves
160 * to be a problem then the loop needs recoding
161 * to skip from right to left and do partial
162 * matches along the way -- ie more expensive.
163 */
164 static void domain_strip P((char *otherdom, char *localdom));
domain_strip(otherdom,localdom)165 static void domain_strip(otherdom, localdom)
166 char *otherdom, *localdom;
167 {
168 #ifdef PARTIAL_DOMAINS
169 char *p1 = otherdom-1;
170 char *p2 = localdom-1;
171
172 do {
173 if (p1 = strchr(p1+1, '.'))
174 if (p2 = strchr(p2+1, '.'))
175 if (strcmp(p1+1, p2+1) == 0) {
176 *p1 = '\0';
177 break;
178 }
179 } while (p1 && p2);
180 #else
181 char *p1, *p2;
182
183 if ((p1 = strchr(otherdom, '.')) &&
184 (p2 = strchr(localdom, '.')) &&
185 (strcmp(p1+1, p2+1) == 0))
186 *p1 = '\0';
187 #endif /* PARTIAL_DOMAINS */
188 }
189
190 /*
191 * Normalize a host name
192 */
193 void host_normalize P((char **chp));
host_normalize(chp)194 void host_normalize(chp)
195 char **chp;
196 {
197 /*
198 * Normalize hosts is used to resolve host name aliases
199 * and replace them with the standard-form name.
200 * Invoked with "-n" command line option.
201 */
202 if (normalize_hosts) {
203 struct hostent *hp;
204 clock_valid = 0;
205 hp = gethostbyname(*chp);
206 if (hp && hp->h_addrtype == AF_INET) {
207 #ifdef DEBUG
208 dlog("Hostname %s normalized to %s", *chp, hp->h_name);
209 #endif /* DEBUG */
210 *chp = strealloc(*chp, hp->h_name);
211 }
212 }
213 domain_strip(*chp, hostd);
214 }
215
216 /*
217 * Make a dotted quad from a 32bit IP address
218 * addr is in network byte order.
219 * sizeof(buf) needs to be at least 16.
220 */
221 char *inet_dquad P((char *buf, unsigned long addr));
inet_dquad(buf,addr)222 char *inet_dquad(buf, addr)
223 char *buf;
224 unsigned long addr;
225 {
226 addr = ntohl(addr);
227 sprintf(buf, "%d.%d.%d.%d",
228 ((addr >> 24) & 0xff),
229 ((addr >> 16) & 0xff),
230 ((addr >> 8) & 0xff),
231 ((addr >> 0) & 0xff));
232 return buf;
233 }
234
235 /*
236 * Keys are not allowed to contain " ' ! or ; to avoid
237 * problems with macro expansions.
238 */
239 static char invalid_keys[] = "\"'!;@ \t\n";
240 int valid_key P((char *key));
valid_key(key)241 int valid_key(key)
242 char *key;
243 {
244 while (*key)
245 if (strchr(invalid_keys, *key++))
246 return FALSE;
247 return TRUE;
248 }
249
250 void going_down P((int rc));
going_down(rc)251 void going_down(rc)
252 int rc;
253 {
254 if (foreground) {
255 if (amd_state != Start) {
256 if (amd_state != Done)
257 return;
258 unregister_amq();
259 }
260 }
261 if (foreground) {
262 plog(XLOG_INFO, "Finishing with status %d", rc);
263 } else {
264 #ifdef DEBUG
265 dlog("background process exiting with status %d", rc);
266 #endif /* DEBUG */
267 }
268
269 exit(rc);
270 }
271
272
273 int bind_resv_port P((int so, u_short *pp));
bind_resv_port(so,pp)274 int bind_resv_port(so, pp)
275 int so;
276 u_short *pp;
277 {
278 struct sockaddr_in sin;
279 int rc;
280 unsigned short port;
281
282 bzero((voidp) &sin, sizeof(sin));
283 sin.sin_family = AF_INET;
284
285 port = IPPORT_RESERVED;
286
287 do {
288 --port;
289 sin.sin_port = htons(port);
290 rc = bind(so, (struct sockaddr *) &sin, sizeof(sin));
291 } while (rc < 0 && port > IPPORT_RESERVED/2);
292
293 if (pp && rc == 0)
294 *pp = port;
295 return rc;
296 }
297
298 void forcibly_timeout_mp P((am_node *mp));
forcibly_timeout_mp(mp)299 void forcibly_timeout_mp(mp)
300 am_node *mp;
301 {
302 mntfs *mf = mp->am_mnt;
303 /*
304 * Arrange to timeout this node
305 */
306 if (mf && ((mp->am_flags & AMF_ROOT) ||
307 (mf->mf_flags & (MFF_MOUNTING|MFF_UNMOUNTING)))) {
308 if (!(mf->mf_flags & MFF_UNMOUNTING))
309 plog(XLOG_WARNING, "ignoring timeout request for active node %s", mp->am_path);
310 } else {
311 plog(XLOG_INFO, "\"%s\" forcibly timed out", mp->am_path);
312 mp->am_flags &= ~AMF_NOTIMEOUT;
313 mp->am_ttl = clocktime();
314 reschedule_timeout_mp();
315 }
316 }
317
318 void mf_mounted P((mntfs *mf));
mf_mounted(mf)319 void mf_mounted(mf)
320 mntfs *mf;
321 {
322 int quoted;
323 int wasmounted = mf->mf_flags & MFF_MOUNTED;
324
325 if (!wasmounted) {
326 /*
327 * If this is a freshly mounted
328 * filesystem then update the
329 * mntfs structure...
330 */
331 mf->mf_flags |= MFF_MOUNTED;
332 mf->mf_error = 0;
333
334 /*
335 * Do mounted callback
336 */
337 if (mf->mf_ops->mounted)
338 (*mf->mf_ops->mounted)(mf);
339
340 mf->mf_fo = 0;
341 }
342
343 /*
344 * Log message
345 */
346 quoted = strchr(mf->mf_info, ' ') != 0;
347 plog(XLOG_INFO, "%s%s%s %s fstype %s on %s",
348 quoted ? "\"" : "",
349 mf->mf_info,
350 quoted ? "\"" : "",
351 wasmounted ? "referenced" : "mounted",
352 mf->mf_ops->fs_type, mf->mf_mount);
353 }
354
355 void am_mounted P((am_node *mp));
am_mounted(mp)356 void am_mounted(mp)
357 am_node *mp;
358 {
359 mntfs *mf = mp->am_mnt;
360
361 mf_mounted(mf);
362
363 /*
364 * Patch up path for direct mounts
365 */
366 if (mp->am_parent && mp->am_parent->am_mnt->mf_ops == &dfs_ops)
367 mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", ".");
368
369 /*
370 * Check whether this mount should be cached permanently
371 */
372 if (mf->mf_ops->fs_flags & FS_NOTIMEOUT) {
373 mp->am_flags |= AMF_NOTIMEOUT;
374 } else if (mf->mf_mount[1] == '\0' && mf->mf_mount[0] == '/') {
375 mp->am_flags |= AMF_NOTIMEOUT;
376 } else {
377 struct mntent mnt;
378 if (mf->mf_mopts) {
379 mnt.mnt_opts = mf->mf_mopts;
380 if (hasmntopt(&mnt, "nounmount"))
381 mp->am_flags |= AMF_NOTIMEOUT;
382 if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0)
383 mp->am_timeo = am_timeo;
384 }
385 }
386
387 /*
388 * If this node is a symlink then
389 * compute the length of the returned string.
390 */
391 if (mp->am_fattr.type == NFLNK)
392 mp->am_fattr.size = strlen(mp->am_link ? mp->am_link : mp->am_mnt->mf_mount);
393
394 /*
395 * Record mount time
396 */
397 mp->am_fattr.mtime.seconds = mp->am_stats.s_mtime = clocktime();
398 new_ttl(mp);
399 /*
400 * Update mtime of parent node
401 */
402 if (mp->am_parent && mp->am_parent->am_mnt)
403 mp->am_parent->am_fattr.mtime.seconds = mp->am_stats.s_mtime;
404
405
406 /*
407 * Update stats
408 */
409 amd_stats.d_mok++;
410 }
411
412 int mount_node P((am_node *mp));
mount_node(mp)413 int mount_node(mp)
414 am_node *mp;
415 {
416 mntfs *mf = mp->am_mnt;
417 int error;
418
419 mf->mf_flags |= MFF_MOUNTING;
420 error = (*mf->mf_ops->mount_fs)(mp);
421 mf = mp->am_mnt;
422 if (error >= 0)
423 mf->mf_flags &= ~MFF_MOUNTING;
424 if (!error && !(mf->mf_ops->fs_flags & FS_MBACKGROUND)) {
425 /* ...but see ifs_mount */
426 am_mounted(mp);
427 }
428
429 return error;
430 }
431
432 void am_unmounted P((am_node *mp));
am_unmounted(mp)433 void am_unmounted(mp)
434 am_node *mp;
435 {
436 mntfs *mf = mp->am_mnt;
437
438 if (!foreground) /* firewall - should never happen */
439 return;
440
441 #ifdef DEBUG
442 /*dlog("in am_unmounted(), foreground = %d", foreground);*/
443 #endif /* DEBUG */
444
445 /*
446 * Do unmounted callback
447 */
448 if (mf->mf_ops->umounted)
449 (*mf->mf_ops->umounted)(mp);
450
451 /*
452 * Update mtime of parent node
453 */
454 if (mp->am_parent && mp->am_parent->am_mnt)
455 mp->am_parent->am_fattr.mtime.seconds = clocktime();
456
457 free_map(mp);
458 }
459
460 int auto_fmount P((am_node *mp));
auto_fmount(mp)461 int auto_fmount(mp)
462 am_node *mp;
463 {
464 mntfs *mf = mp->am_mnt;
465 return (*mf->mf_ops->fmount_fs)(mf);
466 }
467
468 int auto_fumount P((am_node *mp));
auto_fumount(mp)469 int auto_fumount(mp)
470 am_node *mp;
471 {
472 mntfs *mf = mp->am_mnt;
473 return (*mf->mf_ops->fumount_fs)(mf);
474 }
475
476 /*
477 * Fork the automounter
478 *
479 * TODO: Need a better strategy for handling errors
480 */
481 static int dofork(P_void);
dofork()482 static int dofork()
483 {
484 int pid;
485 top:
486 pid = fork();
487
488 if (pid < 0) {
489 sleep(1);
490 goto top;
491 }
492
493 if (pid == 0) {
494 mypid = getpid();
495 foreground = 0;
496 }
497
498 return pid;
499 }
500
501 int background(P_void);
background()502 int background()
503 {
504 int pid = dofork();
505 if (pid == 0) {
506 #ifdef DEBUG
507 dlog("backgrounded");
508 #endif
509 foreground = 0;
510 }
511
512 return pid;
513 }
514
515 /*
516 * Make all the directories in the path.
517 */
518 int mkdirs P((char *path, int mode));
mkdirs(path,mode)519 int mkdirs(path, mode)
520 char *path;
521 int mode;
522 {
523 /*
524 * take a copy in case path is in readonly store
525 */
526 char *p2 = strdup(path);
527 char *sp = p2;
528 struct stat stb;
529 int error_so_far = 0;
530
531 /*
532 * Skip through the string make the directories.
533 * Mostly ignore errors - the result is tested at the end.
534 *
535 * This assumes we are root so that we can do mkdir in a
536 * mode 555 directory...
537 */
538 while (sp = strchr(sp+1, '/')) {
539 *sp = '\0';
540 if (mkdir(p2, mode) < 0) {
541 error_so_far = errno;
542 } else {
543 #ifdef DEBUG
544 dlog("mkdir(%s)", p2);
545 #endif
546 }
547 *sp = '/';
548 }
549
550 if (mkdir(p2, mode) < 0) {
551 error_so_far = errno;
552 } else {
553 #ifdef DEBUG
554 dlog("mkdir(%s)", p2);
555 #endif
556 }
557
558 #ifdef SUNOS4_WORKAROUND
559 /*
560 * Do a sync - if we do rmdirs() immediately
561 * and then the system crashes it leaves
562 * the filesystem in a state that fsck -p
563 * can't fix. (Observed more than once on
564 * SunOS 4 ...)
565 *
566 * The problem was caused by a bug somewhere
567 * in the UFS code which has since been fixed
568 * (at least at Berkeley).
569 *
570 * Attempted workaround - XXX.
571 */
572 sync();
573 #endif /* SUNOS4_WORKAROUND */
574
575 free(p2);
576
577 return stat(path, &stb) == 0 &&
578 (stb.st_mode & S_IFMT) == S_IFDIR ? 0 : error_so_far;
579 }
580
581 /*
582 * Remove as many directories in the path as possible.
583 * Give up if the directory doesn't appear to have
584 * been created by Amd (not mode dr-x) or an rmdir
585 * fails for any reason.
586 */
587 void rmdirs P((char *dir));
rmdirs(dir)588 void rmdirs(dir)
589 char *dir;
590 {
591 char *xdp = strdup(dir);
592 char *dp;
593
594 do {
595 struct stat stb;
596 /*
597 * Try to find out whether this was
598 * created by amd. Do this by checking
599 * for owner write permission.
600 */
601 if (stat(xdp, &stb) == 0 && (stb.st_mode & 0200) == 0) {
602 if (rmdir(xdp) < 0) {
603 if (errno != ENOTEMPTY &&
604 errno != EBUSY &&
605 errno != EEXIST &&
606 errno != EINVAL)
607 plog(XLOG_ERROR, "rmdir(%s): %m", xdp);
608 break;
609 } else {
610 #ifdef DEBUG
611 dlog("rmdir(%s)", xdp);
612 #endif
613 }
614 } else {
615 break;
616 }
617 dp = strrchr(xdp, '/');
618 if (dp)
619 *dp = '\0';
620 } while (dp && dp > xdp);
621 free(xdp);
622 }
623