1/*
2 * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <sys/types.h>
25#include <sys/errno.h>
26#include <sys/wait.h>
27#include <sys/time.h>
28#include <sys/resource.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <unistd.h>
32#include <signal.h>
33#include <strings.h>
34
35#define PRIVATE_WEBDAVFS_AGENT_COMMAND "/System/Library/Extensions/webdav_fs.kext/Contents/Resources/webdavfs_agent"
36#define WEBDAVFS_AGENT_PATH "PATH=/System/Library/Extensions/webdav_fs.kext/Contents/Resources"
37#define CFENVFORMATSTRING "__CF_USER_TEXT_ENCODING=0x%X:0:0"
38
39/*****************************************************************************/
40
41static void usage(void)
42{
43	(void)fprintf(stderr,
44		"usage: mount_webdav [-i] [-s] [-S] [-o options] [-v <volume name>]\n");
45	(void)fprintf(stderr,
46		"\t<WebDAV_URL> node\n");
47}
48
49/*****************************************************************************/
50
51/* called when child normally terminates the parent */
52static void parentexit(int x)
53{
54#pragma unused(x)
55	exit(EXIT_SUCCESS);
56}
57
58/*****************************************************************************/
59
60int main(int argc, char *argv[])
61{
62	int pid, terminated_pid, cnt;
63	union wait status;
64	char **argv_child;
65	int error;
66
67	if (argc < 2) {
68		usage();
69		error = EINVAL;
70		goto error_exit;
71	}
72
73	error = 0;
74
75	/*
76	 * Create an argument vector to pass to execve.
77	 * The only difference is argv[0] is set to "webdavfs_agent",
78	 * which allows the 'ps' command to display the correct command name
79	 * in the listing.
80	 */
81	for (cnt = 0; argv[cnt]; cnt++)
82		;
83
84	// allocate new arg vector, adding an extra element for the list terminator
85	argv_child = alloca((cnt + 1) * sizeof(char *));
86	if (argv_child == NULL) {
87		error = ENOMEM;
88		goto error_exit;
89	}
90
91	/* this is the command name of the new process */
92	argv_child[0] = "webdavfs_agent";
93	argv_child[cnt] = (char *)0;
94	/* copy over the remaining argv strings */
95	bcopy(argv + 1, argv_child + 1, (cnt - 1) * sizeof(char *));
96
97	/* make sure SIGTERM is not masked (<rdar://problem/6019476>) */
98	sigset_t mask;
99	sigemptyset(&mask);
100	sigaddset(&mask, SIGTERM);
101	sigprocmask(SIG_UNBLOCK, &mask, NULL);
102
103	/* if terminated by our child, we will exit with EXIT_SUCCESS */
104	signal(SIGTERM, parentexit);
105
106	pid = fork();
107
108	if (pid < 0) {
109		error = errno;
110		goto error_exit;
111	}
112
113	if (pid == 0)
114	{
115		char CFUserTextEncodingEnvSetting[sizeof(CFENVFORMATSTRING) + 20];
116		/*
117		 * Add  WEBDAVFS_AGENT_PATH to the environment so that libsecurity knows where
118		 * to look for webdavfs_agent when  adding it to the keychain.
119		 */
120		char *env[] = {CFUserTextEncodingEnvSetting, WEBDAVFS_AGENT_PATH, "", (char *) 0 };
121
122		/*
123		 * Create a new environment with a definition of __CF_USER_TEXT_ENCODING to work
124		 * around CF's interest in the user's home directory (which could be networked,
125		 * causing recursive references through automount). Make sure we include the uid
126		 * since CF will check for this when deciding if to look in the home directory.
127		 */
128		snprintf(CFUserTextEncodingEnvSetting, sizeof(CFUserTextEncodingEnvSetting), CFENVFORMATSTRING, getuid());
129
130		/* child executes the webdavfs agent */
131		execve(PRIVATE_WEBDAVFS_AGENT_COMMAND, argv_child, env);
132
133		/* We can only get here if the exec failed */
134		error = errno;
135		goto error_exit;
136	}
137
138	/* Wait for child's signal or for child's completion */
139	while ( (terminated_pid = wait4(pid, (int *)&status, 0, NULL)) < 0 )
140	{
141		/* retry if EINTR, else break out with error */
142		if ( errno != EINTR )
143		{
144			break;
145		}
146	}
147
148	/* we'll get here only if the child completed before killing us */
149	if ( (terminated_pid == pid) && WIFEXITED(status) )
150	{
151		error = WEXITSTATUS(status);
152	}
153	else
154	{
155		error = ECHILD;
156	}
157
158error_exit:
159
160	/* Return the set of error codes things expect mounts to return */
161
162	switch (error)
163	{
164		/* The server directory could not be mounted by mount_webdav because the node path is invalid. */
165		case ENOENT:
166			break;
167
168		/* Could not connect to the server because the name or password is not correct */
169		case EAUTH:
170			break;
171
172		/* Could not connect to the server because the name or password is not correct and the user canceled */
173		case ECANCELED:
174			break;
175
176		/* You are already connected to this server volume */
177		case EBUSY:
178			break;
179
180		/* You cannot connect to this server because it cannot be found on the network. Try again later or try a different URL. */
181		case ENODEV:
182			break;
183
184		/* ap everything else to a generic unexpected error */
185		default:
186			error = EINVAL;
187			break;
188	}
189
190	exit(error);
191}
192