/* * Copyright © 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see . * * Authors: * Ted Gould */ #define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include int main (int argc, char * argv[]) { if (argc != 3) { fprintf(stderr, "Usage: %s \n", argv[0]); exit(EXIT_FAILURE); } /* Not we turn the pid into an integer and back so that we can ensure we don't get used for nefarious tasks. */ int pidval = atoi(argv[1]); if ((pidval < 1) || (pidval >= 32768)) { fprintf(stderr, "PID passed is invalid: %d\n", pidval); exit(EXIT_FAILURE); } /* Not we turn the oom value into an integer and back so that we can ensure we don't get used for nefarious tasks. */ int oomval = atoi(argv[2]); if ((oomval < -1000) || (oomval >= 1000)) { fprintf(stderr, "OOM Value passed is invalid: %d\n", oomval); exit(EXIT_FAILURE); } /* Open up the PID directory first, to ensure that it is actually one of ours, so that we can't be used to set a OOM value on just anything */ char pidpath[32]; snprintf(pidpath, sizeof(pidpath), "/proc/%d", pidval); int piddir = open(pidpath, O_RDONLY | O_DIRECTORY); if (piddir < 0) { fprintf(stderr, "Unable open PID directory '%s' for '%d': %s\n", pidpath, pidval, strerror(errno)); exit(EXIT_FAILURE); } struct stat piddirstat = {0}; if (fstat(piddir, &piddirstat) < 0) { close(piddir); fprintf(stderr, "Unable stat PID directory '%s' for '%d': %s\n", pidpath, pidval, strerror(errno)); exit(EXIT_FAILURE); } if (getuid() != piddirstat.st_uid) { close(piddir); fprintf(stderr, "PID directory '%s' is not owned by %d but by %d\n", pidpath, getuid(), piddirstat.st_uid); exit(EXIT_FAILURE); } /* Looks good, let's try to get the actual oom_adj_score file to write the value to it. */ int adj = openat(piddir, "oom_score_adj", O_WRONLY); int openerr = errno; if (adj < 0) { close(piddir); /* ENOENT happens a fair amount because of races, so it's not worth printing a warning about */ if (openerr != ENOENT) { fprintf(stderr, "Unable to set OOM value of '%d' on '%d': %s\n", oomval, pidval, strerror(openerr)); exit(EXIT_FAILURE); } else { exit(EXIT_SUCCESS); } } char oomstring[32]; snprintf(oomstring, sizeof(oomstring), "%d", oomval); size_t writesize = write(adj, oomstring, strlen(oomstring)); int writeerr = errno; close(adj); close(piddir); if (writesize == strlen(oomstring)) exit(EXIT_SUCCESS); if (writeerr != 0) fprintf(stderr, "Unable to set OOM value of '%d' on '%d': %s\n", oomval, pidval, strerror(writeerr)); else /* No error, but yet, wrong size. Not sure, what could cause this. */ fprintf(stderr, "Unable to set OOM value of '%d' on '%d': Wrote %d bytes\n", oomval, pidval, (int)writesize); exit(EXIT_FAILURE); }