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
34static __attribute__((noreturn)) void
35usage(const char *progname)
36{
37	(void) fprintf(stderr, "Usage: %s <dirname|filename>\n", progname);
38	exit(1);
39}
40
41static __attribute__((noreturn)) void
42fail(const char *err)
43{
44	perror(err);
45	exit(1);
46}
47
48static void
49daemonize(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
67static const char *
68get_basename(const char *path)
69{
70	const char *bn = strrchr(path, '/');
71	return (bn ? bn + 1 : path);
72}
73
74static ssize_t
75get_dirnamelen(const char *path)
76{
77	const char *end = strrchr(path, '/');
78	return (end ? end - path : -1);
79}
80
81int
82main(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