blob: f6cf1b5eab7cd5fd856e9f5764d19bc65788e259 [file] [log] [blame]
plars865695b2001-08-27 22:15:12 +00001/*
2 * proc01.c - Tests Linux /proc file reading.
3 *
4 * Copyright (C) 2001 Stephane Fillod <f4cfe@free.fr>
subrata_modakcf473b72009-02-05 11:15:57 +00005 * Copyright (c) 2008, 2009 Red Hat, Inc.
subrata_modak4bb656a2009-02-26 12:02:09 +00006 *
plars865695b2001-08-27 22:15:12 +00007 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of version 2 of the GNU General Public License as
9 * published by the Free Software Foundation.
subrata_modak4bb656a2009-02-26 12:02:09 +000010 *
plars865695b2001-08-27 22:15:12 +000011 * This program is distributed in the hope that it would be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
subrata_modak4bb656a2009-02-26 12:02:09 +000014 *
plars865695b2001-08-27 22:15:12 +000015 * Further, this software is distributed without any warranty that it is
16 * free of the rightful claim of any third person regarding infringement
17 * or the like. Any license provided herein, whether implied or
18 * otherwise, applies only to this software file. Patent licenses, if
19 * any, provided herein do not apply to combinations of this program with
20 * other software, or any other product whatsoever.
subrata_modak4bb656a2009-02-26 12:02:09 +000021 *
plars865695b2001-08-27 22:15:12 +000022 * You should have received a copy of the GNU General Public License along
23 * with this program; if not, write the Free Software Foundation, Inc., 59
24 * Temple Place - Suite 330, Boston MA 02111-1307, USA.
subrata_modak4bb656a2009-02-26 12:02:09 +000025 *
plars865695b2001-08-27 22:15:12 +000026 */
27
subrata_modakafe343d2009-02-05 11:20:49 +000028#include "config.h"
29
subrata_modakda124b92009-10-13 14:00:45 +000030#include <errno.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <limits.h>
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <dirent.h>
plars865695b2001-08-27 22:15:12 +000038#include <unistd.h>
39#include <fcntl.h>
subrata_modakca317e62007-11-13 09:25:05 +000040#include <fnmatch.h>
plars865695b2001-08-27 22:15:12 +000041
subrata_modak190c7ad2009-03-16 08:27:20 +000042#ifdef HAVE_LIBSELINUX_DEVEL
subrata_modakafe343d2009-02-05 11:20:49 +000043#include <selinux/selinux.h>
44#endif
45
plars865695b2001-08-27 22:15:12 +000046#include "test.h"
47#include "usctest.h"
48
plars865695b2001-08-27 22:15:12 +000049#define MAX_BUFF_SIZE 65536
subrata_modak8a9ecb72008-05-26 06:09:19 +000050#define MAX_FUNC_NAME 256
plars865695b2001-08-27 22:15:12 +000051
vapier10fe5d82006-08-21 07:16:54 +000052char *TCID = "proc01";
53int TST_TOTAL = 1;
plars865695b2001-08-27 22:15:12 +000054extern int Tst_count;
55
yaberauneyada963562009-11-15 08:21:45 +000056static int opt_verbose = 0;
57static int opt_procpath = 0;
58static char *opt_procpathstr;
59static int opt_buffsize = 0;
60static int opt_readirq = 0;
61static char *opt_buffsizestr;
plars865695b2001-08-27 22:15:12 +000062
yaberauneyada963562009-11-15 08:21:45 +000063static char *procpath = "/proc";
64static char selfpath[] = "/proc/self";
65size_t buffsize = 1024;
plars865695b2001-08-27 22:15:12 +000066
yaberauneyada963562009-11-15 08:21:45 +000067unsigned long long total_read = 0;
vapier10fe5d82006-08-21 07:16:54 +000068unsigned int total_obj = 0;
plars865695b2001-08-27 22:15:12 +000069
subrata_modakda124b92009-10-13 14:00:45 +000070struct mapping {
71 char func[MAX_FUNC_NAME];
72 char file[PATH_MAX];
73 int err;
subrata_modak8a9ecb72008-05-26 06:09:19 +000074};
subrata_modakda124b92009-10-13 14:00:45 +000075
subrata_modak8a9ecb72008-05-26 06:09:19 +000076typedef struct mapping Mapping;
77
78/* Those are known failures for 2.6.18 baremetal kernel and Xen dom0
79 kernel on i686, x86_64, ia64, ppc64 and s390x. In addition, It looks
80 like if SELinux is disabled, the test may still fail on some other
81 entries. */
yaberauneyada963562009-11-15 08:21:45 +000082const Mapping known_issues[] = {
subrata_modak8a9ecb72008-05-26 06:09:19 +000083 {"open", "/proc/acpi/event", EBUSY},
84 {"open", "/proc/sal/cpe/data", EBUSY},
85 {"open", "/proc/sal/cmc/data", EBUSY},
86 {"open", "/proc/sal/init/data", EBUSY},
87 {"open", "/proc/sal/mca/data", EBUSY},
88 {"read", "/proc/kmsg", EAGAIN},
89 {"read", "/proc/sal/cpe/event", EAGAIN},
90 {"read", "/proc/sal/cmc/event", EAGAIN},
91 {"read", "/proc/sal/init/event", EAGAIN},
92 {"read", "/proc/sal/mca/event", EAGAIN},
93 {"read", "/proc/xen/privcmd", EINVAL},
94 {"read", "/proc/self/mem", EIO},
95 {"read", "/proc/self/task/[0-9]*/mem", EIO},
subrata_modakcf473b72009-02-05 11:15:57 +000096 {"read", "/proc/self/attr/*", EINVAL},
97 {"read", "/proc/self/task/[0-9]*/attr/*", EINVAL},
98 {"read", "/proc/ppc64/rtas/error_log", EINVAL},
99 {"read", "/proc/fs/nfsd/unlock_filesystem", EINVAL},
100 {"read", "/proc/fs/nfsd/unlock_ip", EINVAL},
101 {"read", "/proc/fs/nfsd/filehandle", EINVAL},
102 {"read", "/proc/fs/nfsd/.getfs", EINVAL},
103 {"read", "/proc/fs/nfsd/.getfd", EINVAL},
subrata_modak8a9ecb72008-05-26 06:09:19 +0000104 {"", "", 0}
yaberauneyada963562009-11-15 08:21:45 +0000105};
subrata_modak8a9ecb72008-05-26 06:09:19 +0000106
subrata_modak190c7ad2009-03-16 08:27:20 +0000107/*
108 * If a particular LSM is enabled, it is expected that some entries can
109 * be read successfully. Otherwise, those entries will retrun some
110 * failures listed above. Here to add any LSM specific entries.
111 */
112
113/*
114 * Test macro to indicate that SELinux libraries and headers are
115 * installed.
116 */
117#ifdef HAVE_LIBSELINUX_DEVEL
yaberauneyada963562009-11-15 08:21:45 +0000118const char lsm_should_work[][PATH_MAX] = {
subrata_modakafe343d2009-02-05 11:20:49 +0000119 "/proc/self/attr/*",
120 "/proc/self/task/[0-9]*/attr/*",
121 ""
yaberauneyada963562009-11-15 08:21:45 +0000122};
subrata_modak190c7ad2009-03-16 08:27:20 +0000123/* Place holder for none of LSM is detected. */
subrata_modak13a21422009-02-16 05:44:04 +0000124#else
yaberauneyada963562009-11-15 08:21:45 +0000125const char lsm_should_work[][PATH_MAX] = {
subrata_modak13a21422009-02-16 05:44:04 +0000126 ""
yaberauneyada963562009-11-15 08:21:45 +0000127};
subrata_modak13a21422009-02-16 05:44:04 +0000128#endif
subrata_modakafe343d2009-02-05 11:20:49 +0000129
subrata_modak8a9ecb72008-05-26 06:09:19 +0000130/* Known files that does not honor O_NONBLOCK, so they will hang
subrata_modakafe343d2009-02-05 11:20:49 +0000131 the test while being read. */
yaberauneyada963562009-11-15 08:21:45 +0000132const char error_nonblock[][PATH_MAX] = {
subrata_modak8a9ecb72008-05-26 06:09:19 +0000133 "/proc/xen/xenbus",
134 ""
yaberauneyada963562009-11-15 08:21:45 +0000135};
subrata_modak8a9ecb72008-05-26 06:09:19 +0000136
yaberauneyada963562009-11-15 08:21:45 +0000137/*
138 * Verify expected failures, and then let the test to continue.
139 *
140 * Return 0 when a problem errno is found.
141 * Return 1 when a known issue is found.
142 *
subrata_modak190c7ad2009-03-16 08:27:20 +0000143 */
subrata_modak8a9ecb72008-05-26 06:09:19 +0000144int found_errno(const char *syscall, const char *obj, int tmperr)
145{
yaberauneyada963562009-11-15 08:21:45 +0000146 int i;
subrata_modak8a9ecb72008-05-26 06:09:19 +0000147
yaberauneyada963562009-11-15 08:21:45 +0000148 /* Should not see any error for certain entries if a LSM is enabled. */
149#ifdef HAVE_LIBSELINUX_DEVEL
150 if (is_selinux_enabled()) {
151 for (i = 0; lsm_should_work[i][0] != '\0'; i++) {
152 if (!strcmp(obj, lsm_should_work[i]) ||
153 !fnmatch(lsm_should_work[i], obj, FNM_PATHNAME)) {
154 return 0;
155 }
156 }
157 }
158#endif
159 for (i = 0; known_issues[i].err != 0; i++) {
160 if (tmperr == known_issues[i].err &&
161 (!strcmp(obj, known_issues[i].file) ||
162 !fnmatch(known_issues[i].file, obj, FNM_PATHNAME)) &&
163 !strcmp(syscall, known_issues[i].func)) {
164 /* Using strcmp / fnmatch could have messed up the
165 * errno value. */
166 errno = tmperr;
167 tst_resm(TINFO | TERRNO, "%s: known issue", obj);
168 return 1;
169 }
170 }
171 return 0;
subrata_modak8a9ecb72008-05-26 06:09:19 +0000172}
173
plars865695b2001-08-27 22:15:12 +0000174void cleanup()
175{
vapier10fe5d82006-08-21 07:16:54 +0000176 /*
177 * remove the tmp directory and exit
178 */
vapier10fe5d82006-08-21 07:16:54 +0000179 TEST_CLEANUP;
vapier10fe5d82006-08-21 07:16:54 +0000180 tst_rmdir();
vapier10fe5d82006-08-21 07:16:54 +0000181 tst_exit();
yaberauneyada963562009-11-15 08:21:45 +0000182
plars865695b2001-08-27 22:15:12 +0000183}
184
185void setup()
186{
vapier10fe5d82006-08-21 07:16:54 +0000187 /*
188 * setup a default signal hander and a
189 * temporary working directory.
190 */
191 tst_sig(FORK, DEF_HANDLER, cleanup);
vapier10fe5d82006-08-21 07:16:54 +0000192 TEST_PAUSE;
vapier10fe5d82006-08-21 07:16:54 +0000193 tst_tmpdir();
plars865695b2001-08-27 22:15:12 +0000194}
195
196void help()
197{
vapier10fe5d82006-08-21 07:16:54 +0000198 printf(" -b x read byte count\n");
yaberauneyada963562009-11-15 08:21:45 +0000199 printf(" -q read .../irq/... entries\n");
vapier10fe5d82006-08-21 07:16:54 +0000200 printf(" -r x proc pathname\n");
201 printf(" -v verbose mode\n");
plars865695b2001-08-27 22:15:12 +0000202}
203
204/*
205 * add the -m option whose parameter is the
206 * pages that should be mapped.
207 */
vapier10fe5d82006-08-21 07:16:54 +0000208option_t options[] = {
yaberauneyada963562009-11-15 08:21:45 +0000209 { "b:", &opt_buffsize, &opt_buffsizestr},
210 { "q", &opt_readirq, NULL },
211 { "r:", &opt_procpath, &opt_procpathstr},
212 { "v", &opt_verbose, NULL },
213 { NULL, NULL, NULL }
plars865695b2001-08-27 22:15:12 +0000214};
215
plars865695b2001-08-27 22:15:12 +0000216/*
subrata_modak4bb656a2009-02-26 12:02:09 +0000217 * NB: this function is recursive
plars865695b2001-08-27 22:15:12 +0000218 * returns 0 if no error encountered, otherwise number of errors (objs)
219 *
yaberauneyada963562009-11-15 08:21:45 +0000220 * REM: Funny enough, while developing this function (actually replacing
plars865695b2001-08-27 22:15:12 +0000221 * streamed fopen by standard open), I hit a real /proc bug.
222 * On a 2.2.13-SuSE kernel, "cat /proc/tty/driver/serial" would fail
223 * with EFAULT, while "cat /proc/tty/driver/serial > somefile" wouldn't.
224 * Okay, this might be due to a slight serial misconfiguration, but still.
225 * Analysis with strace showed up the difference was on the count size
226 * of read (1024 bytes vs 4096 bytes). So I tested further..
subrata_modak4bb656a2009-02-26 12:02:09 +0000227 * read count of 512 bytes adds /proc/tty/drivers to the list
228 * of broken proc files, while 64 bytes reads removes
yaberauneyada963562009-11-15 08:21:45 +0000229 * /proc/tty/driver/serial from the list. Interesting, isn't it?
plars865695b2001-08-27 22:15:12 +0000230 * Now, there's a -b option to this test, so you can try your luck. --SF
231 *
232 * It's more fun to run this test it as root, as all the files will be accessible!
233 * (however, be careful, there might be some bufferoverflow holes..)
234 * reading proc files might be also a good kernel latency killer.
235 */
yaberauneyada963562009-11-15 08:21:45 +0000236long readproc(const char *obj)
plars865695b2001-08-27 22:15:12 +0000237{
yaberauneyada963562009-11-15 08:21:45 +0000238 DIR *dir = NULL; /* pointer to a directory */
vapier10fe5d82006-08-21 07:16:54 +0000239 struct dirent *dir_ent; /* pointer to directory entries */
240 char dirobj[PATH_MAX]; /* object inside directory to modify */
241 struct stat statbuf; /* used to hold stat information */
subrata_modak8a9ecb72008-05-26 06:09:19 +0000242 int fd, tmperr, i;
vapier10fe5d82006-08-21 07:16:54 +0000243 ssize_t nread;
244 static char buf[MAX_BUFF_SIZE]; /* static kills reentrancy, but we don't care about the contents */
plars865695b2001-08-27 22:15:12 +0000245
vapier10fe5d82006-08-21 07:16:54 +0000246 /* Determine the file type */
247 if (lstat(obj, &statbuf) < 0) {
yaberauneyada963562009-11-15 08:21:45 +0000248
vapier10fe5d82006-08-21 07:16:54 +0000249 /* permission denied is not considered as error */
250 if (errno != EACCES) {
yaberauneyada963562009-11-15 08:21:45 +0000251 tst_resm(TFAIL | TERRNO, "%s: lstat", obj);
vapier10fe5d82006-08-21 07:16:54 +0000252 return 1;
plars865695b2001-08-27 22:15:12 +0000253 }
254 return 0;
yaberauneyada963562009-11-15 08:21:45 +0000255
plars865695b2001-08-27 22:15:12 +0000256 }
257
yaberauneyada963562009-11-15 08:21:45 +0000258 /* Prevent loops, but read /proc/self. */
subrata_modakca317e62007-11-13 09:25:05 +0000259 if (S_ISLNK(statbuf.st_mode) && strcmp(obj, selfpath))
vapier10fe5d82006-08-21 07:16:54 +0000260 return 0;
261
262 total_obj++;
263
264 /* Take appropriate action, depending on the file type */
subrata_modakca317e62007-11-13 09:25:05 +0000265 if (S_ISDIR(statbuf.st_mode) || !strcmp(obj, selfpath)) {
yaberauneyada963562009-11-15 08:21:45 +0000266
vapier10fe5d82006-08-21 07:16:54 +0000267 /* object is a directory */
268
yaberauneyada963562009-11-15 08:21:45 +0000269 /*
270 * Skip over the /proc/irq directory, unless the user
271 * requested that we read the directory because it could
272 * map to a broken driver which effectively `hangs' the
273 * test.
274 */
275 if (!opt_readirq && !strcmp(obj, "irq")) {
276 return 0;
vapier10fe5d82006-08-21 07:16:54 +0000277 /* Open the directory to get access to what is in it */
yaberauneyada963562009-11-15 08:21:45 +0000278 } else if ((dir = opendir(obj)) == NULL) {
vapier10fe5d82006-08-21 07:16:54 +0000279 if (errno != EACCES) {
yaberauneyada963562009-11-15 08:21:45 +0000280 tst_resm(TFAIL | TERRNO, "%s: opendir",
281 obj);
vapier10fe5d82006-08-21 07:16:54 +0000282 return 1;
plars865695b2001-08-27 22:15:12 +0000283 }
plars865695b2001-08-27 22:15:12 +0000284 return 0;
yaberauneyada963562009-11-15 08:21:45 +0000285 } else {
286
287 long ret_val = 0;
288
289 /* Loop through the entries in the directory */
290 for (dir_ent = (struct dirent *)readdir(dir);
291 dir_ent != NULL;
292 dir_ent = (struct dirent *)readdir(dir)) {
293
294 /* Ignore ".", "..", "kcore", and
295 * "/proc/<pid>" (unless this is our
296 * starting point as directed by the
297 * user).
298 */
299 if ( strcmp(dir_ent->d_name, ".") &&
300 strcmp(dir_ent->d_name, "..") &&
301 strcmp(dir_ent->d_name, "kcore") &&
302 (fnmatch("[0-9]*", dir_ent->d_name,
303 FNM_PATHNAME) ||
304 strcmp(obj, procpath))) {
305
306 if (opt_verbose) {
307 fprintf(stderr, "%s\n",
308 dir_ent->d_name);
309 }
310
311 /* Recursively call this routine to test the
312 * current entry */
313 snprintf(dirobj, PATH_MAX,
314 "%s/%s", obj,
315 dir_ent->d_name);
316 ret_val += readproc(dirobj);
317
318 }
319
320 }
321
322 /* Close the directory */
323 if (dir)
324 (void) closedir(dir);
325
326 return ret_val;
327
plars865695b2001-08-27 22:15:12 +0000328 }
vapier10fe5d82006-08-21 07:16:54 +0000329
vapier10fe5d82006-08-21 07:16:54 +0000330 } else { /* if it's not a dir, read it! */
331
332 if (!S_ISREG(statbuf.st_mode))
333 return 0;
334
yaberauneyada963562009-11-15 08:21:45 +0000335#ifdef DEBUG
336 fprintf(stderr, "%s", obj);
337#endif
subrata_modak8a9ecb72008-05-26 06:09:19 +0000338
yaberauneyada963562009-11-15 08:21:45 +0000339 /* is O_NONBLOCK enough to escape from FIFO's ? */
340 if ((fd = open(obj, O_RDONLY | O_NONBLOCK)) < 0) {
341
342 tmperr = errno;
343
344 if (!found_errno("open", obj, tmperr)) {
345
346 errno = tmperr;
347
348 if (errno != EACCES) {
349 tst_resm(TFAIL | TERRNO,
350 "%s: open failed", obj);
351 return 1;
352 }
353
vapier10fe5d82006-08-21 07:16:54 +0000354 }
355 return 0;
yaberauneyada963562009-11-15 08:21:45 +0000356
357 }
358
359 /* Skip write-only files. */
360 if ((statbuf.st_mode & S_IRUSR) == 0 &&
361 (statbuf.st_mode & S_IWUSR) != 0) {
362 tst_resm(TINFO, "%s: is write-only.", obj);
363 return 0;
vapier10fe5d82006-08-21 07:16:54 +0000364 }
365
yaberauneyada963562009-11-15 08:21:45 +0000366 /* Skip files does not honor O_NONBLOCK. */
367 for (i = 0; error_nonblock[i][0] != '\0'; i++) {
368 if (!strcmp(obj, error_nonblock[i])) {
369 tst_resm(TWARN, "%s: does not honor "
370 "O_NONBLOCK", obj);
vapier10fe5d82006-08-21 07:16:54 +0000371 return 0;
372 }
yaberauneyada963562009-11-15 08:21:45 +0000373 }
374
375 do {
376
377 nread = read(fd, buf, buffsize);
378
379 if (nread < 0) {
380
381 tmperr = errno;
382 (void) close(fd);
383
384 /* ignore no perm (not root) and no
385 * process (terminated) errors */
386 if (!found_errno("read", obj,
387 tmperr)) {
388
389 errno = tmperr;
390
391 if (errno != EACCES &&
392 errno != ESRCH) {
393 tst_resm(TFAIL | TERRNO,
394 "read failed: "
395 "%s", obj);
396 return 1;
397 }
398 return 0;
399
400 }
401
402 }
403
vapier10fe5d82006-08-21 07:16:54 +0000404 if (opt_verbose) {
405#ifdef DEBUG
yaberauneyada963562009-11-15 08:21:45 +0000406 fprintf(stderr, "%ld", nread);
vapier10fe5d82006-08-21 07:16:54 +0000407#endif
subrata_modak2f4de1d2008-05-26 06:10:16 +0000408 fprintf(stderr, ".");
vapier10fe5d82006-08-21 07:16:54 +0000409 }
410
411 total_read += nread;
yaberauneyada963562009-11-15 08:21:45 +0000412
413 } while (0 < nread);
414
vapier10fe5d82006-08-21 07:16:54 +0000415 if (opt_verbose)
yaberauneyada963562009-11-15 08:21:45 +0000416 fprintf(stderr, "\n");
417
418 if (0 <= fd)
419 (void) close(fd);
420
plars865695b2001-08-27 22:15:12 +0000421 }
plars865695b2001-08-27 22:15:12 +0000422
yaberauneyada963562009-11-15 08:21:45 +0000423 /* It's better to assume success by default rather than failure. */
plars865695b2001-08-27 22:15:12 +0000424 return 0;
yaberauneyada963562009-11-15 08:21:45 +0000425
plars865695b2001-08-27 22:15:12 +0000426}
427
vapier10fe5d82006-08-21 07:16:54 +0000428int main(int argc, char *argv[])
429{
430 char *msg;
431 int lc;
432
subrata_modakda124b92009-10-13 14:00:45 +0000433 if ((msg = parse_opts(argc, argv, options, help)) != NULL)
vapier10fe5d82006-08-21 07:16:54 +0000434 tst_brkm(TBROK, cleanup, "OPTION PARSING ERROR - %s", msg);
435
436 if (opt_buffsize) {
437 size_t bs;
438 bs = atoi(opt_buffsizestr);
439 if (bs <= MAX_BUFF_SIZE)
440 buffsize = bs;
441 else
442 tst_brkm(TBROK, cleanup,
443 "Invalid arg for -b (max: %u): %s",
444 MAX_BUFF_SIZE, opt_buffsizestr);
445 }
446
yaberauneyada963562009-11-15 08:21:45 +0000447 if (opt_procpath)
vapier10fe5d82006-08-21 07:16:54 +0000448 procpath = opt_procpathstr;
vapier10fe5d82006-08-21 07:16:54 +0000449
450 setup();
451
452 for (lc = 0; TEST_LOOPING(lc); lc++) {
453 Tst_count = 0;
454
455 TEST(readproc(procpath));
456
457 if (TEST_RETURN != 0) {
subrata_modakda124b92009-10-13 14:00:45 +0000458 tst_resm(TFAIL, "readproc() failed with %ld errors.",
vapier10fe5d82006-08-21 07:16:54 +0000459 TEST_RETURN);
460 } else {
461 tst_resm(TPASS, "readproc() completed successfully, "
yaberauneyada963562009-11-15 08:21:45 +0000462 "total read: %llu bytes, %u objs", total_read,
vapier10fe5d82006-08-21 07:16:54 +0000463 total_obj);
464 }
465 }
466
467 cleanup();
468 return 0;
469}