diff options
Diffstat (limited to 'systemd/systemd.c')
-rw-r--r-- | systemd/systemd.c | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/systemd/systemd.c b/systemd/systemd.c new file mode 100644 index 0000000..c7bdb4d --- /dev/null +++ b/systemd/systemd.c @@ -0,0 +1,134 @@ +/* + * Helper functions for systemd generators in nfs-utils. + * + * Currently just systemd_escape(). + */ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include "systemd.h" + +static const char hex[16] = +{ + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', +}; + +/* + * determine length of the string that systemd_escape() needs to allocate + */ +static int systemd_len(char *path) +{ + char *p; + int len = 0; + + p = path; + while (*p == '/') + /* multiple leading "/" are ignored */ + p++; + + if (!*p) + /* root directory "/" becomes is encoded as a single "-" */ + return 1; + + if (*p == '.') + /* + * replace "." with "\x2d" escape sequence if + * it's the first character in escaped path + * */ + len += 4; + + while (*p) { + unsigned char c = *p++; + + if (c == '/') { + /* multiple non-trailing slashes become '-' */ + while (*p == '/') + p++; + if (*p) + len++; + } else if (isalnum(c) || c == ':' || c == '.' || c == '_') + /* these characters are not replaced */ + len++; + else + /* replace with "\x2d" escape sequence */ + len += 4; + } + + return len; +} + +/* + * convert c to "\x2d" escape sequence and append to string + * at position p, advancing p + */ +static char *hexify(unsigned char c, char *p) +{ + *p++ = '\\'; + *p++ = 'x'; + *p++ = hex[c >> 4]; + *p++ = hex[c & 0xf]; + return p; +} + +/* + * convert a path to a unit name according to the logic in systemd.unit(5): + * + * Basically, given a path, "/" is replaced by "-", and all other + * characters which are not ASCII alphanumerics are replaced by C-style + * "\x2d" escapes (except that "_" is never replaced and "." is only + * replaced when it would be the first character in the escaped path). + * The root directory "/" is encoded as single dash, while otherwise the + * initial and ending "/" are removed from all paths during + * transformation. + * + * NB: Although the systemd.unit(5) doesn't mention it, the ':' character + * is not escaped. + */ +char *systemd_escape(char *path, char *suffix) +{ + char *result; + char *p; + int len; + + len = systemd_len(path); + result = malloc(len + strlen(suffix) + 1); + p = result; + while (*path == '/') + /* multiple leading "/" are ignored */ + path++; + if (!*path) { + /* root directory "/" becomes is encoded as a single "-" */ + *p++ = '-'; + goto out; + } + if (*path == '.') + /* + * replace "." with "\x2d" escape sequence if + * it's the first character in escaped path + * */ + p = hexify(*path++, p); + + while (*path) { + unsigned char c = *path++; + + if (c == '/') { + /* multiple non-trailing slashes become '-' */ + while (*path == '/') + path++; + if (*path) + *p++ = '-'; + } else if (isalnum(c) || c == ':' || c == '.' || c == '_') + /* these characters are not replaced */ + *p++ = c; + else + /* replace with "\x2d" escape sequence */ + p = hexify(c, p); + } + +out: + sprintf(p, "%s", suffix); + return result; +} |