Coverage Report

Created: 2026-01-22 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/systemd/src/xdg-autostart-generator/xdg-autostart-service.c
Line
Count
Source
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3
#include <stdio.h>
4
5
#include "alloc-util.h"
6
#include "conf-parser.h"
7
#include "escape.h"
8
#include "extract-word.h"
9
#include "fd-util.h"
10
#include "fileio.h"
11
#include "generator.h"
12
#include "glyph-util.h"
13
#include "log.h"
14
#include "parse-util.h"
15
#include "path-util.h"
16
#include "specifier.h"
17
#include "string-util.h"
18
#include "strv.h"
19
#include "unit-name.h"
20
#include "user-util.h"
21
#include "xdg-autostart-service.h"
22
23
1.94k
XdgAutostartService* xdg_autostart_service_free(XdgAutostartService *s) {
24
1.94k
        if (!s)
25
0
                return NULL;
26
27
1.94k
        free(s->name);
28
1.94k
        free(s->path);
29
1.94k
        free(s->description);
30
31
1.94k
        free(s->type);
32
1.94k
        free(s->exec_string);
33
1.94k
        free(s->working_directory);
34
35
1.94k
        strv_free(s->only_show_in);
36
1.94k
        strv_free(s->not_show_in);
37
38
1.94k
        free(s->try_exec);
39
1.94k
        free(s->autostart_condition);
40
1.94k
        free(s->kde_autostart_condition);
41
42
1.94k
        free(s->gnome_autostart_phase);
43
44
1.94k
        return mfree(s);
45
1.94k
}
46
47
0
char* xdg_autostart_service_translate_name(const char *name) {
48
0
        _cleanup_free_ char *c = NULL, *escaped = NULL;
49
0
        char *res;
50
51
0
        c = strdup(name);
52
0
        if (!c)
53
0
                return NULL;
54
55
0
        res = endswith(c, ".desktop");
56
0
        if (res)
57
0
                *res = '\0';
58
59
0
        escaped = unit_name_escape(c);
60
0
        if (!escaped)
61
0
                return NULL;
62
63
0
        return strjoin("app-", escaped, "@autostart.service");
64
0
}
65
66
static int xdg_config_parse_bool(
67
                const char *unit,
68
                const char *filename,
69
                unsigned line,
70
                const char *section,
71
                unsigned section_line,
72
                const char *lvalue,
73
                int ltype,
74
                const char *rvalue,
75
                void *data,
76
887
                void *userdata) {
77
78
887
        bool *b = ASSERT_PTR(data);
79
887
        int r;
80
81
887
        assert(filename);
82
887
        assert(lvalue);
83
887
        assert(rvalue);
84
85
887
        r = parse_boolean(rvalue);
86
887
        if (r < 0)
87
46
                return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Invalid value for boolean: %s", rvalue);
88
841
        *b = r;
89
841
        return 0;
90
887
}
91
92
/* Unescapes the string in-place, returns non-zero status on error. */
93
static int xdg_unescape_string(
94
                const char *unit,
95
                const char *filename,
96
                int line,
97
99.0k
                char *str) {
98
99
99.0k
        char *in;
100
99.0k
        char *out;
101
102
99.0k
        assert(str);
103
104
99.0k
        in = out = str;
105
106
1.74M
        for (; *in; in++, out++) {
107
1.64M
                if (*in == '\\') {
108
                        /* Move forward, and ensure it is a valid escape. */
109
17.9k
                        in++;
110
111
17.9k
                        switch (*in) {
112
202
                                case 's':
113
202
                                        *out = ' ';
114
202
                                        break;
115
197
                                case 'n':
116
197
                                        *out = '\n';
117
197
                                        break;
118
197
                                case 't':
119
197
                                        *out = '\t';
120
197
                                        break;
121
333
                                case 'r':
122
333
                                        *out = '\r';
123
333
                                        break;
124
16.8k
                                case '\\':
125
16.8k
                                        *out = '\\';
126
16.8k
                                        break;
127
227
                                case ';':
128
                                        /* Technically only permitted for strv. */
129
227
                                        *out = ';';
130
227
                                        break;
131
5
                                default:
132
5
                                        return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Undefined escape sequence \\%c.", *in);
133
17.9k
                        }
134
135
17.9k
                        continue;
136
17.9k
                }
137
138
1.62M
                *out = *in;
139
1.62M
        }
140
99.0k
        *out = '\0';
141
142
99.0k
        return 0;
143
99.0k
}
144
145
/* Note: We do not bother with unescaping the strings, hence the _raw postfix. */
146
static int xdg_config_parse_string(
147
                const char *unit,
148
                const char *filename,
149
                unsigned line,
150
                const char *section,
151
                unsigned section_line,
152
                const char *lvalue,
153
                int ltype,
154
                const char *rvalue,
155
                void *data,
156
2.17k
                void *userdata) {
157
158
2.17k
        _cleanup_free_ char *res = NULL;
159
2.17k
        char **out = ASSERT_PTR(data);
160
2.17k
        int r;
161
162
2.17k
        assert(filename);
163
2.17k
        assert(lvalue);
164
2.17k
        assert(rvalue);
165
166
        /* XDG does not allow duplicate definitions. */
167
2.17k
        if (*out) {
168
431
                log_syntax(unit, LOG_WARNING, filename, line, 0, "Key %s was defined multiple times, ignoring.", lvalue);
169
431
                return 0;
170
431
        }
171
172
1.74k
        res = strdup(rvalue);
173
1.74k
        if (!res)
174
0
                return log_oom();
175
176
1.74k
        r = xdg_unescape_string(unit, filename, line, res);
177
1.74k
        if (r < 0)
178
3
                return r;
179
180
1.74k
        *out = TAKE_PTR(res);
181
1.74k
        return 0;
182
1.74k
}
183
184
static int strv_strndup_unescape_and_push(
185
                const char *unit,
186
                const char *filename,
187
                unsigned line,
188
                char ***sv,
189
                size_t *n,
190
                const char *start,
191
97.9k
                const char *end) {
192
193
97.9k
        if (end == start)
194
640
                return 0;
195
196
97.2k
        _cleanup_free_ char *copy = NULL;
197
97.2k
        int r;
198
199
97.2k
        copy = strndup(start, end - start);
200
97.2k
        if (!copy)
201
0
                return log_oom();
202
203
97.2k
        r = xdg_unescape_string(unit, filename, line, copy);
204
97.2k
        if (r < 0)
205
2
                return r;
206
207
97.2k
        if (!GREEDY_REALLOC(*sv, *n + 2)) /* One extra for NULL */
208
0
                return log_oom();
209
210
97.2k
        (*sv)[*n] = TAKE_PTR(copy);
211
97.2k
        (*sv)[*n + 1] = NULL;
212
97.2k
        (*n)++;
213
214
97.2k
        return 0;
215
97.2k
}
216
217
static int xdg_config_parse_strv(
218
                const char *unit,
219
                const char *filename,
220
                unsigned line,
221
                const char *section,
222
                unsigned section_line,
223
                const char *lvalue,
224
                int ltype,
225
                const char *rvalue,
226
                void *data,
227
1.35k
                void *userdata) {
228
229
1.35k
        char ***ret_sv = ASSERT_PTR(data);
230
1.35k
        int r;
231
232
1.35k
        assert(filename);
233
1.35k
        assert(lvalue);
234
1.35k
        assert(rvalue);
235
236
        /* XDG does not allow duplicate definitions. */
237
1.35k
        if (*ret_sv) {
238
377
                log_syntax(unit, LOG_WARNING, filename, line, 0, "Key %s was already defined, ignoring.", lvalue);
239
377
                return 0;
240
377
        }
241
242
982
        size_t n = 0;
243
982
        _cleanup_strv_free_ char **sv = NULL;
244
245
982
        if (!GREEDY_REALLOC0(sv, 1))
246
0
                return log_oom();
247
248
        /* We cannot use strv_split because it does not handle escaping correctly. */
249
982
        const char *start = rvalue, *end;
250
251
197k
        for (end = start; *end; end++) {
252
197k
                if (*end == '\\') {
253
                        /* Move forward, and ensure it is a valid escape. */
254
1.06k
                        end++;
255
1.06k
                        if (!strchr("sntr\\;", *end)) {
256
854
                                log_syntax(unit, LOG_WARNING, filename, line, 0, "Undefined escape sequence \\%c.", *end);
257
854
                                return 0;
258
854
                        }
259
211
                        continue;
260
1.06k
                }
261
262
196k
                if (*end == ';') {
263
97.8k
                        r = strv_strndup_unescape_and_push(unit, filename, line,
264
97.8k
                                                           &sv, &n,
265
97.8k
                                                           start, end);
266
97.8k
                        if (r < 0)
267
0
                                return r;
268
269
97.8k
                        start = end + 1;
270
97.8k
                }
271
196k
        }
272
273
        /* Handle the trailing entry after the last separator */
274
128
        r = strv_strndup_unescape_and_push(unit, filename, line,
275
128
                                           &sv, &n,
276
128
                                           start, end);
277
128
        if (r < 0)
278
2
                return r;
279
280
126
        *ret_sv = TAKE_PTR(sv);
281
126
        return 0;
282
128
}
283
284
static int xdg_config_item_table_lookup(
285
                const void *table,
286
                const char *section,
287
                const char *lvalue,
288
                ConfigParserCallback *ret_func,
289
                int *ret_ltype,
290
                void **ret_data,
291
5.28k
                void *userdata) {
292
293
5.28k
        assert(lvalue);
294
295
        /* Ignore any keys with [] as those are translations. */
296
5.28k
        if (strchr(lvalue, '[')) {
297
262
                *ret_func = NULL;
298
262
                *ret_ltype = 0;
299
262
                *ret_data = NULL;
300
262
                return 1;
301
262
        }
302
303
5.01k
        return config_item_table_lookup(table, section, lvalue, ret_func, ret_ltype, ret_data, userdata);
304
5.28k
}
305
306
1.94k
XdgAutostartService *xdg_autostart_service_parse_desktop(const char *path) {
307
1.94k
        _cleanup_(xdg_autostart_service_freep) XdgAutostartService *service = NULL;
308
1.94k
        int r;
309
310
1.94k
        service = new0(XdgAutostartService, 1);
311
1.94k
        if (!service)
312
0
                return NULL;
313
314
1.94k
        service->path = strdup(path);
315
1.94k
        if (!service->path)
316
0
                return NULL;
317
318
1.94k
        const ConfigTableItem items[] = {
319
1.94k
                { "Desktop Entry", "Name",                      xdg_config_parse_string, 0, &service->description             },
320
1.94k
                { "Desktop Entry", "Exec",                      xdg_config_parse_string, 0, &service->exec_string             },
321
1.94k
                { "Desktop Entry", "Path",                      xdg_config_parse_string, 0, &service->working_directory       },
322
1.94k
                { "Desktop Entry", "TryExec",                   xdg_config_parse_string, 0, &service->try_exec                },
323
1.94k
                { "Desktop Entry", "Type",                      xdg_config_parse_string, 0, &service->type                    },
324
1.94k
                { "Desktop Entry", "OnlyShowIn",                xdg_config_parse_strv,   0, &service->only_show_in            },
325
1.94k
                { "Desktop Entry", "NotShowIn",                 xdg_config_parse_strv,   0, &service->not_show_in             },
326
1.94k
                { "Desktop Entry", "Hidden",                    xdg_config_parse_bool,   0, &service->hidden                  },
327
1.94k
                { "Desktop Entry", "AutostartCondition",        xdg_config_parse_string, 0, &service->autostart_condition     },
328
1.94k
                { "Desktop Entry", "X-KDE-autostart-condition", xdg_config_parse_string, 0, &service->kde_autostart_condition },
329
1.94k
                { "Desktop Entry", "X-GNOME-Autostart-Phase",   xdg_config_parse_string, 0, &service->gnome_autostart_phase   },
330
1.94k
                { "Desktop Entry", "X-systemd-skip",            xdg_config_parse_bool,   0, &service->systemd_skip            },
331
332
                /* Common entries that we do not use currently. */
333
1.94k
                { "Desktop Entry", "Categories",                NULL, 0, NULL},
334
1.94k
                { "Desktop Entry", "Comment",                   NULL, 0, NULL},
335
1.94k
                { "Desktop Entry", "DBusActivatable",           NULL, 0, NULL},
336
1.94k
                { "Desktop Entry", "Encoding",                  NULL, 0, NULL},
337
1.94k
                { "Desktop Entry", "GenericName",               NULL, 0, NULL},
338
1.94k
                { "Desktop Entry", "Icon",                      NULL, 0, NULL},
339
1.94k
                { "Desktop Entry", "Keywords",                  NULL, 0, NULL},
340
1.94k
                { "Desktop Entry", "MimeType",                  NULL, 0, NULL},
341
1.94k
                { "Desktop Entry", "NoDisplay",                 NULL, 0, NULL},
342
1.94k
                { "Desktop Entry", "StartupNotify",             NULL, 0, NULL},
343
1.94k
                { "Desktop Entry", "StartupWMClass",            NULL, 0, NULL},
344
1.94k
                { "Desktop Entry", "Terminal",                  NULL, 0, NULL},
345
1.94k
                { "Desktop Entry", "URL",                       NULL, 0, NULL},
346
1.94k
                { "Desktop Entry", "Version",                   NULL, 0, NULL},
347
1.94k
                {}
348
1.94k
        };
349
350
1.94k
        r = config_parse(NULL, service->path, NULL,
351
1.94k
                         "Desktop Entry\0",
352
1.94k
                         xdg_config_item_table_lookup, items,
353
1.94k
                         CONFIG_PARSE_RELAXED | CONFIG_PARSE_WARN,
354
1.94k
                         service,
355
1.94k
                         NULL);
356
        /* If parsing failed, only hide the file so it will still mask others. */
357
1.94k
        if (r < 0) {
358
338
                log_warning_errno(r, "Failed to parse %s, ignoring it", service->path);
359
338
                service->hidden = true;
360
338
        }
361
362
1.94k
        return TAKE_PTR(service);
363
1.94k
}
364
365
int xdg_autostart_format_exec_start(
366
                const char *exec,
367
768
                char **ret_exec_start) {
368
369
768
        _cleanup_strv_free_ char **exec_split = NULL;
370
768
        char *res;
371
768
        size_t n, i;
372
768
        bool first_arg;
373
768
        int r;
374
375
        /*
376
         * Unfortunately, there is a mismatch between systemd's idea of $PATH and XDGs. I.e. we need to
377
         * ensure that we have an absolute path to support cases where $PATH has been modified from the
378
         * default set.
379
         *
380
         * Note that this is only needed for development environments though; so while it is important, this
381
         * should have no effect in production environments.
382
         *
383
         * To be compliant with the XDG specification, we also need to strip certain parameters and
384
         * such. Doing so properly makes parsing the command line unavoidable.
385
         *
386
         * NOTE: Technically, XDG only specifies " as quotes, while this also accepts '.
387
         */
388
768
        r = strv_split_full(&exec_split, exec, NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX);
389
768
        if (r < 0)
390
0
                return r;
391
392
768
        if (strv_isempty(exec_split))
393
22
                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Exec line is empty");
394
395
746
        first_arg = true;
396
746
        for (i = n = 0; exec_split[i]; i++) {
397
746
                _cleanup_free_ char *c = NULL, *raw = NULL, *percent = NULL, *tilde_expanded = NULL;
398
746
                ssize_t l;
399
400
746
                l = cunescape(exec_split[i], 0, &c);
401
746
                if (l < 0)
402
187
                        return log_debug_errno(l, "Failed to unescape '%s': %m", exec_split[i]);
403
404
559
                if (first_arg) {
405
559
                        _cleanup_free_ char *executable = NULL;
406
407
                        /* This is the executable, find it in $PATH */
408
559
                        first_arg = false;
409
559
                        r = find_executable(c, &executable);
410
559
                        if (r < 0)
411
559
                                return log_info_errno(r, "Exec binary '%s' does not exist: %m", c);
412
413
0
                        free_and_replace(exec_split[n++], executable);
414
0
                        continue;
415
559
                }
416
417
                /*
418
                 * Remove any standardised XDG fields; we assume they never appear as part of another
419
                 * argument as that just does not make any sense as they can be empty (GLib will e.g. turn
420
                 * "%f" into an empty argument).  Other implementations may handle this differently.
421
                 */
422
0
                if (STR_IN_SET(c,
423
0
                               "%f", "%F",
424
0
                               "%u", "%U",
425
0
                               "%d", "%D",
426
0
                               "%n", "%N",
427
0
                               "%i",          /* Location of icon, could be implemented. */
428
0
                               "%c",          /* Translated application name, could be implemented. */
429
0
                               "%k",          /* Location of desktop file, could be implemented. */
430
0
                               "%v",
431
0
                               "%m"
432
0
                               ))
433
0
                        continue;
434
435
                /*
436
                 * %% -> % and then % -> %% means that we correctly quote any % and also quote any left over
437
                 * (and invalid) % specifier from the desktop file.
438
                 */
439
0
                raw = strreplace(c, "%%", "%");
440
0
                if (!raw)
441
0
                        return log_oom();
442
0
                percent = strreplace(raw, "%", "%%");
443
0
                if (!percent)
444
0
                        return log_oom();
445
446
                /*
447
                 * Expand ~ if it comes at the beginning of an argument to form a path.
448
                 *
449
                 * The specification does not mandate this, but we do it anyway for compatibility with
450
                 * older KDE code, which supported a more shell-like syntax for users making custom entries.
451
                 */
452
0
                if (percent[0] == '~' && (isempty(percent + 1) || path_is_absolute(percent + 1))) {
453
0
                        _cleanup_free_ char *home = NULL;
454
455
0
                        r = get_home_dir(&home);
456
0
                        if (r < 0)
457
0
                                return r;
458
459
0
                        tilde_expanded = path_join(home, &percent[1]);
460
0
                        if (!tilde_expanded)
461
0
                                return log_oom();
462
0
                        free_and_replace(exec_split[n++], tilde_expanded);
463
0
                } else
464
0
                        free_and_replace(exec_split[n++], percent);
465
0
        }
466
0
        for (; exec_split[n]; n++)
467
0
                exec_split[n] = mfree(exec_split[n]);
468
469
0
        res = quote_command_line(exec_split, SHELL_ESCAPE_EMPTY);
470
0
        if (!res)
471
0
                return log_oom();
472
473
0
        *ret_exec_start = res;
474
0
        return 0;
475
0
}
476
477
static int xdg_autostart_generate_desktop_condition(
478
                const XdgAutostartService *service,
479
                FILE *f,
480
                const char *test_binary,
481
0
                const char *condition) {
482
483
0
        int r;
484
485
        /* Generate an ExecCondition for GNOME autostart condition */
486
0
        if (!isempty(condition)) {
487
0
                _cleanup_free_ char *gnome_autostart_condition_path = NULL, *e_autostart_condition = NULL;
488
489
0
                r = find_executable(test_binary, &gnome_autostart_condition_path);
490
0
                if (r < 0) {
491
0
                        log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
492
0
                                       "%s: ExecCondition executable %s not found, unit will not be started automatically: %m",
493
0
                                       service->path, test_binary);
494
0
                        fprintf(f, "# ExecCondition using %s skipped due to missing binary.\n", test_binary);
495
0
                        return 0;
496
0
                }
497
498
0
                e_autostart_condition = cescape(condition);
499
0
                if (!e_autostart_condition)
500
0
                        return log_oom();
501
502
0
                log_debug("%s: ExecCondition converted to %s --condition \"%s\"%s",
503
0
                          service->path, gnome_autostart_condition_path, e_autostart_condition,
504
0
                          glyph(GLYPH_ELLIPSIS));
505
506
0
                fprintf(f,
507
0
                         "ExecCondition=%s --condition \"%s\"\n",
508
0
                         gnome_autostart_condition_path,
509
0
                         e_autostart_condition);
510
0
        }
