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