Skip to content

Commit

Permalink
Separate service gen and definitions. More generation from runit.
Browse files Browse the repository at this point in the history
systemd service generator now understands runit finish and conf. The
general service model aquired a few new fields for the aforementioned
things, plus a field for the healthchecking (which is not generated
for systemd, because systemd can't do healthchecks with arbitary
scripts easily). Overall, this should cover most of runit
services. log from runit is left unimplemented, as other service
daemons have their own means of logging.
  • Loading branch information
vaartis committed Jul 29, 2020
1 parent 84c16f4 commit 0b3f46c
Show file tree
Hide file tree
Showing 4 changed files with 445 additions and 330 deletions.
2 changes: 1 addition & 1 deletion src/crossfs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

all: crossfs

crossfs: crossfs.c
crossfs: crossfs.c definitions.h service_generation.h
$(CC) $(CFLAGS) -std=c99 -D_FILE_OFFSET_BITS=64 crossfs.c -o crossfs -lfuse3 -lpthread

clean:
Expand Down
332 changes: 3 additions & 329 deletions src/crossfs/crossfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@
#define FUSE_USE_VERSION 39
#define _GNU_SOURCE

#include <stdbool.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/xattr.h>
Expand All @@ -87,119 +86,12 @@

#include <uthash.h>

#include "definitions.h"
#include "service_generation.h"

#define ARRAY_LEN(x) (sizeof(x) / sizeof(x[0]))
#define MIN(x, y) (x < y ? x : y)

/*
* The directory containing the roots of the various strata. References to a
* specific stratum's instance of a file go through this directory.
*/
#define STRATA_ROOT "/bedrock/strata/"
#define STRATA_ROOT_LEN strlen(STRATA_ROOT)

/*
* Strat runs an executable from given stratum as specified in argument list.
* Crossfs injects this into various references to executables to ensure the
* appropriate stratum's instance of the executable is utilized in that
* context. This is useful for things such as .desktop files with `Exec=`
* references to executables.
*/
#define STRAT_PATH "/bedrock/bin/strat"
#define STRAT_PATH_LEN strlen(STRAT_PATH)

/*
* Bouncer, like strat, redirects to the appropriate stratum's instance of an
* executable. It differs from strat in that it determines which executable by
* looking at its extended filesystem attributes rather than its arguments.
* This is useful for binaries the user will directly execute, as the user
* controls the argument list.
*/
#define BOUNCER_PATH "/bedrock/libexec/bouncer"
#define BOUNCER_PATH_LEN strlen(BOUNCER_PATH)

/*
* The root of the procfs filesystem
*/
#define PROCFS_ROOT "/proc"

/*
* Surface the associated stratum and file path for files via xattrs.
*/
#define STRATUM_XATTR "user.bedrock.stratum"
#define STRATUM_XATTR_LEN strlen(STRATUM_XATTR)

#define LPATH_XATTR "user.bedrock.localpath"
#define LPATH_XATTR_LEN strlen(LPATH_XATTR)

#define RESTRICT_XATTR "user.bedrock.restrict"
#define RESTRICT_XATTR_LEN strlen(RESTRICT_XATTR)
#define RESTRICT "restrict"
#define RESTRICT_LEN strlen(RESTRICT)

/*
* Crossfs may be configured to present a file without being explicitly
* configured to also present its parent directory. It will dynamically create
* a virtual directory in these cases.
*
* This filesystem is typically mounted in the bedrock stratum then shared with
* other strata as a global path. Thus, by definition, anything that isn't
* crossed to another stratum is owned by the bedrock stratum, including these
* virtual directories.
*/
#define VIRTUAL_STRATUM "bedrock"
#define VIRTUAL_STRATUM_LEN strlen(VIRTUAL_STRATUM)
/*
* All crossfs files have an associated file path. While "/" isn't
* particularly meaningful here, no other path is obviously better.
*/
#define VIRTUAL_LPATH "/"
#define VIRTUAL_LPATH_LEN strlen(VIRTUAL_LPATH)

/*
* When merging font directories, these files require extra attention.
*/
#define FONTS_DIR "fonts.dir"
#define FONTS_DIR_LEN strlen(FONTS_DIR)

#define FONTS_ALIAS "fonts.alias"
#define FONTS_ALIAS_LEN strlen(FONTS_ALIAS)