511
512
0
        return 0;
513
0
}
514
515
int xdg_autostart_service_generate_unit(
516
                const XdgAutostartService *service,
517
1.94k
                const char *dest) {
518
519
1.94k
        _cleanup_free_ char *path_escaped = NULL, *exec_start = NULL;
520
1.94k
        _cleanup_fclose_ FILE *f = NULL;
521
1.94k
        _cleanup_strv_free_ char **only_show_in = NULL, **not_show_in = NULL;
522
1.94k
        int r;
523
524
1.94k
        assert(service);
525
526
        /* Nothing to do for hidden services. */
527
1.94k
        if (service->hidden) {
528
350
                log_debug("%s: not generating unit, entry is hidden.", service->path);
529
350
                return 0;
530
350
        }
531
532
1.59k
        if (service->systemd_skip) {
533
1
                log_debug("%s: not generating unit, marked as skipped by generator.", service->path);
534
1
                return 0;
535
1
        }
536
537
        /* Nothing to do if type is not Application. */
538
1.59k
        if (!streq_ptr(service->type, "Application")) {
539
817
                log_debug("%s: not generating unit, Type=%s is not supported.", service->path, service->type);
540
817
                return 0;
541
817
        }
542
543
774
        if (!service->exec_string) {
544
1
                log_warning("%s: not generating unit, no Exec= line.", service->path);
545
1
                return 0;
546
1
        }
547
548
773
        if (service->only_show_in) {
549
5
                only_show_in = strv_copy(service->only_show_in);
550
5
                if (!only_show_in)
551
0
                        return log_oom();
552
5
        }
553
554
773
        if (service->not_show_in) {
555
45
                not_show_in = strv_copy(service->not_show_in);
556
45
                if (!not_show_in)
557
0
                        return log_oom();
558
45
        }
559
560
        /* The TryExec key cannot be checked properly from the systemd unit, it is trivial to check using
561
         * find_executable though. */
562
773
        if (service->try_exec) {
563
5
                r = find_executable(service->try_exec, NULL);
564
5
                if (r < 0) {
565
5
                        log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
566
5
                                       "%s: not generating unit, could not find TryExec= binary %s: %m",
567
5
                                       service->path, service->try_exec);
568
5
                        return 0;
569
5
                }
570
5
        }
