OpenVPN
platform.c
Go to the documentation of this file.
1 /*
2  * OpenVPN -- An application to securely tunnel IP networks
3  * over a single TCP/UDP port, with support for SSL/TLS-based
4  * session authentication and key exchange,
5  * packet encryption, packet authentication, and
6  * packet compression.
7  *
8  * Copyright (C) 2002-2024 OpenVPN Inc <sales@openvpn.net>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2
12  * as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include "syshead.h"
29 
30 #include "openvpn.h"
31 #include "options.h"
32 
33 #include "buffer.h"
34 #include "crypto.h"
35 #include "error.h"
36 #include "misc.h"
37 #include "win32.h"
38 
39 #include "memdbg.h"
40 
41 #include "platform.h"
42 
43 #if _WIN32
44 #include <direct.h>
45 #endif
46 
47 #ifdef HAVE_LIBCAPNG
48 #include <cap-ng.h>
49 #include <sys/prctl.h>
50 #endif
51 
52 /* Redefine the top level directory of the filesystem
53  * to restrict access to files for security */
54 void
55 platform_chroot(const char *path)
56 {
57  if (path)
58  {
59 #ifdef HAVE_CHROOT
60  const char *top = "/";
61  if (chroot(path))
62  {
63  msg(M_ERR, "chroot to '%s' failed", path);
64  }
65  if (platform_chdir(top))
66  {
67  msg(M_ERR, "cd to '%s' failed", top);
68  }
69  msg(M_INFO, "chroot to '%s' and cd to '%s' succeeded", path, top);
70 #else /* ifdef HAVE_CHROOT */
71  msg(M_FATAL, "Sorry but I can't chroot to '%s' because this operating system doesn't appear to support the chroot() system call", path);
72 #endif
73  }
74 }
75 
76 /* Get/Set UID of process */
77 
78 bool
79 platform_user_get(const char *username, struct platform_state_user *state)
80 {
81  bool ret = false;
82  CLEAR(*state);
83  if (username)
84  {
85 #if defined(HAVE_GETPWNAM) && defined(HAVE_SETUID)
86  state->uid = -1;
87  const struct passwd *pw = getpwnam(username);
88  if (!pw)
89  {
90  msg(M_ERR, "failed to find UID for user %s", username);
91  }
92  else
93  {
94  state->uid = pw->pw_uid;
95  }
96  state->username = username;
97  ret = true;
98 #else /* if defined(HAVE_GETPWNAM) && defined(HAVE_SETUID) */
99  msg(M_FATAL, "cannot get UID for user %s -- platform lacks getpwname() or setuid() system calls", username);
100 #endif
101  }
102  return ret;
103 }
104 
105 static void
107 {
108 #if defined(HAVE_GETPWNAM) && defined(HAVE_SETUID)
109  if (state->username && state->uid >= 0)
110  {
111  if (setuid(state->uid))
112  {
113  msg(M_ERR, "setuid('%s') failed", state->username);
114  }
115  msg(M_INFO, "UID set to %s", state->username);
116  }
117 #endif
118 }
119 
120 /* Get/Set GID of process */
121 
122 bool
123 platform_group_get(const char *groupname, struct platform_state_group *state)
124 {
125  bool ret = false;
126  CLEAR(*state);
127  if (groupname)
128  {
129 #if defined(HAVE_GETGRNAM) && defined(HAVE_SETGID)
130  state->gid = -1;
131  const struct group *gr = getgrnam(groupname);
132  if (!gr)
133  {
134  msg(M_ERR, "failed to find GID for group %s", groupname);
135  }
136  else
137  {
138  state->gid = gr->gr_gid;
139  }
140  state->groupname = groupname;
141  ret = true;
142 #else /* if defined(HAVE_GETGRNAM) && defined(HAVE_SETGID) */
143  msg(M_FATAL, "cannot get GID for group %s -- platform lacks getgrnam() or setgid() system calls", groupname);
144 #endif
145  }
146  return ret;
147 }
148 
149 static void
151 {
152 #if defined(HAVE_GETGRNAM) && defined(HAVE_SETGID)
153  if (state->groupname && state->gid >= 0)
154  {
155  if (setgid(state->gid))
156  {
157  msg(M_ERR, "setgid('%s') failed", state->groupname);
158  }
159  msg(M_INFO, "GID set to %s", state->groupname);
160 #ifdef HAVE_SETGROUPS
161  {
162  gid_t gr_list[1];
163  gr_list[0] = state->gid;
164  if (setgroups(1, gr_list))
165  {
166  msg(M_ERR, "setgroups('%s') failed", state->groupname);
167  }
168  }
169 #endif
170  }
171 #endif
172 }
173 
174 /*
175  * Determine if we need to retain process capabilities. DCO and SITNL need it.
176  * Enforce it for DCO, but only try and soft-fail for SITNL to keep backwards compat.
177  *
178  * Returns the tri-state expected by platform_user_group_set.
179  * -1: try to keep caps, but continue if impossible
180  * 0: don't keep caps
181  * 1: keep caps, fail hard if impossible
182  */
183 static int
185 {
186  if (!c)
187  {
188  return -1;
189  }
190 
191  if (dco_enabled(&c->options))
192  {
193 #ifdef TARGET_LINUX
194  /* DCO on Linux does not work at all without CAP_NET_ADMIN */
195  return 1;
196 #else
197  /* Windows/BSD/... has no equivalent capability mechanism */
198  return -1;
199 #endif
200  }
201 
202 #ifdef ENABLE_SITNL
203  return -1;
204 #else
205  return 0;
206 #endif
207 }
208 
209 /* Set user and group, retaining neccesary capabilities required by the platform.
210  *
211  * The keep_caps argument has 3 possible states:
212  * >0: Retain capabilities, and fail hard on failure to do so.
213  * ==0: Don't attempt to retain any capabilities, just sitch user/group.
214  * <0: Try to retain capabilities, but continue on failure.
215  */
216 void
218  const struct platform_state_group *group_state,
219  struct context *c)
220 {
221  int keep_caps = need_keep_caps(c);
222  unsigned int err_flags = (keep_caps > 0) ? M_FATAL : M_NONFATAL;
223 #ifdef HAVE_LIBCAPNG
224  int new_gid = -1, new_uid = -1;
225  int res;
226 
227  if (keep_caps == 0)
228  {
229  goto fallback;
230  }
231 
232  /*
233  * new_uid/new_gid defaults to -1, which will not make
234  * libcap-ng change the UID/GID unless configured
235  */
236  if (group_state->groupname && group_state->gid >= 0)
237  {
238  new_gid = group_state->gid;
239  }
240  if (user_state->username && user_state->uid >= 0)
241  {
242  new_uid = user_state->uid;
243  }
244 
245  /* Prepare capabilities before dropping UID/GID */
246  capng_clear(CAPNG_SELECT_BOTH);
247  res = capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, CAP_NET_ADMIN);
248  if (res < 0)
249  {
250  msg(err_flags, "capng_update(CAP_NET_ADMIN) failed: %d", res);
251  goto fallback;
252  }
253 
254  /* Change to new UID/GID.
255  * capng_change_id() internally calls capng_apply() to apply prepared capabilities.
256  */
257  res = capng_change_id(new_uid, new_gid, CAPNG_DROP_SUPP_GRP);
258  if (res == -4 || res == -6)
259  {
260  /* -4 and -6 mean failure of setuid/gid respectively.
261  * There is no point for us to continue if those failed. */
262  msg(M_ERR, "capng_change_id('%s','%s') failed: %d",
263  user_state->username, group_state->groupname, res);
264  }
265  else if (res == -3)
266  {
267  msg(M_NONFATAL | M_ERRNO, "capng_change_id() failed applying capabilities");
268  msg(err_flags, "NOTE: previous error likely due to missing capability CAP_SETPCAP.");
269  goto fallback;
270  }
271  else if (res < 0)
272  {
273  msg(err_flags | M_ERRNO, "capng_change_id('%s','%s') failed retaining capabilities: %d",
274  user_state->username, group_state->groupname, res);
275  goto fallback;
276  }
277 
278  if (new_uid >= 0)
279  {
280  msg(M_INFO, "UID set to %s", user_state->username);
281  }
282  if (new_gid >= 0)
283  {
284  msg(M_INFO, "GID set to %s", group_state->groupname);
285  }
286 
287  msg(M_INFO, "Capabilities retained: CAP_NET_ADMIN");
288  return;
289 
290 fallback:
291  /* capng_change_id() can leave this flag clobbered on failure
292  * This is working around a bug in libcap-ng, which can leave the flag set
293  * on failure: https://github.com/stevegrubb/libcap-ng/issues/33 */
294  if (prctl(PR_GET_KEEPCAPS) && prctl(PR_SET_KEEPCAPS, 0) < 0)
295  {
296  msg(M_ERR, "Clearing KEEPCAPS flag failed");
297  }
298 #endif /* HAVE_LIBCAPNG */
299 
300  if (keep_caps)
301  {
302  msg(err_flags, "Unable to retain capabilities");
303  }
304 
305  platform_group_set(group_state);
306  platform_user_set(user_state);
307 }
308 
309 /* Change process priority */
310 void
311 platform_nice(int niceval)
312 {
313  if (niceval)
314  {
315 #ifdef HAVE_NICE
316  errno = 0;
317  if (nice(niceval) < 0 && errno != 0)
318  {
319  msg(M_WARN | M_ERRNO, "WARNING: nice %d failed", niceval);
320  }
321  else
322  {
323  msg(M_INFO, "nice %d succeeded", niceval);
324  }
325 #else /* ifdef HAVE_NICE */
326  msg(M_WARN, "WARNING: nice %d failed (function not implemented)", niceval);
327 #endif
328  }
329 }
330 
331 /* Get current PID */
332 unsigned int
334 {
335 #ifdef _WIN32
336  return (unsigned int) GetCurrentProcessId();
337 #else
338  return (unsigned int) getpid();
339 #endif
340 }
341 
342 /* Disable paging */
343 void
344 platform_mlockall(bool print_msg)
345 {
346 #ifdef HAVE_MLOCKALL
347 
348 #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_MEMLOCK)
349 #define MIN_LOCKED_MEM_MB 100
350  struct rlimit rl;
351  if (getrlimit(RLIMIT_MEMLOCK, &rl) < 0)
352  {
353  msg(M_WARN | M_ERRNO, "WARNING: getrlimit(RLIMIT_MEMLOCK) failed");
354  }
355  else
356  {
357  msg(M_INFO, "mlock: MEMLOCK limit: soft=%ld KB, hard=%ld KB",
358  ((long int) rl.rlim_cur) / 1024, ((long int) rl.rlim_max) / 1024);
359  if (rl.rlim_cur < MIN_LOCKED_MEM_MB*1024*1024)
360  {
361  msg(M_INFO, "mlock: RLIMIT_MEMLOCK < %d MB, increase limit",
362  MIN_LOCKED_MEM_MB);
363  rl.rlim_cur = MIN_LOCKED_MEM_MB*1024*1024;
364  if (rl.rlim_max < rl.rlim_cur)
365  {
366  rl.rlim_max = rl.rlim_cur;
367  }
368  if (setrlimit(RLIMIT_MEMLOCK, &rl) < 0)
369  {
370  msg(M_FATAL | M_ERRNO, "ERROR: setrlimit() failed");
371  }
372  }
373  }
374 #endif /* if defined(HAVE_GETRLIMIT) && defined(RLIMIT_MEMLOCK) */
375 
376  if (mlockall(MCL_CURRENT | MCL_FUTURE))
377  {
378  msg(M_WARN | M_ERRNO, "WARNING: mlockall call failed");
379  }
380  else if (print_msg)
381  {
382  msg(M_INFO, "mlockall call succeeded");
383  }
384 #else /* ifdef HAVE_MLOCKALL */
385  msg(M_WARN, "WARNING: mlockall call failed (function not implemented)");
386 #endif /* ifdef HAVE_MLOCKALL */
387 }
388 
389 /*
390  * Wrapper for chdir library function
391  */
392 int
393 platform_chdir(const char *dir)
394 {
395 #ifdef _WIN32
396  int res;
397  struct gc_arena gc = gc_new();
398  res = _wchdir(wide_string(dir, &gc));
399  gc_free(&gc);
400  return res;
401 #else /* ifdef _WIN32 */
402 #ifdef HAVE_CHDIR
403  return chdir(dir);
404 #else /* ifdef HAVE_CHDIR */
405  return -1;
406 #endif
407 #endif
408 }
409 
410 /*
411  * convert execve() return into a success/failure value
412  */
413 bool
415 {
416 #ifdef _WIN32
417  return stat == 0;
418 #else
419  return stat != -1 && WIFEXITED(stat) && WEXITSTATUS(stat) == 0;
420 #endif
421 }
422 
423 #ifdef _WIN32
424 int
426 {
427  if (stat >= 0 && stat < 255)
428  {
429  return stat;
430  }
431  else
432  {
433  return -1;
434  }
435 }
436 #else /* ifdef _WIN32 */
437 int
438 platform_ret_code(int stat)
439 {
440  if (!WIFEXITED(stat) || stat == -1)
441  {
442  return -1;
443  }
444 
445  int status = WEXITSTATUS(stat);
446  if (status >= 0 && status < 255)
447  {
448  return status;
449  }
450  else
451  {
452  return -1;
453  }
454 }
455 #endif /* ifdef _WIN32 */
456 
457 int
458 platform_access(const char *path, int mode)
459 {
460 #ifdef _WIN32
461  struct gc_arena gc = gc_new();
462  int ret = _waccess(wide_string(path, &gc), mode & ~X_OK);
463  gc_free(&gc);
464  return ret;
465 #else
466  return access(path, mode);
467 #endif
468 }
469 
470 /*
471  * Go to sleep for n milliseconds.
472  */
473 void
475 {
476 #ifdef _WIN32
477  Sleep(n);
478 #else
479  struct timeval tv;
480  tv.tv_sec = n / 1000;
481  tv.tv_usec = (n % 1000) * 1000;
482  select(0, NULL, NULL, NULL, &tv);
483 #endif
484 }
485 
486 /*
487  * Go to sleep indefinitely.
488  */
489 void
491 {
492 #ifdef _WIN32
493  ASSERT(0);
494 #else
495  select(0, NULL, NULL, NULL, NULL);
496 #endif
497 }
498 
499 /* delete a file, return true if succeeded */
500 bool
501 platform_unlink(const char *filename)
502 {
503 #if defined(_WIN32)
504  struct gc_arena gc = gc_new();
505  BOOL ret = DeleteFileW(wide_string(filename, &gc));
506  gc_free(&gc);
507  return (ret != 0);
508 #else
509  return (unlink(filename) == 0);
510 #endif
511 }
512 
513 FILE *
514 platform_fopen(const char *path, const char *mode)
515 {
516 #ifdef _WIN32
517  struct gc_arena gc = gc_new();
518  FILE *f = _wfopen(wide_string(path, &gc), wide_string(mode, &gc));
519  gc_free(&gc);
520  return f;
521 #else
522  return fopen(path, mode);
523 #endif
524 }
525 
526 int
527 platform_open(const char *path, int flags, int mode)
528 {
529 #ifdef _WIN32
530  struct gc_arena gc = gc_new();
531  int fd = _wopen(wide_string(path, &gc), flags, mode);
532  gc_free(&gc);
533  return fd;
534 #else
535  return open(path, flags, mode);
536 #endif
537 }
538 
539 int
540 platform_stat(const char *path, platform_stat_t *buf)
541 {
542 #ifdef _WIN32
543  struct gc_arena gc = gc_new();
544  int res = _wstat(wide_string(path, &gc), buf);
545  gc_free(&gc);
546  return res;
547 #else
548  return stat(path, buf);
549 #endif
550 }
551 
552 /* create a temporary filename in directory */
553 const char *
554 platform_create_temp_file(const char *directory, const char *prefix, struct gc_arena *gc)
555 {
556  int fd;
557  const char *retfname = NULL;
558  unsigned int attempts = 0;
559  char fname[256] = { 0 };
560  const char *fname_fmt = PACKAGE "_%.*s_%08lx%08lx.tmp";
561  const int max_prefix_len = sizeof(fname) - (sizeof(PACKAGE) + 7 + (2 * 8));
562 
563  while (attempts < 6)
564  {
565  ++attempts;
566 
567  if (!snprintf(fname, sizeof(fname), fname_fmt, max_prefix_len,
568  prefix, (unsigned long) get_random(),
569  (unsigned long) get_random()))
570  {
571  msg(M_WARN, "ERROR: temporary filename too long");
572  return NULL;
573  }
574 
575  retfname = platform_gen_path(directory, fname, gc);
576  if (!retfname)
577  {
578  msg(M_WARN, "Failed to create temporary filename and path");
579  return NULL;
580  }
581 
582  /* Atomically create the file. Errors out if the file already
583  * exists. */
584  fd = platform_open(retfname, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR);
585  if (fd != -1)
586  {
587  close(fd);
588  return retfname;
589  }
590  else if (fd == -1 && errno != EEXIST)
591  {
592  /* Something else went wrong, no need to retry. */
593  msg(M_WARN | M_ERRNO, "Could not create temporary file '%s'",
594  retfname);
595  return NULL;
596  }
597  }
598 
599  msg(M_WARN, "Failed to create temporary file after %i attempts", attempts);
600  return NULL;
601 }
602 
603 /*
604  * Put a directory and filename together.
605  */
606 const char *
607 platform_gen_path(const char *directory, const char *filename,
608  struct gc_arena *gc)
609 {
610 #ifdef _WIN32
611  const int CC_PATH_RESERVED = CC_LESS_THAN|CC_GREATER_THAN|CC_COLON
613 #else
614  const int CC_PATH_RESERVED = CC_SLASH;
615 #endif
616 
617  if (!gc)
618  {
619  return NULL; /* Would leak memory otherwise */
620  }
621 
622  const char *safe_filename = string_mod_const(filename, CC_PRINT, CC_PATH_RESERVED, '_', gc);
623 
624  if (safe_filename
625  && strcmp(safe_filename, ".")
626  && strcmp(safe_filename, "..")
627 #ifdef _WIN32
628  && win_safe_filename(safe_filename)
629 #endif
630  )
631  {
632  const size_t outsize = strlen(safe_filename) + (directory ? strlen(directory) : 0) + 16;
633  struct buffer out = alloc_buf_gc(outsize, gc);
634  char dirsep[2];
635 
636  dirsep[0] = PATH_SEPARATOR;
637  dirsep[1] = '\0';
638 
639  if (directory)
640  {
641  buf_printf(&out, "%s%s", directory, dirsep);
642  }
643  buf_printf(&out, "%s", safe_filename);
644 
645  return BSTR(&out);
646  }
647  else
648  {
649  return NULL;
650  }
651 }
652 
653 bool
654 platform_absolute_pathname(const char *pathname)
655 {
656  if (pathname)
657  {
658  const int c = pathname[0];
659 #ifdef _WIN32
660  return c == '\\' || (isalpha(c) && pathname[1] == ':' && pathname[2] == '\\');
661 #else
662  return c == '/';
663 #endif
664  }
665  else
666  {
667  return false;
668  }
669 }
670 
671 /* return true if filename can be opened for read */
672 bool
673 platform_test_file(const char *filename)
674 {
675  bool ret = false;
676  if (filename)
677  {
678  FILE *fp = platform_fopen(filename, "r");
679  if (fp)
680  {
681  fclose(fp);
682  ret = true;
683  }
684  else
685  {
686  if (errno == EACCES)
687  {
688  msg( M_WARN | M_ERRNO, "Could not access file '%s'", filename);
689  }
690  }
691  }
692 
693  dmsg(D_TEST_FILE, "TEST FILE '%s' [%d]",
694  filename ? filename : "UNDEF",
695  ret);
696 
697  return ret;
698 }
platform_create_temp_file
const char * platform_create_temp_file(const char *directory, const char *prefix, struct gc_arena *gc)
Create a temporary file in directory, returns the filename of the created file.
Definition: platform.c:554
CC_BACKSLASH
#define CC_BACKSLASH
backslash
Definition: buffer.h:905
platform_user_group_set
void platform_user_group_set(const struct platform_state_user *user_state, const struct platform_state_group *group_state, struct context *c)
Definition: platform.c:217
M_INFO
#define M_INFO
Definition: errlevel.h:55
error.h
platform_sleep_until_signal
void platform_sleep_until_signal(void)
Definition: platform.c:490
gc_new
static struct gc_arena gc_new(void)
Definition: buffer.h:1030
M_ERRNO
#define M_ERRNO
Definition: error.h:94
platform_ret_code
int platform_ret_code(int stat)
Return an exit code if valid and between 0 and 255, -1 otherwise.
Definition: platform.c:425
CC_SLASH
#define CC_SLASH
slash
Definition: buffer.h:911
M_FATAL
#define M_FATAL
Definition: error.h:89
win32.h
M_NONFATAL
#define M_NONFATAL
Definition: error.h:90
context
Contains all state information for one tunnel.
Definition: openvpn.h:476
BSTR
#define BSTR(buf)
Definition: buffer.h:129
get_random
long int get_random(void)
Definition: crypto.c:1611
CC_ASTERISK
#define CC_ASTERISK
asterisk
Definition: buffer.h:921
platform_getpid
unsigned int platform_getpid(void)
Definition: platform.c:333
alloc_buf_gc
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Definition: buffer.c:88
openvpn.h
platform_group_get
bool platform_group_get(const char *groupname, struct platform_state_group *state)
Definition: platform.c:123
dmsg
#define dmsg(flags,...)
Definition: error.h:148
platform_system_ok
bool platform_system_ok(int stat)
interpret the status code returned by execve()
Definition: platform.c:414
PACKAGE
#define PACKAGE
Definition: config.h:486
PATH_SEPARATOR
#define PATH_SEPARATOR
Definition: syshead.h:416
platform_state_group
Definition: platform.h:74
options.h
CC_COLON
#define CC_COLON
colon
Definition: buffer.h:910
CLEAR
#define CLEAR(x)
Definition: basic.h:33
CC_DOUBLE_QUOTE
#define CC_DOUBLE_QUOTE
double quote
Definition: buffer.h:913
platform_stat_t
struct _stat platform_stat_t
Definition: platform.h:146
ASSERT
#define ASSERT(x)
Definition: error.h:195
platform_test_file
bool platform_test_file(const char *filename)
Return true if filename can be opened for read.
Definition: platform.c:673
CC_QUESTION_MARK
#define CC_QUESTION_MARK
question mark
Definition: buffer.h:920
CC_PRINT
#define CC_PRINT
printable (>= 32, != 127)
Definition: buffer.h:896
platform_absolute_pathname
bool platform_absolute_pathname(const char *pathname)
Return true if pathname is absolute.
Definition: platform.c:654
misc.h
M_WARN
#define M_WARN
Definition: error.h:91
CC_LESS_THAN
#define CC_LESS_THAN
less than sign
Definition: buffer.h:917
platform_group_set
static void platform_group_set(const struct platform_state_group *state)
Definition: platform.c:150
wide_string
WCHAR * wide_string(const char *utf8, struct gc_arena *gc)
Definition: win32-util.c:41
context::options
struct options options
Options loaded from command line or configuration file.
Definition: openvpn.h:478
CC_GREATER_THAN
#define CC_GREATER_THAN
greater than sign
Definition: buffer.h:918
crypto.h
CC_PIPE
#define CC_PIPE
pipe
Definition: buffer.h:919
D_TEST_FILE
#define D_TEST_FILE
Definition: errlevel.h:137
M_ERR
#define M_ERR
Definition: error.h:105
buffer
Wrapper structure for dynamically allocated memory.
Definition: buffer.h:60
platform_nice
void platform_nice(int niceval)
Definition: platform.c:311
platform_chroot
void platform_chroot(const char *path)
Definition: platform.c:55
need_keep_caps
static int need_keep_caps(struct context *c)
Definition: platform.c:184
platform_unlink
bool platform_unlink(const char *filename)
Definition: platform.c:501
buffer.h
syshead.h
platform_fopen
FILE * platform_fopen(const char *path, const char *mode)
Definition: platform.c:514
platform.h
gc_arena
Garbage collection arena used to keep track of dynamically allocated memory.
Definition: buffer.h:116
platform_stat
int platform_stat(const char *path, platform_stat_t *buf)
Definition: platform.c:540
dco_enabled
static bool dco_enabled(const struct options *o)
Returns whether the current configuration has dco enabled.
Definition: options.h:907
platform_gen_path
const char * platform_gen_path(const char *directory, const char *filename, struct gc_arena *gc)
Put a directory and filename together.
Definition: platform.c:607
platform_state_user
Definition: platform.h:63
status
static SERVICE_STATUS status
Definition: interactive.c:53
gc_free
static void gc_free(struct gc_arena *a)
Definition: buffer.h:1038
platform_user_set
static void platform_user_set(const struct platform_state_user *state)
Definition: platform.c:106
platform_chdir
int platform_chdir(const char *dir)
Definition: platform.c:393
config.h
platform_user_get
bool platform_user_get(const char *username, struct platform_state_user *state)
Definition: platform.c:79
win_safe_filename
bool win_safe_filename(const char *fn)
Definition: win32-util.c:118
platform_mlockall
void platform_mlockall(bool print_msg)
Definition: platform.c:344
platform_open
int platform_open(const char *path, int flags, int mode)
Definition: platform.c:527
platform_access
int platform_access(const char *path, int mode)
Definition: platform.c:458
memdbg.h
string_mod_const
const char * string_mod_const(const char *str, const unsigned int inclusive, const unsigned int exclusive, const char replace, struct gc_arena *gc)
Returns a copy of a string with certain classes of characters of it replaced with a specified charact...
Definition: buffer.c:1108
msg
#define msg(flags,...)
Definition: error.h:144
platform_sleep_milliseconds
void platform_sleep_milliseconds(unsigned int n)
Definition: platform.c:474
http-client.f
string f
Definition: http-client.py:6
buf_printf
bool buf_printf(struct buffer *buf, const char *format,...)
Definition: buffer.c:240