PostgreSQL Source Code  git master
exec.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * exec.c
4  * Functions for finding and validating executable files
5  *
6  *
7  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  * src/common/exec.c
13  *
14  *-------------------------------------------------------------------------
15  */
16 
17 #ifndef FRONTEND
18 #include "postgres.h"
19 #else
20 #include "postgres_fe.h"
21 #endif
22 
23 #include <signal.h>
24 #include <sys/stat.h>
25 #include <sys/wait.h>
26 #include <unistd.h>
27 
28 #ifndef FRONTEND
29 /* We use only 3- and 4-parameter elog calls in this file, for simplicity */
30 /* NOTE: caller must provide gettext call around str! */
31 #define log_error(str, param) elog(LOG, str, param)
32 #define log_error4(str, param, arg1) elog(LOG, str, param, arg1)
33 #else
34 #define log_error(str, param) (fprintf(stderr, str, param), fputc('\n', stderr))
35 #define log_error4(str, param, arg1) (fprintf(stderr, str, param, arg1), fputc('\n', stderr))
36 #endif
37 
38 #ifdef _MSC_VER
39 #define getcwd(cwd,len) GetCurrentDirectory(len, cwd)
40 #endif
41 
42 static int validate_exec(const char *path);
43 static int resolve_symlinks(char *path);
44 static char *pipe_read_line(char *cmd, char *line, int maxsize);
45 
46 #ifdef WIN32
47 static BOOL GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser);
48 #endif
49 
50 /*
51  * validate_exec -- validate "path" as an executable file
52  *
53  * returns 0 if the file is found and no error is encountered.
54  * -1 if the regular file "path" does not exist or cannot be executed.
55  * -2 if the file is otherwise valid but cannot be read.
56  */
57 static int
58 validate_exec(const char *path)
59 {
60  struct stat buf;
61  int is_r;
62  int is_x;
63 
64 #ifdef WIN32
65  char path_exe[MAXPGPATH + sizeof(".exe") - 1];
66 
67  /* Win32 requires a .exe suffix for stat() */
68  if (strlen(path) >= strlen(".exe") &&
69  pg_strcasecmp(path + strlen(path) - strlen(".exe"), ".exe") != 0)
70  {
71  strlcpy(path_exe, path, sizeof(path_exe) - 4);
72  strcat(path_exe, ".exe");
73  path = path_exe;
74  }
75 #endif
76 
77  /*
78  * Ensure that the file exists and is a regular file.
79  *
80  * XXX if you have a broken system where stat() looks at the symlink
81  * instead of the underlying file, you lose.
82  */
83  if (stat(path, &buf) < 0)
84  return -1;
85 
86  if (!S_ISREG(buf.st_mode))
87  return -1;
88 
89  /*
90  * Ensure that the file is both executable and readable (required for
91  * dynamic loading).
92  */
93 #ifndef WIN32
94  is_r = (access(path, R_OK) == 0);
95  is_x = (access(path, X_OK) == 0);
96 #else
97  is_r = buf.st_mode & S_IRUSR;
98  is_x = buf.st_mode & S_IXUSR;
99 #endif
100  return is_x ? (is_r ? 0 : -2) : -1;
101 }
102 
103 
104 /*
105  * find_my_exec -- find an absolute path to a valid executable
106  *
107  * argv0 is the name passed on the command line
108  * retpath is the output area (must be of size MAXPGPATH)
109  * Returns 0 if OK, -1 if error.
110  *
111  * The reason we have to work so hard to find an absolute path is that
112  * on some platforms we can't do dynamic loading unless we know the
113  * executable's location. Also, we need a full path not a relative
114  * path because we will later change working directory. Finally, we want
115  * a true path not a symlink location, so that we can locate other files
116  * that are part of our installation relative to the executable.
117  */
118 int
119 find_my_exec(const char *argv0, char *retpath)
120 {
121  char cwd[MAXPGPATH],
122  test_path[MAXPGPATH];
123  char *path;
124 
125  if (!getcwd(cwd, MAXPGPATH))
126  {
127  log_error(_("could not identify current directory: %s"),
128  strerror(errno));
129  return -1;
130  }
131 
132  /*
133  * If argv0 contains a separator, then PATH wasn't used.
134  */
135  if (first_dir_separator(argv0) != NULL)
136  {
137  if (is_absolute_path(argv0))
138  StrNCpy(retpath, argv0, MAXPGPATH);
139  else
140  join_path_components(retpath, cwd, argv0);
141  canonicalize_path(retpath);
142 
143  if (validate_exec(retpath) == 0)
144  return resolve_symlinks(retpath);
145 
146  log_error(_("invalid binary \"%s\""), retpath);
147  return -1;
148  }
149 
150 #ifdef WIN32
151  /* Win32 checks the current directory first for names without slashes */
152  join_path_components(retpath, cwd, argv0);
153  if (validate_exec(retpath) == 0)
154  return resolve_symlinks(retpath);
155 #endif
156 
157  /*
158  * Since no explicit path was supplied, the user must have been relying on
159  * PATH. We'll search the same PATH.
160  */
161  if ((path = getenv("PATH")) && *path)
162  {
163  char *startp = NULL,
164  *endp = NULL;
165 
166  do
167  {
168  if (!startp)
169  startp = path;
170  else
171  startp = endp + 1;
172 
173  endp = first_path_var_separator(startp);
174  if (!endp)
175  endp = startp + strlen(startp); /* point to end */
176 
177  StrNCpy(test_path, startp, Min(endp - startp + 1, MAXPGPATH));
178 
179  if (is_absolute_path(test_path))
180  join_path_components(retpath, test_path, argv0);
181  else
182  {
183  join_path_components(retpath, cwd, test_path);
184  join_path_components(retpath, retpath, argv0);
185  }
186  canonicalize_path(retpath);
187 
188  switch (validate_exec(retpath))
189  {
190  case 0: /* found ok */
191  return resolve_symlinks(retpath);
192  case -1: /* wasn't even a candidate, keep looking */
193  break;
194  case -2: /* found but disqualified */
195  log_error(_("could not read binary \"%s\""),
196  retpath);
197  break;
198  }
199  } while (*endp);
200  }
201 
202  log_error(_("could not find a \"%s\" to execute"), argv0);
203  return -1;
204 }
205 
206 
207 /*
208  * resolve_symlinks - resolve symlinks to the underlying file
209  *
210  * Replace "path" by the absolute path to the referenced file.
211  *
212  * Returns 0 if OK, -1 if error.
213  *
214  * Note: we are not particularly tense about producing nice error messages
215  * because we are not really expecting error here; we just determined that
216  * the symlink does point to a valid executable.
217  */
218 static int
219 resolve_symlinks(char *path)
220 {
221 #ifdef HAVE_READLINK
222  struct stat buf;
223  char orig_wd[MAXPGPATH],
224  link_buf[MAXPGPATH];
225  char *fname;
226 
227  /*
228  * To resolve a symlink properly, we have to chdir into its directory and
229  * then chdir to where the symlink points; otherwise we may fail to
230  * resolve relative links correctly (consider cases involving mount
231  * points, for example). After following the final symlink, we use
232  * getcwd() to figure out where the heck we're at.
233  *
234  * One might think we could skip all this if path doesn't point to a
235  * symlink to start with, but that's wrong. We also want to get rid of
236  * any directory symlinks that are present in the given path. We expect
237  * getcwd() to give us an accurate, symlink-free path.
238  */
239  if (!getcwd(orig_wd, MAXPGPATH))
240  {
241  log_error(_("could not identify current directory: %s"),
242  strerror(errno));
243  return -1;
244  }
245 
246  for (;;)
247  {
248  char *lsep;
249  int rllen;
250 
251  lsep = last_dir_separator(path);
252  if (lsep)
253  {
254  *lsep = '\0';
255  if (chdir(path) == -1)
256  {
257  log_error4(_("could not change directory to \"%s\": %s"), path, strerror(errno));
258  return -1;
259  }
260  fname = lsep + 1;
261  }
262  else
263  fname = path;
264 
265  if (lstat(fname, &buf) < 0 ||
266  !S_ISLNK(buf.st_mode))
267  break;
268 
269  rllen = readlink(fname, link_buf, sizeof(link_buf));
270  if (rllen < 0 || rllen >= sizeof(link_buf))
271  {
272  log_error(_("could not read symbolic link \"%s\""), fname);
273  return -1;
274  }
275  link_buf[rllen] = '\0';
276  strcpy(path, link_buf);
277  }
278 
279  /* must copy final component out of 'path' temporarily */
280  strlcpy(link_buf, fname, sizeof(link_buf));
281 
282  if (!getcwd(path, MAXPGPATH))
283  {
284  log_error(_("could not identify current directory: %s"),
285  strerror(errno));
286  return -1;
287  }
288  join_path_components(path, path, link_buf);
289  canonicalize_path(path);
290 
291  if (chdir(orig_wd) == -1)
292  {
293  log_error4(_("could not change directory to \"%s\": %s"), orig_wd, strerror(errno));
294  return -1;
295  }
296 #endif /* HAVE_READLINK */
297 
298  return 0;
299 }
300 
301 
302 /*
303  * Find another program in our binary's directory,
304  * then make sure it is the proper version.
305  */
306 int
307 find_other_exec(const char *argv0, const char *target,
308  const char *versionstr, char *retpath)
309 {
310  char cmd[MAXPGPATH];
311  char line[100];
312 
313  if (find_my_exec(argv0, retpath) < 0)
314  return -1;
315 
316  /* Trim off program name and keep just directory */
317  *last_dir_separator(retpath) = '\0';
318  canonicalize_path(retpath);
319 
320  /* Now append the other program's name */
321  snprintf(retpath + strlen(retpath), MAXPGPATH - strlen(retpath),
322  "/%s%s", target, EXE);
323 
324  if (validate_exec(retpath) != 0)
325  return -1;
326 
327  snprintf(cmd, sizeof(cmd), "\"%s\" -V", retpath);
328 
329  if (!pipe_read_line(cmd, line, sizeof(line)))
330  return -1;
331 
332  if (strcmp(line, versionstr) != 0)
333  return -2;
334 
335  return 0;
336 }
337 
338 
339 /*
340  * The runtime library's popen() on win32 does not work when being
341  * called from a service when running on windows <= 2000, because
342  * there is no stdin/stdout/stderr.
343  *
344  * Executing a command in a pipe and reading the first line from it
345  * is all we need.
346  */
347 static char *
348 pipe_read_line(char *cmd, char *line, int maxsize)
349 {
350 #ifndef WIN32
351  FILE *pgver;
352 
353  /* flush output buffers in case popen does not... */
354  fflush(stdout);
355  fflush(stderr);
356 
357  errno = 0;
358  if ((pgver = popen(cmd, "r")) == NULL)
359  {
360  perror("popen failure");
361  return NULL;
362  }
363 
364  errno = 0;
365  if (fgets(line, maxsize, pgver) == NULL)
366  {
367  if (feof(pgver))
368  fprintf(stderr, "no data was returned by command \"%s\"\n", cmd);
369  else
370  perror("fgets failure");
371  pclose(pgver); /* no error checking */
372  return NULL;
373  }
374 
375  if (pclose_check(pgver))
376  return NULL;
377 
378  return line;
379 #else /* WIN32 */
380 
381  SECURITY_ATTRIBUTES sattr;
382  HANDLE childstdoutrd,
383  childstdoutwr,
384  childstdoutrddup;
385  PROCESS_INFORMATION pi;
386  STARTUPINFO si;
387  char *retval = NULL;
388 
389  sattr.nLength = sizeof(SECURITY_ATTRIBUTES);
390  sattr.bInheritHandle = TRUE;
391  sattr.lpSecurityDescriptor = NULL;
392 
393  if (!CreatePipe(&childstdoutrd, &childstdoutwr, &sattr, 0))
394  return NULL;
395 
396  if (!DuplicateHandle(GetCurrentProcess(),
397  childstdoutrd,
398  GetCurrentProcess(),
399  &childstdoutrddup,
400  0,
401  FALSE,
402  DUPLICATE_SAME_ACCESS))
403  {
404  CloseHandle(childstdoutrd);
405  CloseHandle(childstdoutwr);
406  return NULL;
407  }
408 
409  CloseHandle(childstdoutrd);
410 
411  ZeroMemory(&pi, sizeof(pi));
412  ZeroMemory(&si, sizeof(si));
413  si.cb = sizeof(si);
414  si.dwFlags = STARTF_USESTDHANDLES;
415  si.hStdError = childstdoutwr;
416  si.hStdOutput = childstdoutwr;
417  si.hStdInput = INVALID_HANDLE_VALUE;
418 
419  if (CreateProcess(NULL,
420  cmd,
421  NULL,
422  NULL,
423  TRUE,
424  0,
425  NULL,
426  NULL,
427  &si,
428  &pi))
429  {
430  /* Successfully started the process */
431  char *lineptr;
432 
433  ZeroMemory(line, maxsize);
434 
435  /* Try to read at least one line from the pipe */
436  /* This may require more than one wait/read attempt */
437  for (lineptr = line; lineptr < line + maxsize - 1;)
438  {
439  DWORD bytesread = 0;
440 
441  /* Let's see if we can read */
442  if (WaitForSingleObject(childstdoutrddup, 10000) != WAIT_OBJECT_0)
443  break; /* Timeout, but perhaps we got a line already */
444 
445  if (!ReadFile(childstdoutrddup, lineptr, maxsize - (lineptr - line),
446  &bytesread, NULL))
447  break; /* Error, but perhaps we got a line already */
448 
449  lineptr += strlen(lineptr);
450 
451  if (!bytesread)
452  break; /* EOF */
453 
454  if (strchr(line, '\n'))
455  break; /* One or more lines read */
456  }
457 
458  if (lineptr != line)
459  {
460  /* OK, we read some data */
461  int len;
462 
463  /* If we got more than one line, cut off after the first \n */
464  lineptr = strchr(line, '\n');
465  if (lineptr)
466  *(lineptr + 1) = '\0';
467 
468  len = strlen(line);
469 
470  /*
471  * If EOL is \r\n, convert to just \n. Because stdout is a
472  * text-mode stream, the \n output by the child process is
473  * received as \r\n, so we convert it to \n. The server main.c
474  * sets setvbuf(stdout, NULL, _IONBF, 0) which has the effect of
475  * disabling \n to \r\n expansion for stdout.
476  */
477  if (len >= 2 && line[len - 2] == '\r' && line[len - 1] == '\n')
478  {
479  line[len - 2] = '\n';
480  line[len - 1] = '\0';
481  len--;
482  }
483 
484  /*
485  * We emulate fgets() behaviour. So if there is no newline at the
486  * end, we add one...
487  */
488  if (len == 0 || line[len - 1] != '\n')
489  strcat(line, "\n");
490 
491  retval = line;
492  }
493 
494  CloseHandle(pi.hProcess);
495  CloseHandle(pi.hThread);
496  }
497 
498  CloseHandle(childstdoutwr);
499  CloseHandle(childstdoutrddup);
500 
501  return retval;
502 #endif /* WIN32 */
503 }
504 
505 
506 /*
507  * pclose() plus useful error reporting
508  */
509 int
510 pclose_check(FILE *stream)
511 {
512  int exitstatus;
513  char *reason;
514 
515  exitstatus = pclose(stream);
516 
517  if (exitstatus == 0)
518  return 0; /* all is well */
519 
520  if (exitstatus == -1)
521  {
522  /* pclose() itself failed, and hopefully set errno */
523  log_error(_("pclose failed: %s"), strerror(errno));
524  }
525  else
526  {
527  reason = wait_result_to_str(exitstatus);
528  log_error("%s", reason);
529 #ifdef FRONTEND
530  free(reason);
531 #else
532  pfree(reason);
533 #endif
534  }
535  return exitstatus;
536 }
537 
538 /*
539  * set_pglocale_pgservice
540  *
541  * Set application-specific locale and service directory
542  *
543  * This function takes the value of argv[0] rather than a full path.
544  *
545  * (You may be wondering why this is in exec.c. It requires this module's
546  * services and doesn't introduce any new dependencies, so this seems as
547  * good as anyplace.)
548  */
549 void
550 set_pglocale_pgservice(const char *argv0, const char *app)
551 {
552  char path[MAXPGPATH];
553  char my_exec_path[MAXPGPATH];
554  char env_path[MAXPGPATH + sizeof("PGSYSCONFDIR=")]; /* longer than
555  * PGLOCALEDIR */
556  char *dup_path;
557 
558  /* don't set LC_ALL in the backend */
559  if (strcmp(app, PG_TEXTDOMAIN("postgres")) != 0)
560  {
561  setlocale(LC_ALL, "");
562 
563  /*
564  * One could make a case for reproducing here PostmasterMain()'s test
565  * for whether the process is multithreaded. Unlike the postmaster,
566  * no frontend program calls sigprocmask() or otherwise provides for
567  * mutual exclusion between signal handlers. While frontends using
568  * fork(), if multithreaded, are formally exposed to undefined
569  * behavior, we have not witnessed a concrete bug. Therefore,
570  * complaining about multithreading here may be mere pedantry.
571  */
572  }
573 
574  if (find_my_exec(argv0, my_exec_path) < 0)
575  return;
576 
577 #ifdef ENABLE_NLS
578  get_locale_path(my_exec_path, path);
579  bindtextdomain(app, path);
580  textdomain(app);
581 
582  if (getenv("PGLOCALEDIR") == NULL)
583  {
584  /* set for libpq to use */
585  snprintf(env_path, sizeof(env_path), "PGLOCALEDIR=%s", path);
586  canonicalize_path(env_path + 12);
587  dup_path = strdup(env_path);
588  if (dup_path)
589  putenv(dup_path);
590  }
591 #endif
592 
593  if (getenv("PGSYSCONFDIR") == NULL)
594  {
595  get_etc_path(my_exec_path, path);
596 
597  /* set for libpq to use */
598  snprintf(env_path, sizeof(env_path), "PGSYSCONFDIR=%s", path);
599  canonicalize_path(env_path + 13);
600  dup_path = strdup(env_path);
601  if (dup_path)
602  putenv(dup_path);
603  }
604 }
605 
606 #ifdef WIN32
607 
608 /*
609  * AddUserToTokenDacl(HANDLE hToken)
610  *
611  * This function adds the current user account to the restricted
612  * token used when we create a restricted process.
613  *
614  * This is required because of some security changes in Windows
615  * that appeared in patches to XP/2K3 and in Vista/2008.
616  *
617  * On these machines, the Administrator account is not included in
618  * the default DACL - you just get Administrators + System. For
619  * regular users you get User + System. Because we strip Administrators
620  * when we create the restricted token, we are left with only System
621  * in the DACL which leads to access denied errors for later CreatePipe()
622  * and CreateProcess() calls when running as Administrator.
623  *
624  * This function fixes this problem by modifying the DACL of the
625  * token the process will use, and explicitly re-adding the current
626  * user account. This is still secure because the Administrator account
627  * inherits its privileges from the Administrators group - it doesn't
628  * have any of its own.
629  */
630 BOOL
631 AddUserToTokenDacl(HANDLE hToken)
632 {
633  int i;
634  ACL_SIZE_INFORMATION asi;
635  ACCESS_ALLOWED_ACE *pace;
636  DWORD dwNewAclSize;
637  DWORD dwSize = 0;
638  DWORD dwTokenInfoLength = 0;
639  PACL pacl = NULL;
640  PTOKEN_USER pTokenUser = NULL;
641  TOKEN_DEFAULT_DACL tddNew;
642  TOKEN_DEFAULT_DACL *ptdd = NULL;
643  TOKEN_INFORMATION_CLASS tic = TokenDefaultDacl;
644  BOOL ret = FALSE;
645 
646  /* Figure out the buffer size for the DACL info */
647  if (!GetTokenInformation(hToken, tic, (LPVOID) NULL, dwTokenInfoLength, &dwSize))
648  {
649  if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
650  {
651  ptdd = (TOKEN_DEFAULT_DACL *) LocalAlloc(LPTR, dwSize);
652  if (ptdd == NULL)
653  {
654  log_error("could not allocate %lu bytes of memory", dwSize);
655  goto cleanup;
656  }
657 
658  if (!GetTokenInformation(hToken, tic, (LPVOID) ptdd, dwSize, &dwSize))
659  {
660  log_error("could not get token information: error code %lu", GetLastError());
661  goto cleanup;
662  }
663  }
664  else
665  {
666  log_error("could not get token information buffer size: error code %lu", GetLastError());
667  goto cleanup;
668  }
669  }
670 
671  /* Get the ACL info */
672  if (!GetAclInformation(ptdd->DefaultDacl, (LPVOID) &asi,
673  (DWORD) sizeof(ACL_SIZE_INFORMATION),
674  AclSizeInformation))
675  {
676  log_error("could not get ACL information: error code %lu", GetLastError());
677  goto cleanup;
678  }
679 
680  /* Get the current user SID */
681  if (!GetTokenUser(hToken, &pTokenUser))
682  goto cleanup; /* callee printed a message */
683 
684  /* Figure out the size of the new ACL */
685  dwNewAclSize = asi.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) +
686  GetLengthSid(pTokenUser->User.Sid) - sizeof(DWORD);
687 
688  /* Allocate the ACL buffer & initialize it */
689  pacl = (PACL) LocalAlloc(LPTR, dwNewAclSize);
690  if (pacl == NULL)
691  {
692  log_error("could not allocate %lu bytes of memory", dwNewAclSize);
693  goto cleanup;
694  }
695 
696  if (!InitializeAcl(pacl, dwNewAclSize, ACL_REVISION))
697  {
698  log_error("could not initialize ACL: error code %lu", GetLastError());
699  goto cleanup;
700  }
701 
702  /* Loop through the existing ACEs, and build the new ACL */
703  for (i = 0; i < (int) asi.AceCount; i++)
704  {
705  if (!GetAce(ptdd->DefaultDacl, i, (LPVOID *) &pace))
706  {
707  log_error("could not get ACE: error code %lu", GetLastError());
708  goto cleanup;
709  }
710 
711  if (!AddAce(pacl, ACL_REVISION, MAXDWORD, pace, ((PACE_HEADER) pace)->AceSize))
712  {
713  log_error("could not add ACE: error code %lu", GetLastError());
714  goto cleanup;
715  }
716  }
717 
718  /* Add the new ACE for the current user */
719  if (!AddAccessAllowedAceEx(pacl, ACL_REVISION, OBJECT_INHERIT_ACE, GENERIC_ALL, pTokenUser->User.Sid))
720  {
721  log_error("could not add access allowed ACE: error code %lu", GetLastError());
722  goto cleanup;
723  }
724 
725  /* Set the new DACL in the token */
726  tddNew.DefaultDacl = pacl;
727 
728  if (!SetTokenInformation(hToken, tic, (LPVOID) &tddNew, dwNewAclSize))
729  {
730  log_error("could not set token information: error code %lu", GetLastError());
731  goto cleanup;
732  }
733 
734  ret = TRUE;
735 
736 cleanup:
737  if (pTokenUser)
738  LocalFree((HLOCAL) pTokenUser);
739 
740  if (pacl)
741  LocalFree((HLOCAL) pacl);
742 
743  if (ptdd)
744  LocalFree((HLOCAL) ptdd);
745 
746  return ret;
747 }
748 
749 /*
750  * GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
751  *
752  * Get the users token information from a process token.
753  *
754  * The caller of this function is responsible for calling LocalFree() on the
755  * returned TOKEN_USER memory.
756  */
757 static BOOL
758 GetTokenUser(HANDLE hToken, PTOKEN_USER *ppTokenUser)
759 {
760  DWORD dwLength;
761 
762  *ppTokenUser = NULL;
763 
764  if (!GetTokenInformation(hToken,
765  TokenUser,
766  NULL,
767  0,
768  &dwLength))
769  {
770  if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
771  {
772  *ppTokenUser = (PTOKEN_USER) LocalAlloc(LPTR, dwLength);
773 
774  if (*ppTokenUser == NULL)
775  {
776  log_error("could not allocate %lu bytes of memory", dwLength);
777  return FALSE;
778  }
779  }
780  else
781  {
782  log_error("could not get token information buffer size: error code %lu", GetLastError());
783  return FALSE;
784  }
785  }
786 
787  if (!GetTokenInformation(hToken,
788  TokenUser,
789  *ppTokenUser,
790  dwLength,
791  &dwLength))
792  {
793  LocalFree(*ppTokenUser);
794  *ppTokenUser = NULL;
795 
796  log_error("could not get token information: error code %lu", GetLastError());
797  return FALSE;
798  }
799 
800  /* Memory in *ppTokenUser is LocalFree():d by the caller */
801  return TRUE;
802 }
803 
804 #endif
int find_other_exec(const char *argv0, const char *target, const char *versionstr, char *retpath)
Definition: exec.c:307
#define TRUE
Definition: ecpglib.h:35
static char * argv0
Definition: pg_ctl.c:93
#define log_error(str, param)
Definition: exec.c:31
static int resolve_symlinks(char *path)
Definition: exec.c:219
#define setlocale(a, b)
Definition: win32_port.h:419
#define FALSE
Definition: ecpglib.h:39
#define Min(x, y)
Definition: c.h:802
void canonicalize_path(char *path)
Definition: path.c:254
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
BOOL AddUserToTokenDacl(HANDLE hToken)
#define S_IXUSR
Definition: win32_port.h:277
char * wait_result_to_str(int exitstatus)
Definition: wait_error.c:32
int pclose_check(FILE *stream)
Definition: exec.c:510
void pfree(void *pointer)
Definition: mcxt.c:949
#define MAXPGPATH
static int validate_exec(const char *path)
Definition: exec.c:58
#define log_error4(str, param, arg1)
Definition: exec.c:32
int find_my_exec(const char *argv0, char *retpath)
Definition: exec.c:119
#define readlink(path, buf, size)
Definition: win32_port.h:233
#define is_absolute_path(filename)
Definition: port.h:86
#define S_ISREG(m)
Definition: win32_port.h:310
#define stat(a, b)
Definition: win32_port.h:266
char my_exec_path[MAXPGPATH]
Definition: globals.c:64
#define PG_TEXTDOMAIN(domain)
Definition: c.h:999
char * first_dir_separator(const char *filename)
Definition: path.c:103
static void cleanup(void)
Definition: bootstrap.c:873
char * last_dir_separator(const char *filename)
Definition: path.c:138
void get_etc_path(const char *my_exec_path, char *ret_path)
Definition: path.c:713
#define free(a)
Definition: header.h:65
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
#define StrNCpy(dst, src, len)
Definition: c.h:826
#define putenv(x)
Definition: win32_port.h:487
void join_path_components(char *ret_path, const char *head, const char *tail)
Definition: path.c:218
#define S_IRUSR
Definition: win32_port.h:271
#define lstat(path, sb)
Definition: win32_port.h:255
void set_pglocale_pgservice(const char *argv0, const char *app)
Definition: exec.c:550
int i
const char * strerror(int errnum)
Definition: strerror.c:19
#define EXE
Definition: port.h:117
void get_locale_path(const char *my_exec_path, char *ret_path)
Definition: path.c:767
#define _(x)
Definition: elog.c:84
char * first_path_var_separator(const char *pathlist)
Definition: path.c:120
static char * pipe_read_line(char *cmd, char *line, int maxsize)
Definition: exec.c:348