xref: /original-bsd/usr.sbin/amd/amd/util.c (revision c3e32dec)
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 
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 
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  */
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 
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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);
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);
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));
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));
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