/*
* The file path used to configure this filesystem.
*/
#define CFG_NAME ".bedrock-config-filesystem"
#define CFG_NAME_LEN strlen(CFG_NAME)

#define CFG_PATH "/.bedrock-config-filesystem"
#define CFG_PATH_LEN strlen(CFG_PATH)

/*
* Symlink to stratum root, used for local alias.
*/
#define LOCAL_ALIAS_NAME ".local-alias"
#define LOCAL_ALIAS_NAME_LEN strlen(LOCAL_ALIAS_NAME)

#define LOCAL_ALIAS_PATH "/.local-alias"
#define LOCAL_ALIAS_PATH_LEN strlen(LOCAL_ALIAS_PATH)

/*
* local alias
*/
#define LOCAL "local"
#define LOCAL_LEN strlen(LOCAL)

/*
* Headers for content written to CFG_NAME
*/
#define CMD_CLEAR "clear"
#define CMD_CLEAR_LEN strlen(CMD_CLEAR)

#define CMD_ADD "add"
#define CMD_ADD_LEN strlen(CMD_ADD)

#define CMD_RM "rm"
#define CMD_RM_LEN strlen(CMD_RM)

#define FS_IMP_SETUP(lock_type) \
int rv; \
set_caller_fsid(); \
Expand Down Expand Up @@ -255,55 +147,6 @@ enum ipath_class {
CLASS_ENOENT,
};

/*
* This filesystem may modify contents as it passes the backing file to the
* requesting process. The filter indicates the scheme used to modify the
* contents.
*/
enum filter {
/*
* Files are expected to be executables. Return bouncer.
*/
FILTER_BIN,
/*
* Files are expected to be executables. Return bouncer with restrict set.
*/
FILTER_BIN_RESTRICT,
/*
* Files are expected to be in ini-format. Performs various
* transformations such as injecting calls to strat or stratum root
* paths.
*/
FILTER_INI,
/*
* Combine fonts.dir and fonts.aliases files.
*/
FILTER_FONT,

FILTER_SERVICE,
/*
* Pass file through unaltered.
*/
FILTER_PASS,
};

/*
* Type of init daemon.
*/
enum service_type {
SERVICE_TYPE_SYSTEMD,
SERVICE_TYPE_RUNIT
};

const char *const filter_str[] = {
"bin",
"bin-restrict",
"ini",
"font",
"service",
"pass",
};

/*
* Wrap ini values with strat calls.
*
Expand Down Expand Up @@ -353,67 +196,6 @@ const size_t ini_expand_path_len[] = {
8,
};

struct stratum {
/*
* stratum name
*/
char *name;
size_t name_len;
/*
* A file descriptor relating to the corresponding stratum's root
* directory.
*/
int root_fd;
};

/*
* Each back_entry represents a file or directory which may fulfill a given
* cfg_entry file.
*/
struct back_entry {
/*
* The stratum-local path.
*/
char *lpath;
size_t lpath_len;
/*
* The corresponding stratum/alias. Run through deref() before
* consumption.
*/
struct stratum alias;
/*
* Boolean indicating if this back_entry uses the local alias. If so,
* deref() forwards calls to the local stratum rather than this
* struct's alias field.
*/
int local;
};

/*
* Each cfg_entry represents a user-facing file or directory in the mount
* point.
*/
struct cfg_entry {
/*
* Filter to apply to output.
*/
enum filter filter;
/*
* Path to append to mount point's path. For example, if this
* filesystem is mounted at "/bedrock/cross" and path="/man", this
* cfg_entry refers to "/bedrock/cross/man". Note the preceding slash.
*/
char *cpath;
size_t cpath_len;
/*
* Array of filesystem paths to be searched for this cfg_entry's
* backing file(s).
*/
struct back_entry *back;
size_t back_cnt;
size_t back_alloc;
};

/*
* Hash table entry for a single string.
*/
Expand All @@ -431,30 +213,6 @@ struct h_kv {
char key[];
};

/*
* Hash table entry to hold generated services.
*/
struct h_generated_service {
UT_hash_handle hh;
char *service_text;
int service_text_len;
time_t modification_time;
char original_path[];
};

