Cygwin: flock: Fix overlap handling in lf_setlock() and lf_clearlock()
[newlib-cygwin.git] / winsup / cygwin / external.cc
blob6dfd11c4447059e55524e03eed43af2432b7edd7
1 /* external.cc: Interface to Cygwin internals from external programs.
3 Written by Christopher Faylor <cgf@cygnus.com>
5 This file is part of Cygwin.
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
9 details. */
11 #include "winsup.h"
12 #include "sigproc.h"
13 #include "pinfo.h"
14 #include "shared_info.h"
15 #include "cygwin_version.h"
16 #include "cygerrno.h"
17 #include "path.h"
18 #include "fhandler.h"
19 #include "dtable.h"
20 #include "cygheap.h"
21 #include "heap.h"
22 #include "cygtls.h"
23 #include "child_info.h"
24 #include "environ.h"
25 #include "cygserver_setpwd.h"
26 #include "pwdgrp.h"
27 #include "exception.h"
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <wchar.h>
31 #include <iptypes.h>
33 child_info *get_cygwin_startup_info ();
34 static void exit_process (UINT, bool) __attribute__((noreturn));
36 static winpids pids;
38 static external_pinfo *
39 fillout_pinfo (pid_t pid, int winpid)
41 BOOL nextpid;
42 static external_pinfo ep;
43 static char ep_progname_long_buf[NT_MAX_PATH];
45 if ((nextpid = !!(pid & CW_NEXTPID)))
46 pid ^= CW_NEXTPID;
49 static unsigned int i;
50 if (!pids.npids || !nextpid)
52 pids.set (winpid);
53 i = 0;
56 if (!pid)
57 i = 0;
59 memset (&ep, 0, sizeof ep);
60 while (i < pids.npids)
62 DWORD thispid = pids.winpid (i);
63 _pinfo *p = pids[i];
64 i++;
66 /* Native Windows process not started from Cygwin have no procinfo
67 attached. They don't have a real Cygwin PID either. We fake a
68 Cygwin PID beyond MAX_PID. */
69 if (!p)
71 if (!nextpid && thispid + MAX_PID != (DWORD) pid)
72 continue;
73 ep.pid = thispid + MAX_PID;
74 ep.dwProcessId = thispid;
75 ep.process_state = PID_IN_USE;
76 ep.ctty = CTTY_UNINITIALIZED;
77 break;
79 else if (nextpid || p->pid == pid)
81 ep.ctty = (!CTTY_IS_VALID (p->ctty) || iscons_dev (p->ctty))
82 ? p->ctty : device::minor (p->ctty);
83 ep.pid = p->pid;
84 ep.ppid = p->ppid;
85 ep.dwProcessId = p->dwProcessId;
86 ep.uid = p->uid;
87 ep.gid = p->gid;
88 ep.pgid = p->pgid;
89 ep.sid = p->sid;
90 ep.umask = 0;
91 ep.start_time = p->start_time;
92 ep.rusage_self = p->rusage_self;
93 ep.rusage_children = p->rusage_children;
94 ep.progname[0] = '\0';
95 sys_wcstombs(ep.progname, MAX_PATH, p->progname);
96 ep.strace_mask = 0;
97 ep.version = EXTERNAL_PINFO_VERSION;
99 ep.process_state = p->process_state;
101 ep.uid32 = p->uid;
102 ep.gid32 = p->gid;
104 ep.progname_long = ep_progname_long_buf;
105 mount_table->conv_to_posix_path (p->progname, ep.progname_long, 0);
106 break;
110 if (!ep.pid)
112 i = 0;
113 pids.reset ();
114 return NULL;
116 return &ep;
119 static inline uintptr_t
120 get_cygdrive_info (char *user, char *system, char *user_flags,
121 char *system_flags)
123 int res = mount_table->get_cygdrive_info (user, system, user_flags,
124 system_flags);
125 return (res == ERROR_SUCCESS) ? 1 : 0;
128 static bool
129 check_ntsec (const char *filename)
131 if (!filename)
132 return true;
133 path_conv pc (filename);
134 return pc.has_acls ();
137 /* Copy cygwin environment variables to the Windows environment. */
138 static PWCHAR
139 create_winenv (const char * const *env)
141 int unused_envc;
142 PWCHAR envblock = NULL;
143 char **envp = build_env (env ?: environ, envblock, unused_envc, false,
144 NULL);
145 PWCHAR p = envblock;
147 if (envp)
149 for (char **e = envp; *e; e++)
150 cfree (*e);
151 cfree (envp);
153 /* If we got an env block, just return pointer to win env. */
154 if (env)
155 return envblock;
156 /* Otherwise sync win env of current process with its posix env. */
157 if (!p)
158 return NULL;
159 while (*p)
161 PWCHAR eq = wcschr (p, L'=');
162 if (eq)
164 *eq = L'\0';
165 SetEnvironmentVariableW (p, ++eq);
166 p = eq;
168 p = wcschr (p, L'\0') + 1;
170 free (envblock);
171 return NULL;
175 * Cygwin-specific wrapper for win32 ExitProcess and TerminateProcess.
176 * It ensures that the correct exit code, derived from the specified
177 * status value, will be made available to this process's parent (if
178 * that parent is also a cygwin process). If useTerminateProcess is
179 * true, then TerminateProcess(GetCurrentProcess(),...) will be used;
180 * otherwise, ExitProcess(...) is called.
182 * Used by startup code for cygwin processes which is linked statically
183 * into applications, and is not part of the cygwin DLL -- which is why
184 * this interface is exposed. "Normal" programs should use ANSI exit(),
185 * ANSI abort(), or POSIX _exit(), rather than this function -- because
186 * calling ExitProcess or TerminateProcess, even through this wrapper,
187 * skips much of the cygwin process cleanup code.
189 static void
190 exit_process (UINT status, bool useTerminateProcess)
192 pid_t pid = getpid ();
193 external_pinfo *ep = fillout_pinfo (pid, 1);
194 DWORD dwpid = ep ? ep->dwProcessId : pid;
195 pinfo p (pid, PID_MAP_RW);
196 if (ep)
197 pid = ep->pid;
198 if ((dwpid == GetCurrentProcessId()) && (p->pid == pid))
199 p.set_exit_code ((DWORD)status);
200 if (useTerminateProcess)
201 TerminateProcess (GetCurrentProcess(), status);
202 /* avoid 'else' clause to silence warning */
203 ExitProcess (status);
207 extern "C" uintptr_t
208 cygwin_internal (cygwin_getinfo_types t, ...)
210 va_list arg;
211 uintptr_t res = (uintptr_t) -1;
212 va_start (arg, t);
214 switch (t)
216 case CW_LOCK_PINFO:
217 res = 1;
218 break;
220 case CW_UNLOCK_PINFO:
221 res = 1;
222 break;
224 case CW_GETTHREADNAME:
225 res = (uintptr_t) cygthread::name (va_arg (arg, DWORD));
226 break;
228 case CW_SETTHREADNAME:
230 set_errno (ENOSYS);
231 res = 0;
233 break;
235 case CW_GETPINFO:
236 res = (uintptr_t) fillout_pinfo (va_arg (arg, DWORD), 0);
237 break;
239 case CW_GETVERSIONINFO:
240 res = (uintptr_t) cygwin_version_strings;
241 break;
243 case CW_READ_V1_MOUNT_TABLES:
244 set_errno (ENOSYS);
245 res = 1;
246 break;
248 case CW_USER_DATA:
249 res = (uintptr_t) &__cygwin_user_data;
250 break;
252 case CW_PERFILE:
253 perfile_table = va_arg (arg, struct __cygwin_perfile *);
254 res = 0;
255 break;
257 case CW_GET_CYGDRIVE_PREFIXES:
259 char *user = va_arg (arg, char *);
260 char *system = va_arg (arg, char *);
261 res = get_cygdrive_info (user, system, NULL, NULL);
263 break;
265 case CW_GETPINFO_FULL:
266 res = (uintptr_t) fillout_pinfo (va_arg (arg, pid_t), 1);
267 break;
269 case CW_INIT_EXCEPTIONS:
270 /* noop */ /* init_exceptions (va_arg (arg, exception_list *)); */
271 res = 0;
272 break;
274 case CW_GET_CYGDRIVE_INFO:
276 char *user = va_arg (arg, char *);
277 char *system = va_arg (arg, char *);
278 char *user_flags = va_arg (arg, char *);
279 char *system_flags = va_arg (arg, char *);
280 res = get_cygdrive_info (user, system, user_flags, system_flags);
282 break;
284 case CW_SET_CYGWIN_REGISTRY_NAME:
285 case CW_GET_CYGWIN_REGISTRY_NAME:
286 res = 0;
287 break;
289 case CW_STRACE_TOGGLE:
291 pid_t pid = va_arg (arg, pid_t);
292 pinfo p (pid);
293 if (p)
295 sig_send (p, __SIGSTRACE);
296 res = 0;
298 else
300 set_errno (ESRCH);
301 res = (uintptr_t) -1;
304 break;
306 case CW_STRACE_ACTIVE:
308 res = strace.active ();
310 break;
312 case CW_CYGWIN_PID_TO_WINPID:
314 pinfo p (va_arg (arg, pid_t));
315 res = p ? p->dwProcessId : 0;
317 break;
319 case CW_WINPID_TO_CYGWIN_PID:
321 DWORD winpid = va_arg (arg, DWORD);
322 pid_t pid = cygwin_pid (winpid);
323 res = pid ?: winpid + MAX_PID;
325 break;
327 case CW_MAX_CYGWIN_PID:
328 res = MAX_PID;
329 break;
331 case CW_EXTRACT_DOMAIN_AND_USER:
333 WCHAR nt_domain[MAX_DOMAIN_NAME_LEN + 1];
334 WCHAR nt_user[UNLEN + 1];
336 struct passwd *pw = va_arg (arg, struct passwd *);
337 char *domain = va_arg (arg, char *);
338 char *user = va_arg (arg, char *);
339 extract_nt_dom_user (pw, nt_domain, nt_user);
340 if (domain)
341 sys_wcstombs (domain, MAX_DOMAIN_NAME_LEN + 1, nt_domain);
342 if (user)
343 sys_wcstombs (user, UNLEN + 1, nt_user);
344 res = 0;
346 break;
348 case CW_CMDLINE_ALLOC:
350 size_t n;
351 char *cmdline_cheap;
352 char *cmdline = NULL;
354 pid_t pid = va_arg (arg, pid_t);
355 pinfo p (pid);
356 cmdline_cheap = (p ? p->cmdline (n) : NULL);
357 if (cmdline_cheap)
359 cmdline = (char *) malloc (n + 1);
360 if (cmdline)
362 memcpy (cmdline, cmdline_cheap, n);
363 cmdline[n] = '\0';
365 cfree (cmdline_cheap);
367 res = (uintptr_t) cmdline;
369 break;
371 case CW_CHECK_NTSEC:
373 char *filename = va_arg (arg, char *);
374 res = check_ntsec (filename);
376 break;
378 case CW_GET_ERRNO_FROM_WINERROR:
380 int error = va_arg (arg, int);
381 int deferrno = va_arg (arg, int);
382 res = geterrno_from_win_error (error, deferrno);
384 break;
386 case CW_GET_POSIX_SECURITY_ATTRIBUTE:
388 path_conv dummy;
389 security_descriptor sd;
390 int attribute = va_arg (arg, int);
391 PSECURITY_ATTRIBUTES psa = va_arg (arg, PSECURITY_ATTRIBUTES);
392 void *sd_buf = va_arg (arg, void *);
393 DWORD sd_buf_size = va_arg (arg, DWORD);
394 set_security_attribute (dummy, attribute, psa, sd);
395 if (!psa->lpSecurityDescriptor)
396 res = sd.size ();
397 else
399 psa->lpSecurityDescriptor = sd_buf;
400 res = sd.copy (sd_buf, sd_buf_size);
403 break;
405 case CW_GET_SHMLBA:
407 res = wincap.allocation_granularity ();
409 break;
411 case CW_GET_UID_FROM_SID:
413 cygpsid psid = va_arg (arg, PSID);
414 res = psid.get_uid (NULL);
416 break;
418 case CW_GET_GID_FROM_SID:
420 cygpsid psid = va_arg (arg, PSID);
421 res = psid.get_gid (NULL);
423 break;
425 case CW_GET_BINMODE:
427 const char *path = va_arg (arg, const char *);
428 path_conv p (path, PC_SYM_FOLLOW | PC_NULLEMPTY);
429 if (p.error)
431 set_errno (p.error);
432 res = (unsigned long) -1;
434 else
435 res = p.binmode ();
437 break;
439 case CW_HOOK:
441 const char *name = va_arg (arg, const char *);
442 const void *hookfn = va_arg (arg, const void *);
443 WORD subsys;
444 res = (uintptr_t) hook_or_detect_cygwin (name, hookfn, subsys);
446 break;
448 case CW_ARGV:
450 child_info_spawn *ci = (child_info_spawn *) get_cygwin_startup_info ();
451 res = (uintptr_t) (ci ? ci->moreinfo->argv : NULL);
453 break;
455 case CW_ENVP:
457 child_info_spawn *ci = (child_info_spawn *) get_cygwin_startup_info ();
458 res = (uintptr_t) (ci ? ci->moreinfo->envp : NULL);
460 break;
462 case CW_DEBUG_SELF:
463 error_start_init (va_arg (arg, const char *));
464 res = try_to_debug ();
465 break;
467 case CW_SYNC_WINENV:
468 create_winenv (NULL);
469 res = 0;
470 break;
472 case CW_CYGTLS_PADSIZE:
473 res = __CYGTLS_PADSIZE__;
474 break;
476 case CW_SET_DOS_FILE_WARNING:
477 res = 0;
478 break;
480 case CW_SET_PRIV_KEY:
482 const char *passwd = va_arg (arg, const char *);
483 const char *username = va_arg (arg, const char *);
484 res = setlsapwd (passwd, username);
486 break;
488 case CW_SETERRNO:
490 const char *file = va_arg (arg, const char *);
491 int line = va_arg (arg, int);
492 seterrno(file, line);
493 res = 0;
495 break;
497 case CW_EXIT_PROCESS:
499 UINT status = va_arg (arg, UINT);
500 int useTerminateProcess = va_arg (arg, int);
501 exit_process (status, !!useTerminateProcess); /* no return */
503 case CW_SET_EXTERNAL_TOKEN:
505 HANDLE token = va_arg (arg, HANDLE);
506 int type = va_arg (arg, int);
507 set_imp_token (token, type);
508 res = 0;
510 break;
512 case CW_GET_INSTKEY:
514 PWCHAR dest = va_arg (arg, PWCHAR);
515 wcscpy (dest, cygheap->installation_key_buf);
516 res = 0;
518 break;
520 case CW_INT_SETLOCALE:
522 extern void internal_setlocale ();
523 internal_setlocale ();
524 res = 0;
526 break;
528 case CW_CVT_MNT_OPTS:
530 extern bool fstab_read_flags (char **, unsigned &, bool);
531 char **option_string = va_arg (arg, char **);
532 if (!option_string || !*option_string)
533 set_errno (EINVAL);
534 else
536 unsigned *pflags = va_arg (arg, unsigned *);
537 unsigned flags = pflags ? *pflags : 0;
538 if (fstab_read_flags (option_string, flags, true))
540 if (pflags)
541 *pflags = flags;
542 res = 0;
546 break;
548 case CW_LST_MNT_OPTS:
550 extern char *fstab_list_flags ();
551 char **option_string = va_arg (arg, char **);
552 if (!option_string)
553 set_errno (EINVAL);
554 else
556 *option_string = fstab_list_flags ();
557 if (*option_string)
558 res = 0;
561 break;
563 case CW_STRERROR:
565 int err = va_arg (arg, int);
566 res = (uintptr_t) strerror (err);
568 break;
570 case CW_CVT_ENV_TO_WINENV:
572 char **posix_env = va_arg (arg, char **);
573 res = (uintptr_t) create_winenv (posix_env);
575 break;
577 case CW_ALLOC_DRIVE_MAP:
579 dos_drive_mappings *ddm = new dos_drive_mappings ();
580 res = (uintptr_t) ddm;
582 break;
584 case CW_MAP_DRIVE_MAP:
586 dos_drive_mappings *ddm = va_arg (arg, dos_drive_mappings *);
587 wchar_t *pathbuf = va_arg (arg, wchar_t *);
588 if (ddm && pathbuf)
589 res = (uintptr_t) ddm->fixup_if_match (pathbuf);
591 break;
593 case CW_FREE_DRIVE_MAP:
595 dos_drive_mappings *ddm = va_arg (arg, dos_drive_mappings *);
596 if (ddm)
597 delete ddm;
598 res = 0;
600 break;
602 case CW_SETENT:
604 int group = va_arg (arg, int);
605 int enums = va_arg (arg, int);
606 PCWSTR enum_tdoms = va_arg (arg, PCWSTR);
607 if (group)
608 res = (uintptr_t) setgrent_filtered (enums, enum_tdoms);
609 else
610 res = (uintptr_t) setpwent_filtered (enums, enum_tdoms);
612 break;
614 case CW_GETENT:
616 int group = va_arg (arg, int);
617 void *obj = va_arg (arg, void *);
618 if (obj)
620 if (group)
621 res = (uintptr_t) getgrent_filtered (obj);
622 else
623 res = (uintptr_t) getpwent_filtered (obj);
626 break;
628 case CW_ENDENT:
630 int group = va_arg (arg, int);
631 void *obj = va_arg (arg, void *);
632 if (obj)
634 if (group)
635 endgrent_filtered (obj);
636 else
637 endpwent_filtered (obj);
638 res = 0;
641 break;
643 case CW_GETNSSSEP:
644 res = (uintptr_t) NSS_SEPARATOR_STRING;
645 break;
647 case CW_GETNSS_PWD_SRC:
648 res = (uintptr_t) cygheap->pg.nss_pwd_src ();
649 break;
651 case CW_GETNSS_GRP_SRC:
652 res = (uintptr_t) cygheap->pg.nss_grp_src ();
653 break;
655 case CW_GETPWSID:
657 int db_only = va_arg (arg, int);
658 PSID psid = va_arg (arg, PSID);
659 cygpsid sid (psid);
660 res = (uintptr_t) (db_only ? internal_getpwsid_from_db (sid)
661 : internal_getpwsid (sid));
663 break;
665 case CW_GETGRSID:
667 int db_only = va_arg (arg, int);
668 PSID psid = va_arg (arg, PSID);
669 cygpsid sid (psid);
670 res = (uintptr_t) (db_only ? internal_getgrsid_from_db (sid)
671 : internal_getgrsid (sid));
673 break;
675 case CW_CYGNAME_FROM_WINNAME:
677 /* This functionality has been added mainly for sshd. Sshd
678 calls getpwnam() with the username of the non-privileged
679 user used for privilege separation. This is usually a
680 fixed string "sshd". However, when using usernames from
681 the Windows DBs, it's no safe bet anymore if the username
682 is "sshd", it could also be "DOMAIN+sshd". So what we do
683 here is this:
685 Sshd calls cygwin_internal (CW_CYGNAME_FROM_WINNAME,
686 "sshd",
687 username_buffer,
688 sizeof username_buffer);
690 If this call succeeds, sshd expects the correct Cygwin
691 username of the unprivileged sshd account in username_buffer.
693 The below code checks for a Windows username matching the
694 incoming username, and then fetches the Cygwin username with
695 the matching SID. This is our username used for privsep then.
697 Of course, other applications with similar needs can use the
698 same method. */
699 const char *winname = va_arg (arg, const char *);
700 char *buffer = va_arg (arg, char *);
701 size_t buflen = va_arg (arg, size_t);
703 if (!winname || !buffer || !buflen)
704 break;
706 WCHAR name[UNLEN + 1];
707 sys_mbstowcs (name, sizeof name, winname);
709 cygsid sid;
710 DWORD slen = SECURITY_MAX_SID_SIZE;
711 WCHAR dom[DNLEN + 1];
712 DWORD dlen = DNLEN + 1;
713 SID_NAME_USE acc_type;
715 if (!LookupAccountNameW (NULL, name, sid, &slen, dom, &dlen,
716 &acc_type))
717 break;
719 struct passwd *pw = internal_getpwsid (sid);
720 if (!pw)
721 break;
723 buffer[0] = '\0';
724 strncat (buffer, pw->pw_name, buflen - 1);
725 res = 0;
727 break;
729 case CW_FIXED_ATEXIT:
730 res = 0;
731 break;
733 case CW_EXCEPTION_RECORD_FROM_SIGINFO_T:
735 siginfo_t *si = va_arg(arg, siginfo_t *);
736 EXCEPTION_RECORD *er = va_arg(arg, EXCEPTION_RECORD *);
737 if (si && si->si_cyg && er)
739 memcpy(er, ((cygwin_exception *)si->si_cyg)->exception_record(),
740 sizeof(EXCEPTION_RECORD));
741 res = 0;
744 break;
746 case CW_CYGHEAP_PROFTHR_ALL:
748 typedef void (*func_t) (HANDLE);
749 extern void cygheap_profthr_all (func_t);
751 func_t profthr_byhandle = va_arg(arg, func_t);
752 cygheap_profthr_all (profthr_byhandle);
753 res = 0;
755 break;
757 default:
758 set_errno (ENOSYS);
760 va_end (arg);
761 return res;