Merge lp:~jdstrand/snap-confine/fix-udev-for-1604 into lp:~snappy-dev/snap-confine/trunk
- fix-udev-for-1604
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 125 | ||||
Proposed branch: | lp:~jdstrand/snap-confine/fix-udev-for-1604 | ||||
Merge into: | lp:~snappy-dev/snap-confine/trunk | ||||
Diff against target: |
437 lines (+181/-109) 6 files modified
README (+26/-3) debian/changelog (+21/-0) debian/usr.bin.ubuntu-core-launcher (+4/-6) src/80-snappy-assign.rules (+1/-1) src/main.c (+127/-98) src/snappy-app-dev (+2/-1) |
||||
To merge this branch: | bzr merge lp:~jdstrand/snap-confine/fix-udev-for-1604 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tyler Hicks (community) | Needs Fixing | ||
Review via email: mp+291028@code.launchpad.net |
Commit message
Description of the change
* update cgroup handling for 16.04 (LP: #1564401):
- debian/
+ allow creating cgroups with snap.*
+ allow ixr of 'tr'
+ remove access to /var/lib/
- update README to more fully explain the cgroups implementation
- src/80-
adding a generic tag and snap-specific property
- src/snappy-app-dev: convert the new tag to the directory name
- src/main.c:
+ refactor and simplify control flow to query udev for device assignment
instead of searching apparmor policy for a specific string
+ adjust udev query for app-specific tag
+ raise real_uid after fork() before calling /lib/udev/
so non-root app launches work with the device cgroup
Leo Arias (elopio) : | # |
- 123. By Jamie Strandboge
-
address typo pointed out be Leo
Jamie Strandboge (jdstrand) wrote : | # |
Tyler Hicks (tyhicks) wrote : | # |
See inline comments.
Tyler Hicks (tyhicks) wrote : | # |
I'll also note that, due to release deadlines, I did not have sufficient time to review the greater design changes that are occurring due to this MP. I really only had a chance to review the code changes.
- 124. By Jamie Strandboge
-
merge from trunk
Jamie Strandboge (jdstrand) wrote : | # |
>> +const char *static_devices[] = {
>
> I may be missing something but I don't see why this array was lifted from the function that uses it and turned into a global. If it is to remain as global, it should be declared as "static const char *static_devices[]" since it isn't used outside of main.c.
Ah, yes, this should have been moved back down after I made the code simpler.
Jamie Strandboge (jdstrand) wrote : | # |
> This string test is incomplete for a couple reasons:
>
> 1) tagname_len could be greater than the size of the tagname buffer
> 2) the tagname buffer could be unterminated
True. I was thinking about the must_snprintf() that was called to set it would have taken care of this, but if we are doing all these checks for future-proof coding, we should do all the checks.
Jamie Strandboge (jdstrand) wrote : | # |
>> Is it intentional that we don't attempt to setuid(0) (or fail?) if the first conditional is not met? It seems correct to continue on if real_uid is 0 but what about when effective_uid is not 0? I guess snappy-app-dev will simply fail due to insufficient perms and everything will be ok.
Yes, it will fail due to insufficient perms and yes this was intentional. The idea here is to raise if we can but not if we can't. The testsuite will have euid as non-zero, under sudo real_uid and euid are 0, and under suid launcher euid is 0 but real_uid is not.
- 125. By Jamie Strandboge
-
move static_devices into setup_devices_
cgroup( ), where it should be - 126. By Jamie Strandboge
-
src/main.c: further futreproof our tagname checks. Patch thanks to Tyler Hicks
- 127. By Jamie Strandboge
-
src/main.c: simplify execl() failure
- 128. By Jamie Strandboge
-
use __func__ in debug() statements
Jamie Strandboge (jdstrand) wrote : | # |
Thanks for the review!
Preview Diff
1 | === modified file 'README' |
2 | --- README 2016-04-13 21:10:41 +0000 |
3 | +++ README 2016-04-14 23:03:26 +0000 |
4 | @@ -46,9 +46,32 @@ |
5 | == devices cgroup == |
6 | |
7 | It works like this: |
8 | -- on install of snaps with a special hardware:\n assign yaml udev rules are generated that add tags and properties to the matching hardware |
9 | -- this launcher creates a device cgroup that is deny-all by default, then adds itself to the group and also adds all devices that match the given snappy-assign tag and the appname property |
10 | -- the app is executed and now the normal device permissions/apparmor rules apply |
11 | +- on install of snaps with a special hardware: assign yaml udev rules are |
12 | + generated that add tags to matching hardware. These assign rules are added to |
13 | + udev via /etc/udev/rules.d/70-snap.... for each app within a snap. The tags |
14 | + are of the form 'snap_<snap name>_<app>'. |
15 | +- when an application is launched, the launcher queries udev to detect if any |
16 | + devices are tagged for this application. If no devices are tagged for this |
17 | + application, a device cgroup is not setup |
18 | +- if there are devices tagged for this application, the launcher creates a |
19 | + device cgroup in /sys/fs/cgroups/devices/snap.<snap name>.<app> and adds |
20 | + itself to this cgroup. It then sets the cgroup as deny-all by default, adds |
21 | + some common devices (eg, /dev/null, /dev/zero, etc) and any devices tagged |
22 | + for use by this application using /lib/udev/snappy-app-dev |
23 | +- the app is executed and now the normal device permissions/apparmor rules |
24 | + apply |
25 | +- udev match rules in /lib/udev/rules.d/80-snapy-assign.rules are in place to |
26 | + run /lib/udev/snappy-app-dev to handle device events for devices tagged with |
27 | + snap_*. |
28 | + |
29 | +Note, /sys/fs/cgroups/devices/snap.<snap name>.<app> is not (currently) removed |
30 | +on unassignment and the contents of the cgroup for the app are managed entirely |
31 | +by the launcher. When an application is started, the cgroup is reset by |
32 | +removing all previously added devices and then the list of assigned devices is |
33 | +built back up before launch. In this manner, devices can be assigned, changed, |
34 | +and unassigned and the app will always get the correct device added to the |
35 | +cgroup, but what is in /sys/fs/cgroups/devices/snap.<snap name>.<app> will not |
36 | +reflect assignment/unassignment until after the application is started. |
37 | |
38 | |
39 | == private /tmp == |
40 | |
41 | === modified file 'debian/changelog' |
42 | --- debian/changelog 2016-04-14 22:18:12 +0000 |
43 | +++ debian/changelog 2016-04-14 23:03:26 +0000 |
44 | @@ -1,3 +1,24 @@ |
45 | +ubuntu-core-launcher (1.0.25) UNRELEASED; urgency=medium |
46 | + |
47 | + * update cgroup handling for 16.04 (LP: #1564401): |
48 | + - debian/usr.bin.ubuntu-core-launcher: |
49 | + + allow creating cgroups with snap.* |
50 | + + allow ixr of 'tr' |
51 | + + remove access to /var/lib/apparmor/clicks/ |
52 | + - update README to more fully explain the cgroups implementation |
53 | + - src/80-snappy-assign.rules: append an app-specific tag instead of |
54 | + adding a generic tag and snap-specific property |
55 | + - src/snappy-app-dev: convert the new tag to the directory name |
56 | + - src/main.c: |
57 | + + refactor and simplify control flow to query udev for device assignment |
58 | + instead of searching apparmor policy for a specific string |
59 | + + adjust udev query for app-specific tag |
60 | + + raise real_uid after fork() before calling /lib/udev/snappy-app-dev |
61 | + so non-root app launches work with the device cgroup |
62 | + |
63 | + |
64 | + -- Jamie Strandboge <jamie@ubuntu.com> Thu, 14 Apr 2016 17:18:54 -0500 |
65 | + |
66 | ubuntu-core-launcher (1.0.24) xenial; urgency=medium |
67 | |
68 | [ Michael Vogt ] |
69 | |
70 | === modified file 'debian/usr.bin.ubuntu-core-launcher' |
71 | --- debian/usr.bin.ubuntu-core-launcher 2016-04-11 23:10:01 +0000 |
72 | +++ debian/usr.bin.ubuntu-core-launcher 2016-04-14 23:03:26 +0000 |
73 | @@ -26,15 +26,16 @@ |
74 | # cgroups |
75 | capability sys_admin, |
76 | capability dac_override, |
77 | - /sys/fs/cgroup/devices/snappy.*/ w, |
78 | - /sys/fs/cgroup/devices/snappy.*/tasks w, |
79 | - /sys/fs/cgroup/devices/snappy.*/devices.{allow,deny} w, |
80 | + /sys/fs/cgroup/devices/snap{,py}.*/ w, |
81 | + /sys/fs/cgroup/devices/snap{,py}.*/tasks w, |
82 | + /sys/fs/cgroup/devices/snap{,py}.*/devices.{allow,deny} w, |
83 | |
84 | # querying udev |
85 | /etc/udev/udev.conf r, |
86 | /sys/devices/**/uevent r, |
87 | /lib/udev/snappy-app-dev ixr, # drop |
88 | /run/udev/** rw, |
89 | + /{,usr/}bin/tr ixr, |
90 | |
91 | # priv dropping |
92 | capability setuid, |
93 | @@ -66,9 +67,6 @@ |
94 | # reading seccomp filters |
95 | /var/lib/snapd/seccomp/profiles/* r, |
96 | |
97 | - # read apparmor to figure out if we need cgroups |
98 | - /var/lib/apparmor/clicks/* r, |
99 | - |
100 | # set up snap-specific private /tmp dir |
101 | capability chown, |
102 | /tmp/ w, |
103 | |
104 | === modified file 'src/80-snappy-assign.rules' |
105 | --- src/80-snappy-assign.rules 2015-04-18 14:54:33 +0000 |
106 | +++ src/80-snappy-assign.rules 2016-04-14 23:03:26 +0000 |
107 | @@ -1,2 +1,2 @@ |
108 | # add/remove snap package access to assigned devices |
109 | -TAG=="snappy-assign", RUN+="/lib/udev/snappy-app-dev $env{ACTION} $env{SNAPPY_APP} $devpath $major:$minor" |
110 | +TAG=="snap_*", RUN+="/lib/udev/snappy-app-dev $env{ACTION} $env{TAG} $devpath $major:$minor" |
111 | |
112 | === modified file 'src/main.c' |
113 | --- src/main.c 2016-04-14 20:47:41 +0000 |
114 | +++ src/main.c 2016-04-14 23:03:26 +0000 |
115 | @@ -38,6 +38,8 @@ |
116 | #include <fcntl.h> |
117 | #include <glob.h> |
118 | |
119 | +#include <ctype.h> |
120 | + |
121 | #include "libudev.h" |
122 | |
123 | #include "utils.h" |
124 | @@ -45,6 +47,14 @@ |
125 | |
126 | #define MAX_BUF 1000 |
127 | |
128 | +struct snappy_udev { |
129 | + struct udev *udev; |
130 | + struct udev_enumerate *devices; |
131 | + struct udev_list_entry *assigned; |
132 | + char tagname[MAX_BUF]; |
133 | + size_t tagname_len; |
134 | +}; |
135 | + |
136 | bool verify_appname(const char *appname) |
137 | { |
138 | // these chars are allowed in a appname |
139 | @@ -59,11 +69,22 @@ |
140 | return (status == 0); |
141 | } |
142 | |
143 | -void run_snappy_app_dev_add(struct udev *u, const char *path, |
144 | - const char *appname) |
145 | +void run_snappy_app_dev_add(struct snappy_udev *udev_s, const char *path) |
146 | { |
147 | - debug("run_snappy_app_dev_add: %s %s", path, appname); |
148 | - struct udev_device *d = udev_device_new_from_syspath(u, path); |
149 | + if (udev_s == NULL) |
150 | + die("snappy_udev is NULL"); |
151 | + if (udev_s->udev == NULL) |
152 | + die("snappy_udev->udev is NULL"); |
153 | + if (udev_s->tagname_len == 0 |
154 | + || udev_s->tagname_len >= MAX_BUF |
155 | + || strnlen(udev_s->tagname, MAX_BUF) != udev_s->tagname_len |
156 | + || udev_s->tagname[udev_s->tagname_len] != '\0') |
157 | + die("snappy_udev->tagname has invalid length"); |
158 | + |
159 | + debug("%s: %s %s", __func__, path, udev_s->tagname); |
160 | + |
161 | + struct udev_device *d = |
162 | + udev_device_new_from_syspath(udev_s->udev, path); |
163 | if (d == NULL) |
164 | die("can not find %s", path); |
165 | dev_t devnum = udev_device_get_devnum(d); |
166 | @@ -75,14 +96,21 @@ |
167 | die("could not fork"); |
168 | } |
169 | if (pid == 0) { |
170 | + uid_t real_uid, effective_uid, saved_uid; |
171 | + if (getresuid(&real_uid, &effective_uid, &saved_uid) != 0) |
172 | + die("could not find user IDs"); |
173 | + // can't update the cgroup unless the real_uid is 0, euid as |
174 | + // 0 is not enough |
175 | + if (real_uid != 0 && effective_uid == 0) |
176 | + if (setuid(0) != 0) |
177 | + die("setuid failed"); |
178 | char buf[64]; |
179 | unsigned major = MAJOR(devnum); |
180 | unsigned minor = MINOR(devnum); |
181 | must_snprintf(buf, sizeof(buf), "%u:%u", major, minor); |
182 | - if (execl |
183 | - ("/lib/udev/snappy-app-dev", "/lib/udev/snappy-app-dev", |
184 | - "add", appname, path, buf, NULL) != 0) |
185 | - die("execlp failed"); |
186 | + execl("/lib/udev/snappy-app-dev", "/lib/udev/snappy-app-dev", |
187 | + "add", udev_s->tagname, path, buf, NULL); |
188 | + die("execl failed"); |
189 | } |
190 | if (waitpid(pid, &status, 0) < 0) |
191 | die("waitpid failed"); |
192 | @@ -92,14 +120,64 @@ |
193 | die("child died with signal %i", WTERMSIG(status)); |
194 | } |
195 | |
196 | -void setup_udev_snappy_assign(const char *appname) |
197 | +/* |
198 | + * snappy_udev_init() - setup the snappy_udev structure. Return 0 if devices |
199 | + * are assigned, else return -1. Callers should use snappy_udev_cleanup() to |
200 | + * cleanup. |
201 | + */ |
202 | +int snappy_udev_init(const char *appname, struct snappy_udev *udev_s) |
203 | { |
204 | - debug("setup_udev_snappy_assign"); |
205 | - |
206 | - struct udev *u = udev_new(); |
207 | - if (u == NULL) |
208 | + debug("%s", __func__); |
209 | + int rc = 0; |
210 | + |
211 | + // extra paranoia |
212 | + if (!verify_appname(appname)) |
213 | + die("appname %s not allowed", appname); |
214 | + |
215 | + udev_s->tagname[0] = '\0'; |
216 | + udev_s->tagname_len = 0; |
217 | + // TAG+="snap_<appname>" (udev doesn't like '.' in the tag name) |
218 | + udev_s->tagname_len = must_snprintf(udev_s->tagname, MAX_BUF, |
219 | + "snap_%s", appname); |
220 | + for (int i = 0; i < udev_s->tagname_len; i++) |
221 | + if (udev_s->tagname[i] == '.') |
222 | + udev_s->tagname[i] = '_'; |
223 | + |
224 | + udev_s->udev = udev_new(); |
225 | + if (udev_s->udev == NULL) |
226 | die("udev_new failed"); |
227 | |
228 | + udev_s->devices = udev_enumerate_new(udev_s->udev); |
229 | + if (udev_s->devices == NULL) |
230 | + die("udev_enumerate_new failed"); |
231 | + |
232 | + if (udev_enumerate_add_match_tag(udev_s->devices, udev_s->tagname) != 0) |
233 | + die("udev_enumerate_add_match_tag"); |
234 | + |
235 | + if (udev_enumerate_scan_devices(udev_s->devices) != 0) |
236 | + die("udev_enumerate_scan failed"); |
237 | + |
238 | + udev_s->assigned = udev_enumerate_get_list_entry(udev_s->devices); |
239 | + if (udev_s->assigned == NULL) |
240 | + rc = -1; |
241 | + |
242 | + return rc; |
243 | +} |
244 | + |
245 | +void snappy_udev_cleanup(struct snappy_udev *udev_s) |
246 | +{ |
247 | + // udev_s->assigned does not need to be unreferenced since it is a |
248 | + // pointer into udev_s->devices |
249 | + if (udev_s->devices != NULL) |
250 | + udev_enumerate_unref(udev_s->devices); |
251 | + if (udev_s->udev != NULL) |
252 | + udev_unref(udev_s->udev); |
253 | +} |
254 | + |
255 | +void setup_devices_cgroup(const char *appname, struct snappy_udev *udev_s) |
256 | +{ |
257 | + debug("%s", __func__); |
258 | + // Devices that must always be present |
259 | const char *static_devices[] = { |
260 | "/sys/class/mem/null", |
261 | "/sys/class/mem/full", |
262 | @@ -111,50 +189,29 @@ |
263 | "/sys/class/tty/ptmx", |
264 | NULL, |
265 | }; |
266 | - int i; |
267 | - for (i = 0; static_devices[i] != NULL; i++) { |
268 | - run_snappy_app_dev_add(u, static_devices[i], appname); |
269 | - } |
270 | - |
271 | - struct udev_enumerate *devices = udev_enumerate_new(u); |
272 | - if (devices == NULL) |
273 | - die("udev_enumerate_new failed"); |
274 | - |
275 | - if (udev_enumerate_add_match_tag(devices, "snappy-assign") != 0) |
276 | - die("udev_enumerate_add_match_tag"); |
277 | - |
278 | - if (udev_enumerate_add_match_property(devices, "SNAPPY_APP", appname) != |
279 | - 0) |
280 | - die("udev_enumerate_add_match_property"); |
281 | - |
282 | - if (udev_enumerate_scan_devices(devices) != 0) |
283 | - die("udev_enumerate_scan failed"); |
284 | - |
285 | - struct udev_list_entry *l = udev_enumerate_get_list_entry(devices); |
286 | - while (l != NULL) { |
287 | - const char *path = udev_list_entry_get_name(l); |
288 | - if (path == NULL) |
289 | - die("udev_list_entry_get_name failed"); |
290 | - run_snappy_app_dev_add(u, path, appname); |
291 | - l = udev_list_entry_get_next(l); |
292 | - } |
293 | - |
294 | - udev_enumerate_unref(devices); |
295 | - udev_unref(u); |
296 | -} |
297 | - |
298 | -void setup_devices_cgroup(const char *appname) |
299 | -{ |
300 | - debug("setup_devices_cgroup"); |
301 | |
302 | // extra paranoia |
303 | if (!verify_appname(appname)) |
304 | die("appname %s not allowed", appname); |
305 | + if (udev_s == NULL) |
306 | + die("snappy_udev is NULL"); |
307 | + if (udev_s->udev == NULL) |
308 | + die("snappy_udev->udev is NULL"); |
309 | + if (udev_s->devices == NULL) |
310 | + die("snappy_udev->devices is NULL"); |
311 | + if (udev_s->assigned == NULL) |
312 | + die("snappy_udev->assigned is NULL"); |
313 | + if (udev_s->tagname_len == 0 |
314 | + || udev_s->tagname_len >= MAX_BUF |
315 | + || strnlen(udev_s->tagname, MAX_BUF) != udev_s->tagname_len |
316 | + || udev_s->tagname[udev_s->tagname_len] != '\0') |
317 | + die("snappy_udev->tagname has invalid length"); |
318 | |
319 | // create devices cgroup controller |
320 | char cgroup_dir[PATH_MAX]; |
321 | + |
322 | must_snprintf(cgroup_dir, sizeof(cgroup_dir), |
323 | - "/sys/fs/cgroup/devices/snappy.%s/", appname); |
324 | + "/sys/fs/cgroup/devices/snap.%s/", appname); |
325 | |
326 | if (mkdir(cgroup_dir, 0755) < 0 && errno != EEXIST) |
327 | die("mkdir failed"); |
328 | @@ -168,55 +225,26 @@ |
329 | must_snprintf(buf, sizeof(buf), "%i", getpid()); |
330 | write_string_to_file(cgroup_file, buf); |
331 | |
332 | - // deny by default |
333 | + // deny by default. Write 'a' to devices.deny to remove all existing |
334 | + // devices that were added in previous launcher invocations, then add |
335 | + // the static and assigned devices. This ensures that at application |
336 | + // launch the cgroup only has what is currently assigned. |
337 | must_snprintf(cgroup_file, sizeof(cgroup_file), "%s%s", cgroup_dir, |
338 | "devices.deny"); |
339 | write_string_to_file(cgroup_file, "a"); |
340 | |
341 | -} |
342 | - |
343 | -bool snappy_udev_setup_required(const char *appname) |
344 | -{ |
345 | - debug("snappy_udev_setup_required"); |
346 | - |
347 | - // extra paranoia |
348 | - if (!verify_appname(appname)) |
349 | - die("appname %s not allowed", appname); |
350 | - |
351 | - char override_file[PATH_MAX]; |
352 | - must_snprintf(override_file, sizeof(override_file), |
353 | - "/var/lib/apparmor/clicks/%s.json.additional", appname); |
354 | - |
355 | - // if a snap package gets unrestricted apparmor access we need to setup |
356 | - // a device cgroup. |
357 | - // |
358 | - // the "needle" string is what gives this access so we search for that |
359 | - // here |
360 | - const char *needle = |
361 | - "{" "\n" |
362 | - " \"write_path\": [" "\n" |
363 | - " \"/dev/**\"" "\n" |
364 | - " ]," "\n" |
365 | - " \"read_path\": [" "\n" " \"/run/udev/data/*\"" "\n" " ]\n" "}"; |
366 | - debug("looking for: '%s'", needle); |
367 | - char content[strlen(needle)]; |
368 | - |
369 | - int fd = open(override_file, O_CLOEXEC | O_NOFOLLOW | O_RDONLY); |
370 | - if (fd < 0) |
371 | - return false; |
372 | - int n = read(fd, content, sizeof(content)); |
373 | - if (close(fd) != 0) |
374 | - die("could not close override"); |
375 | - if (n < sizeof(content)) |
376 | - return false; |
377 | - |
378 | - // memcpy so that we don't have to deal with \0 in the input |
379 | - if (memcmp(content, needle, strlen(needle)) == 0) { |
380 | - debug("found needle, need to apply udev setup"); |
381 | - return true; |
382 | + // add the common devices |
383 | + for (int i = 0; static_devices[i] != NULL; i++) |
384 | + run_snappy_app_dev_add(udev_s, static_devices[i]); |
385 | + |
386 | + // add the assigned devices |
387 | + while (udev_s->assigned != NULL) { |
388 | + const char *path = udev_list_entry_get_name(udev_s->assigned); |
389 | + if (path == NULL) |
390 | + die("udev_list_entry_get_name failed"); |
391 | + run_snappy_app_dev_add(udev_s, path); |
392 | + udev_s->assigned = udev_list_entry_get_next(udev_s->assigned); |
393 | } |
394 | - |
395 | - return false; |
396 | } |
397 | |
398 | bool is_running_on_classic_ubuntu() |
399 | @@ -313,7 +341,7 @@ |
400 | |
401 | void setup_snappy_os_mounts() |
402 | { |
403 | - debug("setup_snappy_os_mounts()\n"); |
404 | + debug("%s", __func__); |
405 | |
406 | // FIXME: hardcoded "ubuntu-core.*" |
407 | glob_t glob_res; |
408 | @@ -503,10 +531,11 @@ |
409 | setup_private_pts(); |
410 | |
411 | // this needs to happen as root |
412 | - if (snappy_udev_setup_required(appname)) { |
413 | - setup_devices_cgroup(appname); |
414 | - setup_udev_snappy_assign(appname); |
415 | - } |
416 | + struct snappy_udev udev_s; |
417 | + if (snappy_udev_init(appname, &udev_s) == 0) |
418 | + setup_devices_cgroup(appname, &udev_s); |
419 | + snappy_udev_cleanup(&udev_s); |
420 | + |
421 | // the rest does not so temporarily drop privs back to calling |
422 | // user (we'll permanently drop after loading seccomp) |
423 | if (setegid(real_gid) != 0) |
424 | |
425 | === modified file 'src/snappy-app-dev' |
426 | --- src/snappy-app-dev 2015-04-17 22:23:37 +0000 |
427 | +++ src/snappy-app-dev 2016-04-14 23:03:26 +0000 |
428 | @@ -15,7 +15,8 @@ |
429 | [ -n "$DEVPATH" ] || { echo "no devpath given" >&2; exit 1; } |
430 | [ -n "$MAJMIN" ] || { echo "no major/minor given" >&2; exit 0; } |
431 | |
432 | -app_dev_cgroup="/sys/fs/cgroup/devices/snappy.$APPNAME" |
433 | +APPNAME=`echo $APPNAME | tr '_' '.'` |
434 | +app_dev_cgroup="/sys/fs/cgroup/devices/$APPNAME" |
435 | |
436 | # check if it's a block or char dev |
437 | if [ "${DEVPATH#*/block/}" != "$DEVPATH" ]; then |
Thanks Leo, pushed in r123.