/*
* An init-daemon-independant description of the service.
*/
struct service_desc {
char start[PATH_MAX];
char stop[PATH_MAX];
};

/*
* The hash table that holds generated services.
*/
static struct h_generated_service *generated_services = NULL;

/*
* An array of cfg_entry's listing all of the user-facing files and directories
* in this mount point.
Expand Down Expand Up @@ -504,12 +262,6 @@ static struct stat cfg_stat;
static struct stat local_stat;
static off_t bouncer_size;

/*
* Init daemon type of the init stratum. Services from other strata
* will be translated to this type.
*/
static enum service_type init_stratum_service_type;

/*
* Set the fsuid and fsgid to that of the calling function. setfsuid/setfsgid
* do not indicate success/failure; we have to trust they succeed. A check
Expand Down Expand Up @@ -1890,80 +1642,6 @@ static inline int inject_ini(struct cfg_entry *cfg, const char *ipath, size_t ip
return rv;
}

static inline int generate_service_for(struct back_entry *back, char *bpath, enum service_type service_type, struct h_generated_service **generated_service) {
char *service_stratum = deref(back)->name;

char full_service_path[PATH_MAX] = STRATA_ROOT;
strncat(full_service_path, service_stratum, PATH_MAX);
strncat(full_service_path, bpath, PATH_MAX);

struct stat original_service_stat;
if (stat(full_service_path, &original_service_stat) != 0) {
return -errno;
}

HASH_FIND_STR(generated_services, full_service_path, *generated_service);

bool need_to_create = *generated_service == NULL;

// If the modification date on the original service doesn't match the recorded one,
// the service should be regenerated
if (!need_to_create && (*generated_service)->modification_time != original_service_stat.st_mtime) {
// Remove the service from the table, free the service's text, and free the service itself
HASH_DEL(generated_services, *generated_service);
free((*generated_service)->service_text);
free(*generated_service);

// Mark that a new service has to be generated
need_to_create = true;
}

if (need_to_create) {
struct service_desc service_desc;
switch (service_type) {
case SERVICE_TYPE_RUNIT:
snprintf(service_desc.start, PATH_MAX, "/bedrock/bin/strat -r %s %s/run", service_stratum, full_service_path);

break;
}

// Allocate space for a generated service + service path data
*generated_service = malloc(sizeof(struct h_generated_service) + strlen(full_service_path) + 1);
if (*generated_service == NULL)
return -ENOMEM;

strcpy((*generated_service)->original_path, full_service_path);

switch (init_stratum_service_type) {
case SERVICE_TYPE_SYSTEMD:
(*generated_service)->service_text_len = asprintf(&(*generated_service)->service_text, "[Service]\nExecStart=%s\n", service_desc.start);
break;
}

(*generated_service)->modification_time = original_service_stat.st_mtime;
HASH_ADD_STR(generated_services, original_path, *generated_service);
}
}

static inline int read_service(struct cfg_entry *cfg, const char *const ipath, size_t ipath_len,
char *buf, size_t size, off_t offset, struct back_entry *back, char *bpath,
enum service_type service_type) {
if (init_stratum_service_type == service_type) {
switch (service_type) {
case SERVICE_TYPE_SYSTEMD:
return inject_ini(cfg, ipath, ipath_len, buf, size, offset);
break;
}
} else {
struct h_generated_service *generated_service = NULL;
generate_service_for(back, bpath, service_type, &generated_service);

strncpy(buf, generated_service->service_text, generated_service->service_text_len);

return generated_service->service_text_len;
}
}

static inline int getattr_back(struct cfg_entry *cfg, const char *ipath,
size_t ipath_len, struct stat *stbuf)
{
Expand Down Expand Up @@ -2306,10 +1984,6 @@ static inline int read_pass(struct cfg_entry *cfg, const char *const ipath,
return rv;
}

static inline int read_systemd_service(struct cfg_entry *cfg, const char *const ipath, size_t ipath_len, char *buf, size_t size, off_t offset) {
return inject_ini(cfg, ipath, ipath_len, buf, size, offset);
}

static inline int read_back(struct cfg_entry *cfg, const char *ipath, size_t
ipath_len, char *buf, size_t size, off_t offset)
{
Expand Down
Loading

0 comments on commit 0b3f46c

Please sign in to comment.