1 /* Tests for posix_spawn cgroup extension.
2 Copyright (C) 2023-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
26 #include <support/check.h>
27 #include <support/support.h>
28 #include <support/xstdio.h>
29 #include <support/xunistd.h>
30 #include <support/temp_file.h>
35 #define CGROUPFS "/sys/fs/cgroup/"
36 #ifndef CGROUP2_SUPER_MAGIC
37 # define CGROUP2_SUPER_MAGIC 0x63677270
40 #define F_TYPE_EQUAL(a, b) (a == (typeof (a)) b)
42 #define CGROUP_TEST "test-spawn-cgroup"
44 /* Nonzero if the program gets called via `exec'. */
45 #define CMDLINE_OPTIONS \
46 { "restart", no_argument, &restart, 1 },
49 /* Hold the four initial argument used to respawn the process, plus the extra
50 '--direct', '--restart', the check type ('SIG_IGN' or 'SIG_DFL'), and a
52 static char *spargs
[8];
55 startswith (const char *s
, const char *prefix
)
57 size_t l
= strlen (prefix
);
58 if (strncmp (s
, prefix
, l
) == 0)
59 return (char *) s
+ l
;
66 FILE *f
= fopen ("/proc/self/cgroup", "re");
68 FAIL_UNSUPPORTED ("no cgroup defined for the process: %m");
74 while (xgetline (&line
, &linesiz
, f
) > 0)
76 char *entry
= startswith (line
, "0:");
80 entry
= strchr (entry
, ':');
85 size_t l
= strlen (cgroup
);
86 if (cgroup
[l
- 1] == '\n')
89 cgroup
= xstrdup (entry
+ 1);
100 /* Called on process re-execution. */
102 handle_restart (int argc
, char *argv
[])
105 char *newcgroup
= argv
[0];
107 char *current_cgroup
= get_cgroup ();
108 TEST_VERIFY_EXIT (current_cgroup
!= NULL
);
109 TEST_COMPARE_STRING (newcgroup
, current_cgroup
);
113 do_test_cgroup_failure (pid_t
*pid
, int cgroup
)
115 posix_spawnattr_t attr
;
116 TEST_COMPARE (posix_spawnattr_init (&attr
), 0);
117 TEST_COMPARE (posix_spawnattr_setflags (&attr
, POSIX_SPAWN_SETCGROUP
), 0);
118 TEST_COMPARE (posix_spawnattr_setcgroup_np (&attr
, cgroup
), 0);
121 TEST_COMPARE (posix_spawnattr_getcgroup_np (&attr
, &cgetgroup
), 0);
122 TEST_COMPARE (cgroup
, cgetgroup
);
124 return posix_spawn (pid
, spargs
[0], NULL
, &attr
, spargs
, environ
);
128 create_new_cgroup (char **newcgroup
)
131 if (statfs (CGROUPFS
, &fs
) < 0)
134 FAIL_UNSUPPORTED ("no cgroupv2 mount found");
135 FAIL_EXIT1 ("statfs (%s): %m\n", CGROUPFS
);
138 if (!F_TYPE_EQUAL (fs
.f_type
, CGROUP2_SUPER_MAGIC
))
139 FAIL_UNSUPPORTED ("%s is not a cgroupv2 (expected %#jx, got %#jx)",
140 CGROUPFS
, (intmax_t) CGROUP2_SUPER_MAGIC
,
141 (intmax_t) fs
.f_type
);
143 char *cgroup
= get_cgroup ();
144 TEST_VERIFY_EXIT (cgroup
!= NULL
);
145 *newcgroup
= xasprintf ("%s/%s", cgroup
, CGROUP_TEST
);
146 char *cgpath
= xasprintf ("%s%s/%s", CGROUPFS
, cgroup
, CGROUP_TEST
);
149 if (mkdir (cgpath
, 0755) == -1 && errno
!= EEXIST
)
151 if (errno
== EACCES
|| errno
== EPERM
|| errno
== EROFS
)
152 FAIL_UNSUPPORTED ("can not create a new cgroupv2 group");
153 FAIL_EXIT1 ("mkdir (%s): %m", cgpath
);
155 add_temp_file (cgpath
);
157 return xopen (cgpath
, O_DIRECTORY
| O_RDONLY
| O_CLOEXEC
, 0666);
161 do_test (int argc
, char *argv
[])
163 /* We must have either:
165 - one or four parameters if called initially:
166 + argv[1]: path for ld.so optional
167 + argv[2]: "--library-path" optional
168 + argv[3]: the library path optional
169 + argv[4]: the application name
171 - six parameters left if called through re-execution:
172 + argv[4/1]: the application name
173 + argv[5/2]: the created cgroup
175 * When built with --enable-hardcoded-path-in-tests or issued without
176 using the loader directly. */
180 handle_restart (argc
- 1, &argv
[1]);
184 TEST_VERIFY_EXIT (argc
== 2 || argc
== 5);
187 int cgroup
= create_new_cgroup (&newcgroup
);
190 for (i
= 0; i
< argc
- 1; i
++)
191 spargs
[i
] = argv
[i
+ 1];
192 spargs
[i
++] = (char *) "--direct";
193 spargs
[i
++] = (char *) "--restart";
194 spargs
[i
++] = (char *) newcgroup
;
197 /* Check if invalid cgroups returns an error. */
199 int r
= do_test_cgroup_failure (NULL
, -1);
201 FAIL_UNSUPPORTED ("posix_spawn POSIX_SPAWN_SETCGROUP is not supported");
202 TEST_COMPARE (r
, EINVAL
);
207 TEST_COMPARE (do_test_cgroup_failure (&pid
, cgroup
), 0);
210 TEST_COMPARE (waitid (P_PID
, pid
, &sinfo
, WEXITED
), 0);
211 TEST_COMPARE (sinfo
.si_signo
, SIGCHLD
);
212 TEST_COMPARE (sinfo
.si_code
, CLD_EXITED
);
213 TEST_COMPARE (sinfo
.si_status
, 0);
222 #define TEST_FUNCTION_ARGV do_test
223 #include <support/test-driver.c>