1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright (c) 2012 by Delphix. All rights reserved.
14  */
15 
16 /*
17  * Make a directory busy. If the argument is an existing file or directory,
18  * simply open it directly and pause. If not, verify that the parent directory
19  * exists, and create a new file in that directory.
20  */
21 
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <dirent.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <string.h>
32 
33 
34 static __attribute__((noreturn)) void
35 usage(const char *progname)
36 {
37 	(void) fprintf(stderr, "Usage: %s <dirname|filename>\n", progname);
38 	exit(1);
39 }
40 
41 static __attribute__((noreturn)) void
42 fail(const char *err)
43 {
44 	perror(err);
45 	exit(1);
46 }
47 
48 static void
49 daemonize(void)
50 {
51 	pid_t	pid;
52 
53 	if ((pid = fork()) < 0) {
54 		fail("fork");
55 	} else if (pid != 0) {
56 		(void) fprintf(stdout, "%ld\n", (long)pid);
57 		exit(0);
58 	}
59 
60 	(void) setsid();
61 	(void) close(0);
62 	(void) close(1);
63 	(void) close(2);
64 }
65 
66 
67 static const char *
68 get_basename(const char *path)
69 {
70 	const char *bn = strrchr(path, '/');
71 	return (bn ? bn + 1 : path);
72 }
73 
74 static ssize_t
75 get_dirnamelen(const char *path)
76 {
77 	const char *end = strrchr(path, '/');
78 	return (end ? end - path : -1);
79 }
80 
81 int
82 main(int argc, char *argv[])
83 {
84 	int		c;
85 	boolean_t	isdir = B_FALSE;
86 	struct stat	sbuf;
87 	char		*fpath = NULL;
88 	char		*prog = argv[0];
89 
90 	while ((c = getopt(argc, argv, "")) != -1) {
91 		switch (c) {
92 		default:
93 			usage(prog);
94 		}
95 	}
96 
97 	argc -= optind;
98 	argv += optind;
99 
100 	if (argc != 1)
101 		usage(prog);
102 
103 	if (stat(argv[0], &sbuf) != 0) {
104 		char	*arg;
105 		const char	*dname, *fname;
106 		size_t	arglen;
107 		ssize_t	dnamelen;
108 
109 		/*
110 		 * The argument supplied doesn't exist. Copy the path, and
111 		 * remove the trailing slash if present.
112 		 */
113 		if ((arg = strdup(argv[0])) == NULL)
114 			fail("strdup");
115 		arglen = strlen(arg);
116 		if (arg[arglen - 1] == '/')
117 			arg[arglen - 1] = '\0';
118 
119 		/* Get the directory and file names. */
120 		fname = get_basename(arg);
121 		dname = arg;
122 		if ((dnamelen = get_dirnamelen(arg)) != -1)
123 			arg[dnamelen] = '\0';
124 		else
125 			dname = ".";
126 
127 		/* The directory portion of the path must exist */
128 		if (stat(dname, &sbuf) != 0 || !(sbuf.st_mode & S_IFDIR))
129 			usage(prog);
130 
131 		if (asprintf(&fpath, "%s/%s", dname, fname) == -1)
132 			fail("asprintf");
133 
134 		free(arg);
135 	} else
136 		switch (sbuf.st_mode & S_IFMT) {
137 			case S_IFDIR:
138 				isdir = B_TRUE;
139 				zfs_fallthrough;
140 			case S_IFLNK:
141 			case S_IFCHR:
142 			case S_IFBLK:
143 				if ((fpath = strdup(argv[0])) == NULL)
144 					fail("strdup");
145 				break;
146 			default:
147 				usage(prog);
148 		}
149 
150 	if (!isdir) {
151 		if (open(fpath, O_CREAT | O_RDWR, 0600) < 0)
152 			fail("open");
153 	} else {
154 		if (opendir(fpath) == NULL)
155 			fail("opendir");
156 	}
157 	free(fpath);
158 
159 	daemonize();
160 	(void) pause();
161 
162 	return (0);
163 }
164