]> git.saurik.com Git - apple/cf.git/blob - CFPlatform.c
CF-476.17.tar.gz
[apple/cf.git] / CFPlatform.c
1 /*
2 * Copyright (c) 2008 Apple 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 /* CFPlatform.c
24 Copyright 1999-2002, Apple, Inc. All rights reserved.
25 Responsibility: Christopher Kane
26 */
27
28 #include "CFInternal.h"
29 #include "CFPriv.h"
30 #include <stdlib.h>
31 #include <sys/stat.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <pwd.h>
35 #include <crt_externs.h>
36 #include <mach-o/dyld.h>
37
38 #if DEPLOYMENT_TARGET_MACOSX
39 #define kCFPlatformInterfaceStringEncoding kCFStringEncodingUTF8
40 #else
41 #define kCFPlatformInterfaceStringEncoding CFStringGetSystemEncoding()
42 #endif
43
44 static CFStringRef _CFUserName(void);
45
46 #if DEPLOYMENT_TARGET_MACOSX
47 // CoreGraphics and LaunchServices are only projects (1 Dec 2006) that use these
48 char **_CFArgv(void) { return *_NSGetArgv(); }
49 int _CFArgc(void) { return *_NSGetArgc(); }
50 #endif
51
52
53 __private_extern__ Boolean _CFGetCurrentDirectory(char *path, int maxlen) {
54 #if 0 || 0
55 DWORD len = GetCurrentDirectoryA(maxlen, path);
56 return ((0 != len) && (maxlen > 0) && (len + 1 <= (DWORD)maxlen));
57 #else
58 return getcwd(path, maxlen) != NULL;
59 #endif
60 }
61
62 static Boolean __CFIsCFM = false;
63
64 // If called super early, we just return false
65 __private_extern__ Boolean _CFIsCFM(void) {
66 return __CFIsCFM;
67 }
68
69 #if 0 || 0
70 #define PATH_SEP '\\'
71 #else
72 #define PATH_SEP '/'
73 #endif
74
75 #if !defined(__WIN32__)
76 #define PATH_LIST_SEP ':'
77
78 static char *_CFSearchForNameInPath(const char *name, char *path) {
79 struct stat statbuf;
80 char nname[strlen(name) + strlen(path) + 2];
81 int no_hang_fd = open("/dev/autofs_nowait", 0);
82 for (;;) {
83 char *p = (char *)strchr(path, PATH_LIST_SEP);
84 if (NULL != p) {
85 *p = '\0';
86 }
87 nname[0] = '\0';
88 strlcat(nname, path, sizeof(nname));
89 strlcat(nname, "/", sizeof(nname));
90 strlcat(nname, name, sizeof(nname));
91 // Could also do access(us, X_OK) == 0 in next condition,
92 // for executable-only searching
93 if (0 == stat(nname, &statbuf) && (statbuf.st_mode & S_IFMT) == S_IFREG) {
94 if (p != NULL) {
95 *p = PATH_LIST_SEP;
96 }
97 close(no_hang_fd);
98 return strdup(nname);
99 }
100 if (NULL == p) {
101 break;
102 }
103 *p = PATH_LIST_SEP;
104 path = p + 1;
105 }
106 close(no_hang_fd);
107 return NULL;
108 }
109
110 #endif
111
112 static const char *__CFProcessPath = NULL;
113 static const char *__CFprogname = NULL;
114
115 const char **_CFGetProgname(void) {
116 if (!__CFprogname)
117 _CFProcessPath(); // sets up __CFprogname as a side-effect
118 return &__CFprogname;
119 }
120
121 const char **_CFGetProcessPath(void) {
122 if (!__CFProcessPath)
123 _CFProcessPath(); // sets up __CFProcessPath as a side-effect
124 return &__CFProcessPath;
125 }
126
127 const char *_CFProcessPath(void) {
128 if (__CFProcessPath) return __CFProcessPath;
129
130 char *thePath = NULL;
131 #if DEPLOYMENT_TARGET_MACOSX
132 if (!issetugid()) {
133 thePath = getenv("CFProcessPath");
134 if (thePath) {
135 int len = strlen(thePath);
136 __CFProcessPath = (const char *)CFAllocatorAllocate(kCFAllocatorSystemDefault, len+1, 0);
137 if (__CFOASafe) __CFSetLastAllocationEventName((void *)__CFProcessPath, "CFUtilities (process-path)");
138 memmove((char *)__CFProcessPath, thePath, len + 1);
139 }
140 }
141 #endif
142 #if DEPLOYMENT_TARGET_MACOSX
143 int execIndex = 0;
144 #endif
145
146 if (!__CFProcessPath && NULL != (*_NSGetArgv())[execIndex]) {
147 int no_hang_fd = open("/dev/autofs_nowait", 0);
148 char buf[CFMaxPathSize] = {0};
149 struct stat statbuf;
150 const char *arg0 = (*_NSGetArgv())[execIndex];
151 if (arg0[0] == '/') {
152 // We've got an absolute path; look no further;
153 thePath = (char *)arg0;
154 } else {
155 char *theList = getenv("PATH");
156 if (NULL != theList && NULL == strrchr(arg0, '/')) {
157 thePath = _CFSearchForNameInPath(arg0, theList);
158 if (thePath) {
159 // User could have "." or "../bin" or other relative path in $PATH
160 if (('/' != thePath[0]) && _CFGetCurrentDirectory(buf, CFMaxPathSize)) {
161 strlcat(buf, "/", sizeof(buf));
162 strlcat(buf, thePath, sizeof(buf));
163 if (0 == stat(buf, &statbuf)) {
164 free(thePath);
165 thePath = buf;
166 }
167 }
168 if (thePath != buf) {
169 strlcpy(buf, thePath, sizeof(buf));
170 free((void *)thePath);
171 thePath = buf;
172 }
173 }
174 }
175 }
176
177 // After attempting a search through $PATH, if existant,
178 // try prepending the current directory to argv[0].
179 if (!thePath && _CFGetCurrentDirectory(buf, CFMaxPathSize)) {
180 if (buf[strlen(buf)-1] != '/') {
181 strlcat(buf, "/", sizeof(buf));
182 }
183 strlcat(buf, arg0, CFMaxPathSize);
184 if (0 == stat(buf, &statbuf)) {
185 thePath = buf;
186 }
187 }
188
189 if (thePath) {
190 // We are going to process the buffer replacing all "/./" and "//" with "/"
191 CFIndex srcIndex = 0, dstIndex = 0;
192 CFIndex len = strlen(thePath);
193 for (srcIndex=0; srcIndex<len; srcIndex++) {
194 thePath[dstIndex] = thePath[srcIndex];
195 dstIndex++;
196 while (srcIndex < len-1 && thePath[srcIndex] == '/' && (thePath[srcIndex+1] == '/' || (thePath[srcIndex+1] == '.' && srcIndex < len-2 && thePath[srcIndex+2] == '/'))) srcIndex += (thePath[srcIndex+1] == '/' ? 1 : 2);
197 }
198 thePath[dstIndex] = 0;
199 }
200 if (!thePath) {
201 thePath = (*_NSGetArgv())[execIndex];
202 }
203
204 if (thePath) {
205 int len = strlen(thePath);
206 __CFProcessPath = (const char *)CFAllocatorAllocate(kCFAllocatorSystemDefault, len + 1, 0);
207 if (__CFOASafe) __CFSetLastAllocationEventName((void *)__CFProcessPath, "CFUtilities (process-path)");
208 memmove((char *)__CFProcessPath, thePath, len + 1);
209 }
210 if (__CFProcessPath) {
211
212 const char *p = 0;
213 int i;
214 for (i = 0; __CFProcessPath[i] != 0; i++){
215 if (__CFProcessPath[i] == PATH_SEP)
216 p = __CFProcessPath + i;
217 }
218 if (p != 0)
219 __CFprogname = p + 1;
220 else
221 __CFprogname = __CFProcessPath;
222 }
223
224 close(no_hang_fd);
225 }
226 if (!__CFProcessPath) {
227 __CFProcessPath = "";
228 __CFprogname = __CFProcessPath;
229 } else {
230 const char *p = 0;
231 int i;
232 for (i = 0; __CFProcessPath[i] != 0; i++){
233 if (__CFProcessPath[i] == PATH_SEP)
234 p = __CFProcessPath + i;
235 }
236 if (p != 0)
237 __CFprogname = p + 1;
238 else
239 __CFprogname = __CFProcessPath;
240 }
241 return __CFProcessPath;
242 }
243
244 __private_extern__ CFStringRef _CFProcessNameString(void) {
245 static CFStringRef __CFProcessNameString = NULL;
246 if (!__CFProcessNameString) {
247 const char *processName = *_CFGetProgname();
248 if (!processName) processName = "";
249 __CFProcessNameString = CFStringCreateWithCString(__CFGetDefaultAllocator(), processName, kCFPlatformInterfaceStringEncoding);
250 }
251 return __CFProcessNameString;
252 }
253
254 static CFStringRef __CFUserName = NULL;
255
256 #if (DEPLOYMENT_TARGET_MACOSX) || defined(__svr4__) || defined(__hpux__) || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
257 static CFURLRef __CFHomeDirectory = NULL;
258 static uint32_t __CFEUID = -1;
259 static uint32_t __CFUID = -1;
260
261 static CFURLRef _CFCopyHomeDirURLForUser(struct passwd *upwd) {
262 CFURLRef home = NULL;
263 if (!issetugid()) {
264 const char *path = getenv("CFFIXED_USER_HOME");
265 if (path) {
266 home = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)path, strlen(path), true);
267 }
268 }
269 if (!home) {
270 if (upwd && upwd->pw_dir) {
271 home = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)upwd->pw_dir, strlen(upwd->pw_dir), true);
272 }
273 }
274 return home;
275 }
276
277 static void _CFUpdateUserInfo(void) {
278 struct passwd *upwd;
279
280 __CFEUID = geteuid();
281 __CFUID = getuid();
282 if (__CFHomeDirectory) CFRelease(__CFHomeDirectory);
283 __CFHomeDirectory = NULL;
284 if (__CFUserName) CFRelease(__CFUserName);
285 __CFUserName = NULL;
286
287 upwd = getpwuid(__CFEUID ? __CFEUID : __CFUID);
288 __CFHomeDirectory = _CFCopyHomeDirURLForUser(upwd);
289 if (!__CFHomeDirectory) {
290 const char *cpath = getenv("HOME");
291 if (cpath) {
292 __CFHomeDirectory = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)cpath, strlen(cpath), true);
293 }
294 }
295
296 // This implies that UserManager stores directory info in CString
297 // rather than FileSystemRep. Perhaps this is wrong & we should
298 // expect NeXTSTEP encodings. A great test of our localized system would
299 // be to have a user "O-umlat z e r". XXX
300 if (upwd && upwd->pw_name) {
301 __CFUserName = CFStringCreateWithCString(kCFAllocatorSystemDefault, upwd->pw_name, kCFPlatformInterfaceStringEncoding);
302 } else {
303 const char *cuser = getenv("USER");
304 if (cuser)
305 __CFUserName = CFStringCreateWithCString(kCFAllocatorSystemDefault, cuser, kCFPlatformInterfaceStringEncoding);
306 }
307 }
308 #endif
309
310 static CFURLRef _CFCreateHomeDirectoryURLForUser(CFStringRef uName) {
311 #if (DEPLOYMENT_TARGET_MACOSX) || defined(__svr4__) || defined(__hpux__) || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
312 if (!uName) {
313 if (geteuid() != __CFEUID || getuid() != __CFUID || !__CFHomeDirectory)
314 _CFUpdateUserInfo();
315 if (__CFHomeDirectory) CFRetain(__CFHomeDirectory);
316 return __CFHomeDirectory;
317 } else {
318 struct passwd *upwd = NULL;
319 char buf[128], *user;
320 SInt32 len = CFStringGetLength(uName), size = CFStringGetMaximumSizeForEncoding(len, kCFPlatformInterfaceStringEncoding);
321 CFIndex usedSize;
322 if (size < 127) {
323 user = buf;
324 } else {
325 user = CFAllocatorAllocate(kCFAllocatorSystemDefault, size+1, 0);
326 if (__CFOASafe) __CFSetLastAllocationEventName(user, "CFUtilities (temp)");
327 }
328 if (CFStringGetBytes(uName, CFRangeMake(0, len), kCFPlatformInterfaceStringEncoding, 0, true, (uint8_t *)user, size, &usedSize) == len) {
329 user[usedSize] = '\0';
330 upwd = getpwnam(user);
331 }
332 if (buf != user) {
333 CFAllocatorDeallocate(kCFAllocatorSystemDefault, user);
334 }
335 return _CFCopyHomeDirURLForUser(upwd);
336 }
337 #elif defined(__WIN32__)
338 CFStringRef user = !uName ? _CFUserName() : uName;
339 CFURLRef home = NULL;
340
341 if (!uName || CFEqual(user, _CFUserName())) {
342 const char *cpath = getenv("HOMEPATH");
343 const char *cdrive = getenv("HOMEDRIVE");
344 if (cdrive && cpath) {
345 char fullPath[CFMaxPathSize];
346 CFStringRef str;
347 strlcpy(fullPath, cdrive, sizeof(fullPath));
348 strlcat(fullPath, cpath, sizeof(fullPath));
349 str = CFStringCreateWithCString(kCFAllocatorSystemDefault, fullPath, kCFPlatformInterfaceStringEncoding);
350 home = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, kCFURLWindowsPathStyle, true);
351 CFRelease(str);
352 }
353 }
354 if (home == NULL) {
355 UniChar pathChars[MAX_PATH];
356 if (S_OK == SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, (LPWSTR) pathChars)) {
357 UniChar* p = pathChars;
358 CFIndex len = 0;
359 CFStringRef str;
360 while (*p++ != 0)
361 ++len;
362 str = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, pathChars, len);
363 home = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, kCFURLWindowsPathStyle, true);
364 CFRelease(str);
365 } else {
366 // We have to get "some" directory location, so fall-back to the
367 // processes current directory.
368 UniChar currDir[MAX_PATH];
369 DWORD dwChars = GetCurrentDirectory(MAX_PATH + 1, (LPWSTR)currDir);
370 if (dwChars > 0) {
371 UniChar* p = currDir;
372 CFIndex len = 0;
373 CFStringRef str;
374 while (*p++ != 0)
375 ++len;
376 str = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, currDir, len);
377 home = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, kCFURLWindowsPathStyle, true);
378 }
379 }
380 }
381 // We could do more here (as in KB Article Q101507). If that article is to
382 // be believed, we should only run into this case on Win95, or through
383 // user error.
384 if (home) {
385 CFStringRef str = CFURLCopyFileSystemPath(home, kCFURLWindowsPathStyle);
386 if (str && CFStringGetLength(str) == 0) {
387 CFRelease(home);
388 home=NULL;
389 }
390 if (str) CFRelease(str);
391 }
392 return home;
393 #else
394 #error Dont know how to compute users home directories on this platform
395 #endif
396 }
397
398 static CFStringRef _CFUserName(void) {
399 #if (DEPLOYMENT_TARGET_MACOSX) || defined(__svr4__) || defined(__hpux__) || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
400 if (geteuid() != __CFEUID || getuid() != __CFUID)
401 _CFUpdateUserInfo();
402 #elif defined(__WIN32__)
403 if (!__CFUserName) {
404 char username[1040];
405 DWORD size = 1040;
406 username[0] = 0;
407 if (GetUserNameA(username, &size)) {
408 __CFUserName = CFStringCreateWithCString(kCFAllocatorSystemDefault, username, kCFPlatformInterfaceStringEncoding);
409 } else {
410 const char *cname = getenv("USERNAME");
411 if (cname)
412 __CFUserName = CFStringCreateWithCString(kCFAllocatorSystemDefault, cname, kCFPlatformInterfaceStringEncoding);
413 }
414 }
415 #else
416 #error Dont know how to compute user name on this platform
417 #endif
418 if (!__CFUserName)
419 __CFUserName = (CFStringRef)CFRetain(CFSTR(""));
420 return __CFUserName;
421 }
422
423 __private_extern__ CFStringRef _CFGetUserName(void) {
424 return CFStringCreateCopy(kCFAllocatorSystemDefault, _CFUserName());
425 }
426
427 #define CFMaxHostNameLength 256
428 #define CFMaxHostNameSize (CFMaxHostNameLength+1)
429
430 __private_extern__ CFStringRef _CFStringCreateHostName(void) {
431 char myName[CFMaxHostNameSize];
432
433 // return @"" instead of nil a la CFUserName() and Ali Ozer
434 if (0 != gethostname(myName, CFMaxHostNameSize)) myName[0] = '\0';
435 return CFStringCreateWithCString(kCFAllocatorSystemDefault, myName, kCFPlatformInterfaceStringEncoding);
436 }
437
438 /* These are sanitized versions of the above functions. We might want to eliminate the above ones someday.
439 These can return NULL.
440 */
441 CF_EXPORT CFStringRef CFGetUserName(void) {
442 return _CFUserName();
443 }
444
445 CF_EXPORT CFURLRef CFCopyHomeDirectoryURLForUser(CFStringRef uName) {
446 return _CFCreateHomeDirectoryURLForUser(uName);
447 }
448
449 #undef CFMaxHostNameLength
450 #undef CFMaxHostNameSize
451