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