571
572
768
        r = xdg_autostart_format_exec_start(service->exec_string, &exec_start);
573
768
        if (r < 0) {
574
768
                log_full_errno(r == -ENOENT ? LOG_INFO : LOG_WARNING, r,
575
768
                               r == -ENOENT ? "%s: not generating unit, executable specified in Exec= does not exist."
576
768
                                            : "%s: not generating unit, error parsing Exec= line: %m",
577
768
                               service->path);
578
768
                return 0;
579
768
        }
580
581
0
        if (service->gnome_autostart_phase) {
582
                /* There is no explicit value for the "Application" phase.
583
                 *
584
                 * On GNOME secondary startup mechanism handles desktop files with startup phases set.
585
                 * We want to mark these as "NotShowIn=GNOME"
586
                 *
587
                 * If that means no-one will load them, we can get skip it entirely.
588
                 */
589
0
                if (strv_contains(only_show_in, "GNOME")) {
590
0
                        strv_remove(only_show_in, "GNOME");
591
592
0
                        if (strv_isempty(only_show_in)) {
593
0
                                log_debug("%s: GNOME startup phases are handled separately. Skipping.",
594
0
                                          service->path);
595
0
                                return 0;
596
0
                        }
597
0
                }
598
0
                log_debug("%s: GNOME startup phases are handled separately, marking as NotShowIn=GNOME.",
599
0
                          service->path);
600
601
0
                if (strv_extend(&not_show_in, "GNOME") < 0)
602
0
                        return log_oom();
603
0
        }
