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 <strings.h>
28#include <stdlib.h>
29#include <unistd.h>
30#include <errno.h>
31#include <string.h>
32
33static void
34usage(char *progname)
35{
36	(void) fprintf(stderr, "Usage: %s <dirname|filename>\n", progname);
37	exit(1);
38}
39
40static void
41fail(char *err, int rval)
42{
43	perror(err);
44	exit(rval);
45}
46
47static void
48daemonize(void)
49{
50	pid_t	pid;
51
52	if ((pid = fork()) < 0) {
53		fail("fork", 1);
54	} else if (pid != 0) {
55		(void) fprintf(stdout, "%ld\n", (long)pid);
56		exit(0);
57	}
58
59	(void) setsid();
60	(void) close(0);
61	(void) close(1);
62	(void) close(2);
63}
64
65int
66main(int argc, char *argv[])
67{
68	int		ret, c;
69	boolean_t	isdir = B_FALSE;
70	boolean_t	fflag = B_FALSE;
71	boolean_t	rflag = B_FALSE;
72	struct stat	sbuf;
73	char		*fpath = NULL;
74	char		*prog = argv[0];
75
76	while ((c = getopt(argc, argv, "fr")) != -1) {
77		switch (c) {
78		/* Open the file or directory read only */
79		case 'r':
80			rflag = B_TRUE;
81			break;
82		/* Run in the foreground */
83		case 'f':
84			fflag = B_TRUE;
85			break;
86		default:
87			usage(prog);
88		}
89	}
90
91	argc -= optind;
92	argv += optind;
93
94	if (argc != 1)
95		usage(prog);
96
97	if ((ret = stat(argv[0], &sbuf)) != 0) {
98		char	*arg, *dname, *fname;
99		int	arglen;
100		char	*slash;
101		int	rc;
102
103		/*
104		 * The argument supplied doesn't exist. Copy the path, and
105		 * remove the trailing slash if present.
106		 */
107		if ((arg = strdup(argv[0])) == NULL)
108			fail("strdup", 1);
109		arglen = strlen(arg);
110		if (arg[arglen - 1] == '/')
111			arg[arglen - 1] = '\0';
112
113		/*
114		 * Get the directory and file names, using the current directory
115		 * if the provided path doesn't specify a directory at all.
116		 */
117		if ((slash = strrchr(arg, '/')) == NULL) {
118			dname = strdup(".");
119			fname = strdup(arg);
120		} else {
121			*slash = '\0';
122			dname = strdup(arg);
123			fname = strdup(slash + 1);
124		}
125		free(arg);
126		if (dname == NULL || fname == NULL)
127			fail("strdup", 1);
128
129		/* The directory portion of the path must exist */
130		if ((ret = stat(dname, &sbuf)) != 0 || !(sbuf.st_mode &
131		    S_IFDIR))
132			usage(prog);
133
134		rc = asprintf(&fpath, "%s/%s", dname, fname);
135		free(dname);
136		free(fname);
137		if (rc == -1 || fpath == NULL)
138			fail("asprintf", 1);
139
140	} else if ((sbuf.st_mode & S_IFMT) == S_IFREG ||
141	    (sbuf.st_mode & S_IFMT) == S_IFLNK ||
142	    (sbuf.st_mode & S_IFMT) == S_IFCHR ||
143	    (sbuf.st_mode & S_IFMT) == S_IFBLK) {
144		fpath = strdup(argv[0]);
145	} else if ((sbuf.st_mode & S_IFMT) == S_IFDIR) {
146		fpath = strdup(argv[0]);
147		isdir = B_TRUE;
148	} else {
149		usage(prog);
150	}
151
152	if (fpath == NULL)
153		fail("strdup", 1);
154
155	if (isdir == B_FALSE) {
156		int	fd, flags;
157		mode_t	mode = S_IRUSR | S_IWUSR;
158
159		flags = rflag == B_FALSE ? O_CREAT | O_RDWR : O_RDONLY;
160
161		if ((fd = open(fpath, flags, mode)) < 0)
162			fail("open", 1);
163	} else {
164		DIR	*dp;
165
166		if ((dp = opendir(fpath)) == NULL)
167			fail("opendir", 1);
168	}
169	free(fpath);
170
171	if (fflag == B_FALSE)
172		daemonize();
173	(void) pause();
174
175	/* NOTREACHED */
176	return (0);
177}
178