chiark / gitweb /
main: interpret all argv[] arguments unconditionally when run in a container
[elogind.git] / src / load-fragment.c
index 74114c43b865a9c82e294c52fc015aaacbfd8720..334bc713be8f691bcb65ba7ebf93d2ef31e5aab5 100644 (file)
@@ -191,7 +191,6 @@ static int config_parse_listen(
                 void *data,
                 void *userdata) {
 
-        int r;
         SocketPort *p;
         Socket *s;
 
@@ -217,7 +216,7 @@ static int config_parse_listen(
         } else {
                 p->type = SOCKET_SOCKET;
 
-                if ((r = socket_address_parse(&p->address, rvalue)) < 0) {
+                if (socket_address_parse(&p->address, rvalue) < 0) {
                         log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue);
                         free(p);
                         return 0;
@@ -289,14 +288,14 @@ static int config_parse_nice(
                 void *userdata) {
 
         ExecContext *c = data;
-        int priority, r;
+        int priority;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
-        if ((r = safe_atoi(rvalue, &priority)) < 0) {
+        if (safe_atoi(rvalue, &priority) < 0) {
                 log_error("[%s:%u] Failed to parse nice priority, ignoring: %s. ", filename, line, rvalue);
                 return 0;
         }
@@ -307,7 +306,7 @@ static int config_parse_nice(
         }
 
         c->nice = priority;
-        c->nice_set = false;
+        c->nice_set = true;
 
         return 0;
 }
@@ -322,14 +321,14 @@ static int config_parse_oom_score_adjust(
                 void *userdata) {
 
         ExecContext *c = data;
-        int oa, r;
+        int oa;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
-        if ((r = safe_atoi(rvalue, &oa)) < 0) {
+        if (safe_atoi(rvalue, &oa) < 0) {
                 log_error("[%s:%u] Failed to parse the OOM score adjust value, ignoring: %s", filename, line, rvalue);
                 return 0;
         }
@@ -433,7 +432,7 @@ static int config_parse_exec(
 
                 k = 0;
                 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
-                        if (strncmp(w, ";", l) == 0)
+                        if (strncmp(w, ";", MAX(l, 1U)) == 0)
                                 break;
 
                         k++;
@@ -444,7 +443,7 @@ static int config_parse_exec(
 
                 k = 0;
                 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
-                        if (strncmp(w, ";", l) == 0)
+                        if (strncmp(w, ";", MAX(l, 1U)) == 0)
                                 break;
 
                         if (honour_argv0 && w == rvalue) {
@@ -506,14 +505,13 @@ static int config_parse_usec(
                 void *userdata) {
 
         usec_t *usec = data;
-        int r;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
-        if ((r = parse_usec(rvalue, usec)) < 0) {
+        if (parse_usec(rvalue, usec) < 0) {
                 log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
                 return 0;
         }
@@ -893,14 +891,13 @@ static int config_parse_timer_slack_nsec(
 
         ExecContext *c = data;
         unsigned long u;
-        int r;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
-        if ((r = safe_atolu(rvalue, &u)) < 0) {
+        if (safe_atolu(rvalue, &u) < 0) {
                 log_error("[%s:%u] Failed to parse time slack value, ignoring: %s", filename, line, rvalue);
                 return 0;
         }
@@ -921,14 +918,13 @@ static int config_parse_limit(
 
         struct rlimit **rl = data;
         unsigned long long u;
-        int r;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
-        if ((r = safe_atollu(rvalue, &u)) < 0) {
+        if (safe_atollu(rvalue, &u) < 0) {
                 log_error("[%s:%u] Failed to parse resource value, ignoring: %s", filename, line, rvalue);
                 return 0;
         }
@@ -985,14 +981,14 @@ static int config_parse_sysv_priority(
                 void *userdata) {
 
         int *priority = data;
-        int r, i;
+        int i;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
-        if ((r = safe_atoi(rvalue, &i)) < 0 || i < 0) {
+        if (safe_atoi(rvalue, &i) < 0 || i < 0) {
                 log_error("[%s:%u] Failed to parse SysV start priority, ignoring: %s", filename, line, rvalue);
                 return 0;
         }
@@ -1002,6 +998,32 @@ static int config_parse_sysv_priority(
 }
 #endif
 
+static int config_parse_fsck_passno(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        int *passno = data;
+        int i;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (safe_atoi(rvalue, &i) || i < 0) {
+                log_error("[%s:%u] Failed to parse fsck pass number, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        *passno = (int) i;
+        return 0;
+}
+
 static DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
 
 static int config_parse_kill_signal(
@@ -1021,11 +1043,7 @@ static int config_parse_kill_signal(
         assert(rvalue);
         assert(sig);
 
-        if ((r = signal_from_string(rvalue)) <= 0)
-                if (startswith(rvalue, "SIG"))
-                        r = signal_from_string(rvalue+3);
-
-        if (r <= 0) {
+        if ((r = signal_from_string_try_harder(rvalue)) <= 0) {
                 log_error("[%s:%u] Failed to parse kill signal, ignoring: %s", filename, line, rvalue);
                 return 0;
         }
@@ -1055,11 +1073,11 @@ static int config_parse_mount_flags(
         assert(data);
 
         FOREACH_WORD_QUOTED(w, l, rvalue, state) {
-                if (strncmp(w, "shared", l) == 0)
+                if (strncmp(w, "shared", MAX(l, 6U)) == 0)
                         flags |= MS_SHARED;
-                else if (strncmp(w, "slave", l) == 0)
+                else if (strncmp(w, "slave", MAX(l, 5U)) == 0)
                         flags |= MS_SLAVE;
-                else if (strncmp(w, "private", l) == 0)
+                else if (strncmp(w, "private", MAX(l, 7U)) == 0)
                         flags |= MS_PRIVATE;
                 else {
                         log_error("[%s:%u] Failed to parse mount flags, ignoring: %s", filename, line, rvalue);
@@ -1082,7 +1100,6 @@ static int config_parse_timer(
 
         Timer *t = data;
         usec_t u;
-        int r;
         TimerValue *v;
         TimerBase b;
 
@@ -1096,7 +1113,7 @@ static int config_parse_timer(
                 return 0;
         }
 
-        if ((r = parse_usec(rvalue, &u)) < 0) {
+        if (parse_usec(rvalue, &u) < 0) {
                 log_error("[%s:%u] Failed to parse timer value, ignoring: %s", filename, line, rvalue);
                 return 0;
         }
@@ -1226,7 +1243,7 @@ static int config_parse_path_unit(
         return 0;
 }
 
-static int config_parse_env_file(
+static int config_parse_socket_service(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1235,54 +1252,113 @@ static int config_parse_env_file(
                 void *data,
                 void *userdata) {
 
-        FILE *f;
+        Socket *s = data;
         int r;
-        char ***env = data;
+        DBusError error;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
-        if (!(f = fopen(rvalue, "re"))) {
-                log_error("[%s:%u] Failed to open environment file '%s', ignoring: %m", filename, line, rvalue);
+        dbus_error_init(&error);
+
+        if (!endswith(rvalue, ".service")) {
+                log_error("[%s:%u] Unit must be of type service, ignoring: %s", filename, line, rvalue);
                 return 0;
         }
 
-        while (!feof(f)) {
-                char l[LINE_MAX], *p;
-                char **t;
+        if ((r = manager_load_unit(s->meta.manager, rvalue, NULL, &error, (Unit**) &s->service)) < 0) {
+                log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
+                dbus_error_free(&error);
+                return 0;
+        }
 
-                if (!fgets(l, sizeof(l), f)) {
-                        if (feof(f))
-                                break;
+        return 0;
+}
 
-                        r = -errno;
-                        log_error("[%s:%u] Failed to read environment file '%s', ignoring: %m", filename, line, rvalue);
-                        r = 0;
-                        goto finish;
-                }
+static int config_parse_service_sockets(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Service *s = data;
+        int r;
+        DBusError error;
+        char *state, *w;
+        size_t l;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        dbus_error_init(&error);
+
+        FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+                char *t;
+                Unit *sock;
 
-                p = strstrip(l);
+                if (!(t = strndup(w, l)))
+                        return -ENOMEM;
 
-                if (!*p)
+                if (!endswith(t, ".socket")) {
+                        log_error("[%s:%u] Unit must be of type socket, ignoring: %s", filename, line, rvalue);
+                        free(t);
                         continue;
+                }
 
-                if (strchr(COMMENTS, *p))
+                r = manager_load_unit(s->meta.manager, t, NULL, &error, &sock);
+                free(t);
+
+                if (r < 0) {
+                        log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
+                        dbus_error_free(&error);
                         continue;
+                }
 
-                t = strv_env_set(*env, p);
-                strv_free(*env);
-                *env = t;
+                if ((r = set_ensure_allocated(&s->configured_sockets, trivial_hash_func, trivial_compare_func)) < 0)
+                        return r;
+
+                if ((r = set_put(s->configured_sockets, sock)) < 0)
+                        return r;
         }
 
-        r = 0;
+        return 0;
+}
 
-finish:
-        if (f)
-                fclose(f);
+static int config_parse_env_file(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
-        return r;
+        char ***env = data, **k;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (!path_is_absolute(rvalue[0] == '-' ? rvalue + 1 : rvalue)) {
+                log_error("[%s:%u] Path '%s' is not absolute, ignoring.", filename, line, rvalue);
+                return 0;
+        }
+
+        if (!(k = strv_append(*env, rvalue)))
+                return -ENOMEM;
+
+        strv_free(*env);
+        *env = k;
+
+        return 0;
 }
 
 static int config_parse_ip_tos(
@@ -1295,7 +1371,6 @@ static int config_parse_ip_tos(
                 void *userdata) {
 
         int *ip_tos = data, x;
-        int r;
 
         assert(filename);
         assert(lvalue);
@@ -1303,7 +1378,7 @@ static int config_parse_ip_tos(
         assert(data);
 
         if ((x = ip_tos_from_string(rvalue)) < 0)
-                if ((r = safe_atoi(rvalue, &x)) < 0) {
+                if (safe_atoi(rvalue, &x) < 0) {
                         log_error("[%s:%u] Failed to parse IP TOS value, ignoring: %s", filename, line, rvalue);
                         return 0;
                 }
@@ -1312,6 +1387,145 @@ static int config_parse_ip_tos(
         return 0;
 }
 
+static int config_parse_condition_path(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Unit *u = data;
+        bool trigger, negate;
+        Condition *c;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if ((trigger = rvalue[0] == '|'))
+                rvalue++;
+
+        if ((negate = rvalue[0] == '!'))
+                rvalue++;
+
+        if (!path_is_absolute(rvalue)) {
+                log_error("[%s:%u] Path in condition not absolute, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        if (!(c = condition_new(streq(lvalue, "ConditionPathExists") ? CONDITION_PATH_EXISTS : CONDITION_DIRECTORY_NOT_EMPTY,
+                                rvalue, trigger, negate)))
+                return -ENOMEM;
+
+        LIST_PREPEND(Condition, conditions, u->meta.conditions, c);
+        return 0;
+}
+
+static int config_parse_condition_kernel(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Unit *u = data;
+        bool trigger, negate;
+        Condition *c;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if ((trigger = rvalue[0] == '|'))
+                rvalue++;
+
+        if ((negate = rvalue[0] == '!'))
+                rvalue++;
+
+        if (!(c = condition_new(CONDITION_KERNEL_COMMAND_LINE, rvalue, trigger, negate)))
+                return -ENOMEM;
+
+        LIST_PREPEND(Condition, conditions, u->meta.conditions, c);
+        return 0;
+}
+
+static int config_parse_condition_virt(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Unit *u = data;
+        bool trigger, negate;
+        Condition *c;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if ((trigger = rvalue[0] == '|'))
+                rvalue++;
+
+        if ((negate = rvalue[0] == '!'))
+                rvalue++;
+
+        if (!(c = condition_new(CONDITION_VIRTUALIZATION, rvalue, trigger, negate)))
+                return -ENOMEM;
+
+        LIST_PREPEND(Condition, conditions, u->meta.conditions, c);
+        return 0;
+}
+
+static int config_parse_condition_null(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Unit *u = data;
+        Condition *c;
+        bool trigger, negate;
+        int b;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if ((trigger = rvalue[0] == '|'))
+                rvalue++;
+
+        if ((negate = rvalue[0] == '!'))
+                rvalue++;
+
+        if ((b = parse_boolean(rvalue)) < 0) {
+                log_error("[%s:%u] Failed to parse boolean value in condition, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        if (!b)
+                negate = !negate;
+
+        if (!(c = condition_new(CONDITION_NULL, NULL, trigger, negate)))
+                return -ENOMEM;
+
+        LIST_PREPEND(Condition, conditions, u->meta.conditions, c);
+        return 0;
+}
+
 static DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
 
 #define FOLLOW_MAX 8
@@ -1343,7 +1557,7 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
                  * unit name. */
                 name = file_name_from_path(*filename);
 
-                if (unit_name_is_valid(name)) {
+                if (unit_name_is_valid(name, false)) {
                         if (!(id = set_get(names, name))) {
 
                                 if (!(id = strdup(name)))
@@ -1483,6 +1697,10 @@ static void dump_items(FILE *f, const ConfigItem *items) {
                 { config_parse_path_unit,        "UNIT" },
                 { config_parse_notify_access,    "ACCESS" },
                 { config_parse_ip_tos,           "TOS" },
+                { config_parse_condition_path,   "CONDITION" },
+                { config_parse_condition_kernel, "CONDITION" },
+                { config_parse_condition_null,   "CONDITION" },
+                { config_parse_condition_virt,   "CONDITION" },
         };
 
         assert(f);
@@ -1543,7 +1761,7 @@ static int load_from_path(Unit *u, const char *path) {
                 { "CPUAffinity",            config_parse_cpu_affinity,    &(context),                                      section   }, \
                 { "UMask",                  config_parse_mode,            &(context).umask,                                section   }, \
                 { "Environment",            config_parse_strv,            &(context).environment,                          section   }, \
-                { "EnvironmentFile",        config_parse_env_file,        &(context).environment,                          section   }, \
+                { "EnvironmentFile",        config_parse_env_file,        &(context).environment_files,                    section   }, \
                 { "StandardInput",          config_parse_input,           &(context).std_input,                            section   }, \
                 { "StandardOutput",         config_parse_output,          &(context).std_output,                           section   }, \
                 { "StandardError",          config_parse_output,          &(context).std_error,                            section   }, \
@@ -1581,7 +1799,9 @@ static int load_from_path(Unit *u, const char *path) {
                 { "TCPWrapName",            config_parse_string_printf,   &(context).tcpwrap_name,                         section   }, \
                 { "PAMName",                config_parse_string_printf,   &(context).pam_name,                             section   }, \
                 { "KillMode",               config_parse_kill_mode,       &(context).kill_mode,                            section   }, \
-                { "KillSignal",             config_parse_kill_signal,     &(context).kill_signal,                          section   }
+                { "KillSignal",             config_parse_kill_signal,     &(context).kill_signal,                          section   }, \
+                { "SendSIGKILL",            config_parse_bool,            &(context).send_sigkill,                         section   }, \
+                { "UtmpIdentifier",         config_parse_string_printf,   &(context).utmp_id,                              section   }
 
         const ConfigItem items[] = {
                 { "Names",                  config_parse_names,           u,                                               "Unit"    },
@@ -1591,18 +1811,22 @@ static int load_from_path(Unit *u, const char *path) {
                 { "Requisite",              config_parse_deps,            UINT_TO_PTR(UNIT_REQUISITE),                     "Unit"    },
                 { "RequisiteOverridable",   config_parse_deps,            UINT_TO_PTR(UNIT_REQUISITE_OVERRIDABLE),         "Unit"    },
                 { "Wants",                  config_parse_deps,            UINT_TO_PTR(UNIT_WANTS),                         "Unit"    },
+                { "BindTo",                 config_parse_deps,            UINT_TO_PTR(UNIT_BIND_TO),                       "Unit"    },
                 { "Conflicts",              config_parse_deps,            UINT_TO_PTR(UNIT_CONFLICTS),                     "Unit"    },
                 { "Before",                 config_parse_deps,            UINT_TO_PTR(UNIT_BEFORE),                        "Unit"    },
                 { "After",                  config_parse_deps,            UINT_TO_PTR(UNIT_AFTER),                         "Unit"    },
                 { "OnFailure",              config_parse_deps,            UINT_TO_PTR(UNIT_ON_FAILURE),                    "Unit"    },
-                { "RecursiveStop",          config_parse_bool,            &u->meta.recursive_stop,                         "Unit"    },
                 { "StopWhenUnneeded",       config_parse_bool,            &u->meta.stop_when_unneeded,                     "Unit"    },
                 { "RefuseManualStart",      config_parse_bool,            &u->meta.refuse_manual_start,                    "Unit"    },
                 { "RefuseManualStop",       config_parse_bool,            &u->meta.refuse_manual_stop,                     "Unit"    },
                 { "AllowIsolate",           config_parse_bool,            &u->meta.allow_isolate,                          "Unit"    },
                 { "DefaultDependencies",    config_parse_bool,            &u->meta.default_dependencies,                   "Unit"    },
-                { "IgnoreDependencyFailure",config_parse_bool,            &u->meta.ignore_dependency_failure,              "Unit"    },
                 { "JobTimeoutSec",          config_parse_usec,            &u->meta.job_timeout,                            "Unit"    },
+                { "ConditionPathExists",    config_parse_condition_path,  u,                                               "Unit"    },
+                { "ConditionDirectoryNotEmpty", config_parse_condition_path,  u,                                           "Unit"    },
+                { "ConditionKernelCommandLine", config_parse_condition_kernel, u,                                          "Unit"    },
+                { "ConditionVirtualization",config_parse_condition_virt,  u,                                               "Unit"    },
+                { "ConditionNull",          config_parse_condition_null,  u,                                               "Unit"    },
 
                 { "PIDFile",                config_parse_path,            &u->service.pid_file,                            "Service" },
                 { "ExecStartPre",           config_parse_exec,            u->service.exec_command+SERVICE_EXEC_START_PRE,  "Service" },
@@ -1618,14 +1842,17 @@ static int load_from_path(Unit *u, const char *path) {
                 { "PermissionsStartOnly",   config_parse_bool,            &u->service.permissions_start_only,              "Service" },
                 { "RootDirectoryStartOnly", config_parse_bool,            &u->service.root_directory_start_only,           "Service" },
                 { "RemainAfterExit",        config_parse_bool,            &u->service.remain_after_exit,                   "Service" },
+                { "GuessMainPID",           config_parse_bool,            &u->service.guess_main_pid,                      "Service" },
 #ifdef HAVE_SYSV_COMPAT
                 { "SysVStartPriority",      config_parse_sysv_priority,   &u->service.sysv_start_priority,                 "Service" },
 #else
-                { "SysVStartPriority",      config_parse_warn_compat,     NULL,                                           "Service" },
+                { "SysVStartPriority",      config_parse_warn_compat,     NULL,                                            "Service" },
 #endif
                 { "NonBlocking",            config_parse_bool,            &u->service.exec_context.non_blocking,           "Service" },
                 { "BusName",                config_parse_string_printf,   &u->service.bus_name,                            "Service" },
                 { "NotifyAccess",           config_parse_notify_access,   &u->service.notify_access,                       "Service" },
+                { "Sockets",                config_parse_service_sockets, &u->service,                                     "Service" },
+                { "FsckPassNo",             config_parse_fsck_passno,     &u->service.fsck_passno,                         "Service" },
                 EXEC_CONTEXT_CONFIG_ITEMS(u->service.exec_context, "Service"),
 
                 { "ListenStream",           config_parse_listen,          &u->socket,                                      "Socket"  },
@@ -1654,6 +1881,7 @@ static int load_from_path(Unit *u, const char *path) {
                 { "PipeSize",               config_parse_size,            &u->socket.pipe_size,                            "Socket"  },
                 { "FreeBind",               config_parse_bool,            &u->socket.free_bind,                            "Socket"  },
                 { "TCPCongestion",          config_parse_string,          &u->socket.tcp_congestion,                       "Socket"  },
+                { "Service",                config_parse_socket_service,  &u->socket,                                      "Socket"  },
                 EXEC_CONTEXT_CONFIG_ITEMS(u->socket.exec_context, "Socket"),
 
                 { "What",                   config_parse_string,          &u->mount.parameters_fragment.what,              "Mount"   },
@@ -1669,6 +1897,8 @@ static int load_from_path(Unit *u, const char *path) {
 
                 { "What",                   config_parse_path,            &u->swap.parameters_fragment.what,               "Swap"    },
                 { "Priority",               config_parse_int,             &u->swap.parameters_fragment.priority,           "Swap"    },
+                { "TimeoutSec",             config_parse_usec,            &u->swap.timeout_usec,                           "Swap"    },
+                EXEC_CONTEXT_CONFIG_ITEMS(u->swap.exec_context, "Swap"),
 
                 { "OnActiveSec",            config_parse_timer,           &u->timer,                                       "Timer"   },
                 { "OnBootSec",              config_parse_timer,           &u->timer,                                       "Timer"   },
@@ -1793,9 +2023,15 @@ static int load_from_path(Unit *u, const char *path) {
                 goto finish;
         }
 
-        /* Now, parse the file contents */
-        if ((r = config_parse(filename, f, sections, items, false, u)) < 0)
-                goto finish;
+        if (null_or_empty(&st))
+                u->meta.load_state = UNIT_MASKED;
+        else {
+                /* Now, parse the file contents */
+                if ((r = config_parse(filename, f, sections, items, false, u)) < 0)
+                        goto finish;
+
+                u->meta.load_state = UNIT_LOADED;
+        }
 
         free(u->meta.fragment_path);
         u->meta.fragment_path = filename;
@@ -1803,7 +2039,6 @@ static int load_from_path(Unit *u, const char *path) {
 
         u->meta.fragment_mtime = timespec_load(&st.st_mtim);
 
-        u->meta.load_state = UNIT_LOADED;
         r = 0;
 
 finish:
@@ -1846,10 +2081,20 @@ int unit_load_fragment(Unit *u) {
                 }
 
         /* And now, try looking for it under the suggested (originally linked) path */
-        if (u->meta.load_state == UNIT_STUB && u->meta.fragment_path)
+        if (u->meta.load_state == UNIT_STUB && u->meta.fragment_path) {
+
                 if ((r = load_from_path(u, u->meta.fragment_path)) < 0)
                         return r;
 
+                if (u->meta.load_state == UNIT_STUB) {
+                        /* Hmm, this didn't work? Then let's get rid
+                         * of the fragment path stored for us, so that
+                         * we don't point to an invalid location. */
+                        free(u->meta.fragment_path);
+                        u->meta.fragment_path = NULL;
+                }
+        }
+
         /* Look for a template */
         if (u->meta.load_state == UNIT_STUB && u->meta.instance) {
                 char *k;