604
605
0
        path_escaped = specifier_escape(service->path);
606
0
        if (!path_escaped)
607
0
                return log_oom();
608
609
0
        r = generator_open_unit_file(dest, /* source= */ NULL, service->name, &f);
610
0
        if (r < 0)
611
0
                return r;
612
613
0
        fprintf(f,
614
0
                "[Unit]\n"
615
0
                "Documentation=man:systemd-xdg-autostart-generator(8)\n"
616
0
                "SourcePath=%s\n"
617
0
                "PartOf=graphical-session.target\n\n",
618
0
                path_escaped);
619
620
0
        if (service->description) {
621
0
                _cleanup_free_ char *t = NULL;
622
623
0
                t = specifier_escape(service->description);
624
0
                if (!t)
625
0
                        return log_oom();
626
627
0
                fprintf(f, "Description=%s\n", t);
628
0
        }
629
630
        /* Only start after the session is ready. */
631
0
        fprintf(f,
632
0
                "After=graphical-session.target\n");
633
634
0
        fprintf(f,
635
0
                "\n[Service]\n"
636
0
                "Type=exec\n"
637
0
                "ExitType=cgroup\n"
638
0
                "ExecStart=:%s\n"
639
0
                "Restart=no\n"
640
0
                "TimeoutStopSec=5s\n"
641
0
                "Slice=app.slice\n",
642
0
                exec_start);
