summaryrefslogtreecommitdiff
path: root/src/s6-rc/s6-rc-init.c
blob: dfc7b7cb8652b58ae70f1eb19bc7deee7d552031 (plain)
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/* ISC license. */

#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <skalibs/types.h>
#include <skalibs/sgetopt.h>
#include <skalibs/strerr2.h>
#include <skalibs/stralloc.h>
#include <skalibs/tai.h>
#include <skalibs/djbunix.h>
#include <skalibs/unix-transactional.h>
#include <s6-rc/config.h>
#include <s6-rc/s6rc.h>

#define USAGE "s6-rc-init [ -c compiled ] [ -l live ] [ -p prefix ] [ -t timeout ] [ -d ] scandir"
#define dieusage() strerr_dieusage(100, USAGE)

static void cleanup (stralloc *sa)
{
  int e = errno ;
  rm_rf_in_tmp(sa, 0) ;
  errno = e ;
}

int main (int argc, char const *const *argv)
{
  tain_t deadline ;
  stralloc sa = STRALLOC_ZERO ;
  size_t dirlen ;
  char const *live = S6RC_LIVE_BASE ;
  char const *compiled = S6RC_COMPILED_BASE ;
  char const *prefix = "" ;
  int deref = 0 ;
  PROG = "s6-rc-init" ;
  {
    unsigned int t = 0 ;
    subgetopt_t l = SUBGETOPT_ZERO ;
    for (;;)
    {
      int opt = subgetopt_r(argc, argv, "c:l:p:t:bd", &l) ;
      if (opt == -1) break ;
      switch (opt)
      {
        case 'c' : compiled = l.arg ; break ;
        case 'l' : live = l.arg ; break ;
        case 'p' : prefix = l.arg ; break ;
        case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ;
        case 'b' : break ;
        case 'd' : deref = 1 ; break ;
        default : dieusage() ;
      }
    }
    argc -= l.ind ; argv += l.ind ;
    if (t) tain_from_millisecs(&deadline, t) ;
    else deadline = tain_infinite_relative ;
  }
  if (!argc) dieusage() ;

  if (!deref && compiled[0] != '/')
    strerr_dief2x(100, compiled, " is not an absolute path") ;
  if (argv[0][0] != '/')
    strerr_dief2x(100, argv[0], " is not an absolute path") ;
  if (strchr(prefix, '/') || strchr(prefix, '\n'))
    strerr_dief1x(100, "prefix cannot contain a / or a newline") ;

  tain_now_g() ;
  tain_add_g(&deadline, &deadline) ;

  if (deref)
  {
    char *x = realpath(compiled, 0) ;
    if (!x) strerr_diefu2sys(111, "realpath ", compiled) ;
    compiled = x ;
  }
  {
    s6rc_db_t db ;
    int r ;
    int fdcompiled = open_readb(compiled) ;
    if (fdcompiled < 0)
      strerr_diefu2sys(111, "open ", compiled) ;
    r = s6rc_db_read_sizes(fdcompiled, &db) ;
    if (r < 0)
      strerr_diefu2sys(111, "read database size in ", compiled) ;
    else if (!r)
      strerr_dief2x(4, "invalid database size in ", compiled) ;
    close(fdcompiled) ;
    {
      unsigned char state[db.nshort + db.nlong] ;
      memset(state, 0, db.nshort + db.nlong) ;
      if (!s6rc_livedir_create(&sa, live, PROG, argv[0], prefix, compiled, state, db.nshort + db.nlong, &dirlen))
        strerr_diefu1sys(111, "create live directory") ;
    }
  }
  {
    size_t clen = strlen(compiled) ;
    char lfn[sa.len + 13] ;
    char cfn[clen + 13] ;
    memcpy(lfn, sa.s, sa.len) ;
    memcpy(lfn + sa.len, "/servicedirs", 13) ;
    memcpy(cfn, compiled, clen) ;
    memcpy(cfn + clen, "/servicedirs", 13) ;
    if (!hiercopy(cfn, lfn))
    {
      cleanup(&sa) ;
      strerr_diefu4sys(111, "recursively copy ", cfn, " to ", lfn) ;
    }
  }
  if (!atomic_symlink(sa.s + dirlen, live, PROG))
  {
    cleanup(&sa) ;
    strerr_diefu4sys(111, "symlink ", sa.s + dirlen, " to ", live) ;
  }

  deref = s6rc_servicedir_manage_g(live, prefix, &deadline) ;
  if (!deref)
  {
    cleanup(&sa) ;
    strerr_diefu3sys(111, "supervise service directories in ", live, "/servicedirs") ;
  }
  if (deref & 2)
    strerr_warnw2x("s6-svscan not running on ", argv[0]) ;
  return 0 ;
}