/*-*- Mode: C; c-basic-offset: 8 -*-*/ /*** This file is part of systemd. Copyright 2010 Lennart Poettering systemd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. systemd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ #include <errno.h> #include <string.h> #include "unit.h" #include "unit-name.h" #define VALID_CHARS \ "0123456789" \ "abcdefghijklmnopqrstuvwxyz" \ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ ":-_.\\" UnitType unit_name_to_type(const char *n) { UnitType t; assert(n); for (t = 0; t < _UNIT_TYPE_MAX; t++) if (endswith(n, unit_vtable[t]->suffix)) return t; return _UNIT_TYPE_INVALID; } bool unit_name_is_valid(const char *n) { UnitType t; const char *e, *i, *at; /* Valid formats: * * string@instance.suffix * string.suffix */ assert(n); if (strlen(n) >= UNIT_NAME_MAX) return false; t = unit_name_to_type(n); if (t < 0 || t >= _UNIT_TYPE_MAX) return false; assert_se(e = strrchr(n, '.')); if (e == n) return false; for (i = n, at = NULL; i < e; i++) { if (*i == '@' && !at) at = i; if (!strchr("@" VALID_CHARS, *i)) return false; } if (at) { if (at == n) return false; if (at[1] == '.') return false; } return true; } bool unit_instance_is_valid(const char *i) { assert(i); /* The max length depends on the length of the string, so we * don't really check this here. */ if (i[0] == 0) return false; /* We allow additional @ in the instance string, we do not * allow them in the prefix! */ for (; *i; i++) if (!strchr("@" VALID_CHARS, *i)) return false; return true; } bool unit_prefix_is_valid(const char *p) { /* We don't allow additional @ in the instance string */ if (p[0] == 0) return false; for (; *p; p++) if (!strchr(VALID_CHARS, *p)) return false; return true; } int unit_name_to_instance(const char *n, char **instance) { const char *p, *d; char *i; assert(n); assert(instance); /* Everything past the first @ and before the last . is the instance */ if (!(p = strchr(n, '@'))) { *instance = NULL; return 0; } assert_se(d = strrchr(n, '.')); assert(p < d); if (!(i = strndup(p+1, d-p-1))) return -ENOMEM; *instance = i; return 0; } char *unit_name_to_prefix_and_instance(const char *n) { const char *d; assert(n); assert_se(d = strrchr(n, '.')); return strndup(n, d - n); } char *unit_name_to_prefix(const char *n) { const char *p; if ((p = strchr(n, '@'))) return strndup(n, p - n); return unit_name_to_prefix_and_instance(n); } char *unit_name_change_suffix(const char *n, const char *suffix) { char *e, *r; size_t a, b; assert(n); assert(unit_name_is_valid(n)); assert(suffix); assert_se(e = strrchr(n, '.')); a = e - n; b = strlen(suffix); if (!(r = new(char, a + b + 1))) return NULL; memcpy(r, n, a); memcpy(r+a, suffix, b+1); return r; } char *unit_name_build(const char *prefix, const char *instance, const char *suffix) { char *r; assert(prefix); assert(unit_prefix_is_valid(prefix)); assert(!instance || unit_instance_is_valid(instance)); assert(suffix); assert(unit_name_to_type(suffix) >= 0); if (!instance) return strappend(prefix, suffix); if (asprintf(&r, "%s@%s%s", prefix, instance, suffix) < 0) return NULL; return r; } static char* do_escape(const char *f, char *t) { assert(f); assert(t); for (; *f; f++) { if (*f == '/') *(t++) = '-'; else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f)) { *(t++) = '\\'; *(t++) = 'x'; *(t++) = hexchar(*f > 4); *(t++) = hexchar(*f); } else *(t++) = *f; } return t; } char *unit_name_build_escape(const char *prefix, const char *instance, const char *suffix) { char *r, *t; size_t a, b, c; assert(prefix); assert(suffix); assert(unit_name_to_type(suffix) >= 0); /* Takes a arbitrary string for prefix and instance plus a * suffix and makes a nice string suitable as unit name of it, * escaping all weird chars on the way. * * / becomes ., and all chars not alloweed in a unit name get * escaped as \xFF, including \ and ., of course. This * escaping is hence reversible. * * This is primarily useful to make nice unit names from * strings, but is actually useful for any kind of string. */ a = strlen(prefix); c = strlen(suffix); if (instance) { b = strlen(instance); if (!(r = new(char, a*4 + 1 + b*4 + c + 1))) return NULL; t = do_escape(prefix, r); *(t++) = '@'; t = do_escape(instance, t); } else { if (!(r = new(char, a*4 + c + 1))) return NULL; t = do_escape(prefix, r); } strcpy(t, suffix); return r; } char *unit_name_escape(const char *f) { char *r, *t; if (!(r = new(char, strlen(f)*4+1))) return NULL; t = do_escape(f, r); *t = 0; return r; } char *unit_name_unescape(const char *f) { char *r, *t; assert(f); if (!(r = strdup(f))) return NULL; for (t = r; *f; f++) { if (*f == '-') *(t++) = '/'; else if (*f == '\\') { int a, b; if ((a = unhexchar(f[1])) < 0 || (b = unhexchar(f[2])) < 0) { /* Invalid escape code, let's take it literal then */ *(t++) = '\\'; } else { *(t++) = (char) ((a << 4) | b); f += 2; } } else *(t++) = *f; } *t = 0; return r; } bool unit_name_is_template(const char *n) { const char *p; assert(n); if (!(p = strchr(n, '@'))) return false; return p[1] == '.'; } char *unit_name_replace_instance(const char *f, const char *i) { const char *p, *e; char *r, *k; size_t a; assert(f); p = strchr(f, '@'); assert_se(e = strrchr(f, '.')); a = p - f; if (p) { size_t b; b = strlen(i); if (!(r = new(char, a + 1 + b + strlen(e) + 1))) return NULL; k = mempcpy(r, f, a + 1); k = mempcpy(k, i, b); } else { if (!(r = new(char, a + strlen(e) + 1))) return NULL; k = mempcpy(r, f, a); } strcpy(k, e); return r; } char *unit_name_template(const char *f) { const char *p, *e; char *r; size_t a; if (!(p = strchr(f, '@'))) return strdup(f); assert_se(e = strrchr(f, '.')); a = p - f + 1; if (!(r = new(char, a + strlen(e) + 1))) return NULL; strcpy(mempcpy(r, f, a), e); return r; } char *unit_name_from_path(const char *path, const char *suffix) { char *p, *r; assert(path); assert(suffix); if (!(p = strdup(path))) return NULL; path_kill_slashes(p); path = p[0] == '/' ? p + 1 : p; if (path[0] == 0) { free(p); return strappend("-", suffix); } r = unit_name_build_escape(path, NULL, suffix); free(p); return r; } char *unit_name_to_path(const char *name) { char *w, *e; assert(name); if (!(w = unit_name_to_prefix(name))) return NULL; e = unit_name_unescape(w); free(w); if (!e) return NULL; if (e[0] != '/') { w = strappend("/", e); free(e); if (!w) return NULL; e = w; } return e; }