643
644
0
        if (service->working_directory) {
645
0
                _cleanup_free_ char *e_working_directory = NULL;
646
647
0
                e_working_directory = cescape(service->working_directory);
648
0
                if (!e_working_directory)
649
0
                        return log_oom();
650
651
0
                fprintf(f, "WorkingDirectory=-%s\n", e_working_directory);
652
0
        }
653
654
        /* Generate an ExecCondition to check $XDG_CURRENT_DESKTOP */
655
0
        if (!strv_isempty(only_show_in) || !strv_isempty(not_show_in)) {
656
0
                _cleanup_free_ char *only_show_in_string = NULL, *not_show_in_string = NULL, *e_only_show_in = NULL, *e_not_show_in = NULL;
657
658
0
                only_show_in_string = strv_join(only_show_in, ":");
659
0
                not_show_in_string = strv_join(not_show_in, ":");
660
0
                if (!only_show_in_string || !not_show_in_string)
661
0
                        return log_oom();
662
663
0
                e_only_show_in = cescape(only_show_in_string);
664
0
                e_not_show_in = cescape(not_show_in_string);
665
0
                if (!e_only_show_in || !e_not_show_in)
666
0
                        return log_oom();
667
668
                /* Just assume the values are reasonably sane */
669
0
                fprintf(f,
670
0
                        "ExecCondition=" LIBEXECDIR "/systemd-xdg-autostart-condition \"%s\" \"%s\"\n",
671
0
                        e_only_show_in,
672
0
                        e_not_show_in);
673
0
        }
674
675
0
        r = xdg_autostart_generate_desktop_condition(service, f,
676
0
                                                     "gnome-systemd-autostart-condition",
677
0
                                                     service->autostart_condition);
678
0
        if (r < 0)
679
0
                return r;
680
681
0
        r = xdg_autostart_generate_desktop_condition(service, f,
682
0
                                                     "kde-systemd-start-condition",
683
0
                                                     service->kde_autostart_condition);
684
0
        if (r < 0)
685
0
                return r;
686
687
0
        r = fflush_and_check(f);
688
0
        if (r < 0)
689
0
                return log_error_errno(r, "Failed to write unit %s: %m", service->name);
690
691
0
        log_debug("%s: symlinking %s in xdg-desktop-autostart.target/.wants%s",
692
0
                  service->path, service->name, glyph(GLYPH_ELLIPSIS));
693
0
        return generator_add_symlink(dest, "xdg-desktop-autostart.target", "wants", service->name);
694
0
}