File: env.c

package info (click to toggle)
xchpst 0.7.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 304 kB
  • sloc: ansic: 2,792; sh: 75; makefile: 47
file content (109 lines) | stat: -rw-r--r-- 2,847 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/* SPDX-License-Identifier: MIT */
/* SPDX-FileCopyrightText: (c) Copyright 2024 Andrew Bower <andrew@bower.uk> */

/* xchpst: eXtended Change Process State
 * A tool that is backwards compatible with chpst(8) from runit(8),
 * offering additional options to harden process with namespace isolation
 * and more. */

#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/file.h>
#include <sys/stat.h>

#include "xchpst.h"
#include "caps.h"
#include "options.h"

bool read_env_dir(const char *dir_name) {
  int dir1 = -1;
  int dir2 = -1;
  int file = -1;
  DIR *dir = NULL;
  const char *entity;
  const struct dirent *de;
  bool success = false;
  struct stat statbuf;
  char *data = NULL;
  ssize_t data_sz = -1;
  ssize_t buffered;
  ssize_t end;
  ssize_t ptr;
  int rc;

  if ((dir1 = open(entity = dir_name, O_RDONLY | O_DIRECTORY)) == -1)
    goto fail;
  if ((dir2 = openat(dir1, "./", O_RDONLY | O_DIRECTORY)) == -1)
    goto fail;
  if ((dir = fdopendir(dir1)) == NULL)
    goto fail;

  for (errno = 0; (de = readdir(dir));) {
    if (de->d_type == DT_DIR)
      continue;
    if ((file = openat(dir2, entity = de->d_name, O_RDONLY)) == -1)
      goto fail;
    if ((rc = fstat(file, &statbuf)) == -1)
      goto fail;
    assert(statbuf.st_size >= 0); /* educate gcc -fanalyzer */
    if ((ssize_t) statbuf.st_size > data_sz) {
      data_sz = statbuf.st_size;
      free(data);
      data = malloc(data_sz + 1);
      if (data == NULL)
        goto fail;
    }

    for (ptr = 0, buffered = 0, end = statbuf.st_size; ptr < end; ptr++) {
      /* Slurp chunks of data */
      if (ptr == buffered) {
        if ((rc = read(file, data + ptr, end - ptr)) == -1)
          goto fail;
        else
          buffered += rc;
      }
      /* Terminate at first LF; turn NUL within value into LF */
      if (data[ptr] == '\n')
        end = ptr;
      else if (data[ptr] == '\0')
        data[ptr] = '\n';
    }
    /* Remove trailing whitespace */
    for (ptr = end - 1; ptr >= 0; ptr--)
      if (data[ptr] == ' ' || data[ptr] == '\t')
        data[ptr] = '\0';
      else
        break;

    close(file);
    file = -1;

    if (statbuf.st_size != 0) {
      data[end] = '\0';
      if (is_verbose())
        fprintf(stderr, "setting %s=%s\n", de->d_name, data);
      setenv(de->d_name, data, 1);
    } else {
      if (is_verbose())
        fprintf(stderr, "unsetting %s\n", de->d_name);
      unsetenv(de->d_name);
    }
  }
  success = errno == 0 ? true : false;

fail:
  if (!success)
    fprintf(stderr, "error reading environment \"%s\", %s\n", entity, strerror(errno));
  free(data);
  if (file != -1)
    close(file);
  if (dir != NULL)
    closedir(dir);
 /* if fdopendir(dir1) succeeded, then don't close dir1 */
  else if (dir1 != -1)
    close(dir1);
  if (dir2 != -1)
    close(dir2);
  return success;
}