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