1 /*
2  * ProFTPD - FTP server testsuite
3  * Copyright (c) 2008-2016 The ProFTPD Project team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program 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
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18  *
19  * As a special exemption, The ProFTPD Project team and other respective
20  * copyright holders give permission to link this program with OpenSSL, and
21  * distribute the resulting executable, without including the source code for
22  * OpenSSL in the source distribution.
23  */
24 
25 /* Scoreboard API tests */
26 
27 #include "tests.h"
28 
29 static pool *p = NULL;
30 
31 static const char *test_dir = "/tmp/prt-scoreboard/";
32 static const char *test_file = "/tmp/prt-scoreboard/test.dat";
33 static const char *test_mutex = "/tmp/prt-scoreboard/test.dat.lck";
34 static const char *test_file2 = "/tmp/prt-scoreboard-mutex.dat";
35 
set_up(void)36 static void set_up(void) {
37   (void) unlink(test_file);
38   (void) unlink(test_file2);
39   (void) unlink(test_mutex);
40   (void) rmdir(test_dir);
41 
42   if (p == NULL) {
43     p = permanent_pool = make_sub_pool(NULL);
44   }
45 
46   ServerType = SERVER_STANDALONE;
47   init_netaddr();
48 
49   if (getenv("TEST_VERBOSE") != NULL) {
50     pr_trace_set_levels("lock", 1, 20);
51     pr_trace_set_levels("scoreboard", 1, 20);
52   }
53 }
54 
tear_down(void)55 static void tear_down(void) {
56   (void) unlink(test_file);
57   (void) unlink(test_file2);
58   (void) unlink(test_mutex);
59   (void) rmdir(test_dir);
60 
61   if (getenv("TEST_VERBOSE") != NULL) {
62     pr_trace_set_levels("lock", 0, 0);
63     pr_trace_set_levels("scoreboard", 0, 0);
64   }
65 
66   if (p) {
67     destroy_pool(p);
68     p = permanent_pool = NULL;
69   }
70 }
71 
START_TEST(scoreboard_get_test)72 START_TEST (scoreboard_get_test) {
73   const char *ok, *res;
74 
75   ok = PR_RUN_DIR "/proftpd.scoreboard";
76 
77   res = pr_get_scoreboard();
78   fail_unless(res != NULL, "Failed to get scoreboard path: %s",
79     strerror(errno));
80   fail_unless(strcmp(res, ok) == 0,
81     "Expected scoreboard path '%s', got '%s'", ok, res);
82 
83   ok = PR_RUN_DIR "/proftpd.scoreboard.lck";
84 
85   res = pr_get_scoreboard_mutex();
86   fail_unless(res != NULL, "Failed to get scoreboard mutex path: %s",
87     strerror(errno));
88   fail_unless(strcmp(res, ok) == 0,
89     "Expected scoreboard mutex path '%s', got '%s'", ok, res);
90 }
91 END_TEST
92 
START_TEST(scoreboard_set_test)93 START_TEST (scoreboard_set_test) {
94   int res;
95   const char *path;
96 
97   res = pr_set_scoreboard(NULL);
98   fail_unless(res == -1, "Failed to handle null argument");
99   fail_unless(errno == EINVAL, "Failed to set errno to EINVAL");
100 
101   res = pr_set_scoreboard("foo");
102   fail_unless(res == -1, "Failed to handle non-path argument");
103   fail_unless(errno == EINVAL, "Failed to set errno to EINVAL (got %d)",
104     errno);
105 
106   res = pr_set_scoreboard("foo/");
107   fail_unless(res == -1, "Failed to handle relative path argument");
108   fail_unless(errno == EINVAL, "Failed to set errno to EINVAL (got %d)",
109     errno);
110 
111   res = pr_set_scoreboard("/foo");
112   fail_unless(res == -1, "Failed to handle nonexistent path argument");
113   fail_unless(errno == EINVAL, "Failed to set errno to EINVAL (got %d)",
114     errno);
115 
116   res = pr_set_scoreboard("/tmp");
117   fail_unless(res == -1, "Failed to handle nonexistent path argument");
118   fail_unless(errno == EINVAL, "Failed to set errno to EINVAL (got %d)",
119     errno);
120 
121   res = pr_set_scoreboard("/tmp/");
122   fail_unless(res == -1, "Failed to handle nonexistent path argument");
123   fail_unless(errno == EINVAL, "Failed to set errno to EINVAL (got %d)",
124     errno);
125 
126   res = mkdir(test_dir, 0777);
127   fail_unless(res == 0,
128     "Failed to create tmp directory '%s': %s", test_dir, strerror(errno));
129   res = chmod(test_dir, 0777);
130   fail_unless(res == 0,
131     "Failed to create set 0777 perms on '%s': %s", test_dir, strerror(errno));
132 
133   res = pr_set_scoreboard(test_dir);
134   fail_unless(res == -1, "Failed to handle nonexistent file argument");
135   fail_unless(errno == EINVAL, "Failed to set errno to EINVAL");
136 
137   res = pr_set_scoreboard("/tmp/prt-scoreboard/bar");
138   fail_unless(res == -1, "Failed to handle world-writable path argument");
139   fail_unless(errno == EPERM, "Failed to set errno to EPERM");
140 
141   res = chmod(test_dir, 0775);
142   fail_unless(res == 0, "Failed to set 0775 perms on '%s': %s", test_dir,
143     strerror(errno));
144 
145   res = pr_set_scoreboard("/tmp/prt-scoreboard/bar");
146   fail_unless(res == 0, "Failed to set scoreboard: %s", strerror(errno));
147   (void) rmdir(test_dir);
148 
149   path = pr_get_scoreboard();
150   fail_unless(path != NULL, "Failed to get scoreboard path: %s",
151     strerror(errno));
152   fail_unless(strcmp("/tmp/prt-scoreboard/bar", path) == 0,
153     "Expected '%s', got '%s'", "/tmp/prt-scoreboard/bar", path);
154 
155   (void) rmdir(test_dir);
156 }
157 END_TEST
158 
START_TEST(scoreboard_set_mutex_test)159 START_TEST (scoreboard_set_mutex_test) {
160   int res;
161   const char *path;
162 
163   res = pr_set_scoreboard_mutex(NULL);
164   fail_unless(res == -1, "Failed to handle null argument");
165   fail_unless(errno == EINVAL, "Failed to set errno to EINVAL");
166 
167   res = pr_set_scoreboard_mutex("/tmp");
168   fail_unless(res == 0, "Failed to set scoreboard mutex: %s", strerror(errno));
169 
170   path = pr_get_scoreboard_mutex();
171   fail_unless(path != NULL, "Failed to get scoreboard mutex path: %s",
172     strerror(errno));
173   fail_unless(strcmp("/tmp", path) == 0,
174     "Expected '%s', got '%s'", "/tmp", path);
175 }
176 END_TEST
177 
START_TEST(scoreboard_open_close_test)178 START_TEST (scoreboard_open_close_test) {
179   int res;
180   const char *symlink_path = "/tmp/prt-scoreboard/symlink";
181 
182   res = mkdir(test_dir, 0775);
183   fail_unless(res == 0, "Failed to create directory '%s': %s", test_dir,
184     strerror(errno));
185 
186   res = chmod(test_dir, 0775);
187   fail_unless(res == 0, "Failed to set perms on '%s': %s", test_dir,
188     strerror(errno));
189 
190   res = pr_set_scoreboard(test_file);
191   fail_unless(res == 0, "Failed to set scoreboard to '%s': %s", test_file,
192     strerror(errno));
193 
194   if (symlink(symlink_path, test_file) == 0) {
195 
196     res = pr_open_scoreboard(O_RDWR);
197     if (res == 0) {
198       (void) unlink(symlink_path);
199 
200       fail("Unexpectedly opened symlink scoreboard");
201     }
202 
203     if (errno != EPERM) {
204       int xerrno = errno;
205 
206       (void) unlink(symlink_path);
207 
208       fail("Failed to set errno to EPERM (got %d)", xerrno);
209     }
210 
211     (void) unlink(symlink_path);
212 
213     res = pr_set_scoreboard(test_file);
214     fail_unless(res == 0, "Failed to set scoreboard to '%s': %s", test_file,
215       strerror(errno));
216   }
217 
218   res = pr_open_scoreboard(O_RDONLY);
219   fail_unless(res < 0, "Unexpectedly opened scoreboard using O_RDONLY");
220 
221   if (errno != EINVAL) {
222     int xerrno = errno;
223 
224     (void) unlink(symlink_path);
225 
226     fail("Failed to set errno to EINVAL (got %d)", xerrno);
227   }
228 
229   (void) unlink(test_mutex);
230   (void) unlink(test_file);
231   res = pr_open_scoreboard(O_RDWR);
232   fail_unless(res == 0, "Failed to open scoreboard: %s", strerror(errno));
233 
234   /* Try opening the scoreboard again; it should be OK, since we are the
235    * opener.
236    */
237   res = pr_open_scoreboard(O_RDWR);
238   fail_unless(res == 0, "Failed to open scoreboard again: %s", strerror(errno));
239 
240   /* Now that we have a scoreboard, try opening it again using O_RDONLY. */
241   pr_close_scoreboard(FALSE);
242 
243   res = pr_open_scoreboard(O_RDONLY);
244   fail_unless(res < 0, "Unexpectedly opened scoreboard using O_RDONLY");
245   fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
246     strerror(errno), errno);
247 
248   (void) unlink(test_mutex);
249   (void) unlink(test_file);
250   (void) rmdir(test_dir);
251 }
252 END_TEST
253 
START_TEST(scoreboard_lock_test)254 START_TEST (scoreboard_lock_test) {
255   int fd = -1, lock_type = -1, res;
256 
257   res = pr_lock_scoreboard(fd, lock_type);
258   fail_unless(res < 0, "Failed to handle bad lock type");
259   fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
260     strerror(errno), errno);
261 
262   lock_type = F_RDLCK;
263   res = pr_lock_scoreboard(fd, lock_type);
264   fail_unless(res < 0, "Failed to handle bad file descriptor");
265   fail_unless(errno == EBADF, "Expected EBADF (%d), got %s (%d)", EBADF,
266     strerror(errno), errno);
267 
268   fd = open(test_file2, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);
269   fail_unless(fd >= 0, "Failed to open '%s': %s", test_file2, strerror(errno));
270 
271   res = pr_lock_scoreboard(fd, lock_type);
272   fail_unless(res == 0, "Failed to lock fd %d: %s", fd, strerror(errno));
273 
274   lock_type = F_WRLCK;
275   res = pr_lock_scoreboard(fd, lock_type);
276   fail_unless(res == 0, "Failed to lock fd %d: %s", fd, strerror(errno));
277 
278   lock_type = F_UNLCK;
279   res = pr_lock_scoreboard(fd, lock_type);
280   fail_unless(res == 0, "Failed to lock fd %d: %s", fd, strerror(errno));
281 
282   lock_type = F_WRLCK;
283   res = pr_lock_scoreboard(fd, lock_type);
284   fail_unless(res == 0, "Failed to lock fd %d: %s", fd, strerror(errno));
285 
286   /* Note: apparently attempt to lock (again) a file on which a lock
287    * (of the same type) is already held will succeed.  Huh.
288    */
289   res = pr_lock_scoreboard(fd, lock_type);
290   fail_unless(res == 0, "Failed to lock fd %d: %s", fd, strerror(errno));
291 
292   lock_type = F_RDLCK;
293   res = pr_lock_scoreboard(fd, lock_type);
294   fail_unless(res == 0, "Failed to lock fd %d: %s", fd, strerror(errno));
295 
296   lock_type = F_UNLCK;
297   res = pr_lock_scoreboard(fd, lock_type);
298   fail_unless(res == 0, "Failed to lock fd %d: %s", fd, strerror(errno));
299 
300   (void) unlink(test_file2);
301 }
302 END_TEST
303 
START_TEST(scoreboard_delete_test)304 START_TEST (scoreboard_delete_test) {
305   int res;
306   struct stat st;
307 
308   res = mkdir(test_dir, 0775);
309   fail_unless(res == 0, "Failed to create directory '%s': %s", test_dir,
310     strerror(errno));
311 
312   res = chmod(test_dir, 0775);
313   fail_unless(res == 0, "Failed to set perms on '%s' to 0775': %s", test_dir,
314     strerror(errno));
315 
316   res = pr_set_scoreboard(test_file);
317   fail_unless(res == 0, "Failed to set scoreboard to '%s': %s", test_file,
318     strerror(errno));
319 
320   res = pr_open_scoreboard(O_RDWR);
321   fail_unless(res == 0, "Failed to open scoreboard: %s", strerror(errno));
322 
323   res = stat(pr_get_scoreboard(), &st);
324   fail_unless(res == 0, "Failed to stat scoreboard: %s", strerror(errno));
325 
326   pr_delete_scoreboard();
327 
328   res = stat(pr_get_scoreboard(), &st);
329   fail_unless(res < 0, "Unexpectedly found deleted scoreboard");
330 
331   res = stat(pr_get_scoreboard_mutex(), &st);
332   fail_unless(res < 0, "Unexpectedly found deleted scoreboard mutex");
333 
334   (void) unlink(test_mutex);
335   (void) unlink(test_file);
336   (void) rmdir(test_dir);
337 }
338 END_TEST
339 
START_TEST(scoreboard_restore_test)340 START_TEST (scoreboard_restore_test) {
341   int res;
342 
343   res = mkdir(test_dir, 0775);
344   fail_unless(res == 0, "Failed to create directory '%s': %s", test_dir,
345     strerror(errno));
346 
347   res = chmod(test_dir, 0775);
348   fail_unless(res == 0, "Failed to set perms on '%s' to 0775': %s", test_dir,
349     strerror(errno));
350 
351   res = pr_set_scoreboard(test_file);
352   fail_unless(res == 0, "Failed to set scoreboard to '%s': %s", test_file,
353     strerror(errno));
354 
355   res = pr_restore_scoreboard();
356   fail_unless(res < 0, "Unexpectedly restored scoreboard");
357   fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
358     strerror(errno), errno);
359 
360   res = pr_open_scoreboard(O_RDWR);
361   fail_unless(res == 0, "Failed to open scoreboard: %s", strerror(errno));
362 
363   res = pr_restore_scoreboard();
364   fail_unless(res < 0,
365     "Restoring scoreboard before rewind succeeded unexpectedly");
366 
367   res = pr_rewind_scoreboard();
368   fail_unless(res == 0, "Failed to rewind scoreboard: %s", strerror(errno));
369 
370   res = pr_restore_scoreboard();
371   fail_unless(res == 0, "Failed to restore scoreboard: %s", strerror(errno));
372 
373   (void) unlink(test_mutex);
374   (void) unlink(test_file);
375   (void) rmdir(test_dir);
376 }
377 END_TEST
378 
START_TEST(scoreboard_rewind_test)379 START_TEST (scoreboard_rewind_test) {
380   int res;
381 
382   res = mkdir(test_dir, 0775);
383   fail_unless(res == 0, "Failed to create directory '%s': %s", test_dir,
384     strerror(errno));
385 
386   res = chmod(test_dir, 0775);
387   fail_unless(res == 0, "Failed to set perms on '%s' to 0775': %s", test_dir,
388     strerror(errno));
389 
390   res = pr_set_scoreboard(test_file);
391   fail_unless(res == 0, "Failed to set scoreboard to '%s': %s", test_file,
392     strerror(errno));
393 
394   res = pr_rewind_scoreboard();
395   fail_unless(res < 0, "Unexpectedly rewound scoreboard");
396   fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
397     strerror(errno), errno);
398 
399   res = pr_open_scoreboard(O_RDWR);
400   fail_unless(res == 0, "Failed to open scoreboard: %s", strerror(errno));
401 
402   res = pr_rewind_scoreboard();
403   fail_unless(res == 0, "Failed to rewind scoreboard: %s", strerror(errno));
404 
405   (void) unlink(test_mutex);
406   (void) unlink(test_file);
407   (void) rmdir(test_dir);
408 }
409 END_TEST
410 
START_TEST(scoreboard_scrub_test)411 START_TEST (scoreboard_scrub_test) {
412   uid_t euid;
413   int res;
414 
415   res = mkdir(test_dir, 0775);
416   fail_unless(res == 0, "Failed to create directory '%s': %s", test_dir,
417     strerror(errno));
418 
419   res = chmod(test_dir, 0775);
420   fail_unless(res == 0, "Failed to set perms on '%s' to 0775': %s", test_dir,
421     strerror(errno));
422 
423   res = pr_set_scoreboard(test_file);
424   fail_unless(res == 0, "Failed to set scoreboard to '%s': %s", test_file,
425     strerror(errno));
426 
427   res = pr_scoreboard_scrub();
428   fail_unless(res < 0, "Unexpectedly scrubbed scoreboard");
429 
430   euid = geteuid();
431   if (euid != 0) {
432     if (errno != EPERM &&
433         errno != ENOENT) {
434       fail("Failed to set errno to EPERM/ENOENT, got %d [%s] (euid = %lu)",
435         errno, strerror(errno), (unsigned long) euid);
436     }
437 
438   } else {
439     if (errno != ENOENT) {
440       fail("Failed to set errno to ENOENT, got %d [%s] (euid = %lu)", errno,
441         strerror(errno), (unsigned long) euid);
442     }
443   }
444 
445   res = pr_open_scoreboard(O_RDWR);
446   fail_unless(res == 0, "Failed to open scoreboard: %s", strerror(errno));
447 
448   res = pr_scoreboard_scrub();
449   fail_unless(res == 0, "Failed to scrub scoreboard: %s", strerror(errno));
450 
451   (void) unlink(test_mutex);
452   (void) unlink(test_file);
453   (void) rmdir(test_dir);
454 }
455 END_TEST
456 
START_TEST(scoreboard_get_daemon_pid_test)457 START_TEST (scoreboard_get_daemon_pid_test) {
458   int res;
459   pid_t daemon_pid;
460 
461   res = mkdir(test_dir, 0775);
462   fail_unless(res == 0, "Failed to create directory '%s': %s", test_dir,
463     strerror(errno));
464 
465   res = chmod(test_dir, 0775);
466   fail_unless(res == 0, "Failed to set perms on '%s' to 0775': %s", test_dir,
467     strerror(errno));
468 
469   res = pr_set_scoreboard(test_file);
470   fail_unless(res == 0, "Failed to set scoreboard to '%s': %s", test_file,
471     strerror(errno));
472 
473   res = pr_open_scoreboard(O_RDWR);
474   fail_unless(res == 0, "Failed to open scoreboard: %s", strerror(errno));
475 
476   daemon_pid = pr_scoreboard_get_daemon_pid();
477   if (daemon_pid != getpid()) {
478     fail("Expected %lu, got %lu", (unsigned long) getpid(),
479       (unsigned long) daemon_pid);
480   }
481 
482   pr_delete_scoreboard();
483 
484   ServerType = SERVER_INETD;
485 
486   res = pr_open_scoreboard(O_RDWR);
487   fail_unless(res == 0, "Failed to open scoreboard: %s", strerror(errno));
488 
489   daemon_pid = pr_scoreboard_get_daemon_pid();
490   if (daemon_pid != 0) {
491     fail("Expected %lu, got %lu", (unsigned long) 0,
492       (unsigned long) daemon_pid);
493   }
494 
495   (void) unlink(test_mutex);
496   (void) unlink(test_file);
497   (void) rmdir(test_dir);
498 }
499 END_TEST
500 
START_TEST(scoreboard_get_daemon_uptime_test)501 START_TEST (scoreboard_get_daemon_uptime_test) {
502   int res;
503   time_t daemon_uptime, now;
504 
505   res = mkdir(test_dir, 0775);
506   fail_unless(res == 0, "Failed to create directory '%s': %s", test_dir,
507     strerror(errno));
508 
509   res = chmod(test_dir, 0775);
510   fail_unless(res == 0, "Failed to set perms on '%s' to 0775': %s", test_dir,
511     strerror(errno));
512 
513   res = pr_set_scoreboard(test_file);
514   fail_unless(res == 0, "Failed to set scoreboard to '%s': %s", test_file,
515     strerror(errno));
516 
517   res = pr_open_scoreboard(O_RDWR);
518   fail_unless(res == 0, "Failed to open scoreboard: %s", strerror(errno));
519 
520   daemon_uptime = pr_scoreboard_get_daemon_uptime();
521   now = time(NULL);
522 
523   if (daemon_uptime > now) {
524     fail("Expected %lu, got %lu", (unsigned long) now,
525       (unsigned long) daemon_uptime);
526   }
527 
528   pr_delete_scoreboard();
529 
530   ServerType = SERVER_INETD;
531 
532   res = pr_open_scoreboard(O_RDWR);
533   fail_unless(res == 0, "Failed to open scoreboard: %s", strerror(errno));
534 
535   daemon_uptime = pr_scoreboard_get_daemon_uptime();
536   if (daemon_uptime != 0) {
537     fail("Expected %lu, got %lu", (unsigned long) 0,
538       (unsigned long) daemon_uptime);
539   }
540 
541   (void) unlink(test_mutex);
542   (void) unlink(test_file);
543   (void) rmdir(test_dir);
544 }
545 END_TEST
546 
START_TEST(scoreboard_entry_add_test)547 START_TEST (scoreboard_entry_add_test) {
548   int res;
549 
550   res = mkdir(test_dir, 0775);
551   fail_unless(res == 0, "Failed to create directory '%s': %s", test_dir,
552     strerror(errno));
553 
554   res = chmod(test_dir, 0775);
555   fail_unless(res == 0, "Failed to set perms on '%s' to 0775': %s", test_dir,
556     strerror(errno));
557 
558   res = pr_set_scoreboard(test_file);
559   fail_unless(res == 0, "Failed to set scoreboard to '%s': %s", test_file,
560     strerror(errno));
561 
562   res = pr_scoreboard_entry_add();
563   fail_unless(res < 0, "Unexpectedly added entry to scoreboard");
564   fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
565     strerror(errno), errno);
566 
567   res = pr_open_scoreboard(O_RDWR);
568   fail_unless(res == 0, "Failed to open scoreboard: %s", strerror(errno));
569 
570   res = pr_scoreboard_entry_add();
571   fail_unless(res == 0, "Failed to add entry to scoreboard: %s",
572     strerror(errno));
573 
574   res = pr_scoreboard_entry_add();
575   fail_unless(res < 0, "Unexpectedly added entry to scoreboard");
576   fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM,
577     strerror(errno), errno);
578 
579   (void) unlink(test_mutex);
580   (void) unlink(test_file);
581   (void) rmdir(test_dir);
582 }
583 END_TEST
584 
START_TEST(scoreboard_entry_del_test)585 START_TEST (scoreboard_entry_del_test) {
586   int res;
587 
588   res = mkdir(test_dir, 0775);
589   fail_unless(res == 0, "Failed to create directory '%s': %s", test_dir,
590     strerror(errno));
591 
592   res = chmod(test_dir, 0775);
593   fail_unless(res == 0, "Failed to set perms on '%s' to 0775': %s", test_dir,
594     strerror(errno));
595 
596   res = pr_set_scoreboard(test_file);
597   fail_unless(res == 0, "Failed to set scoreboard to '%s': %s", test_file,
598     strerror(errno));
599 
600   res = pr_scoreboard_entry_del(FALSE);
601   fail_unless(res < 0, "Unexpectedly deleted entry from scoreboard");
602   fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
603     strerror(errno), errno);
604 
605   res = pr_open_scoreboard(O_RDWR);
606   fail_unless(res == 0, "Failed to open scoreboard: %s", strerror(errno));
607 
608   res = pr_scoreboard_entry_del(FALSE);
609   fail_unless(res < 0, "Unexpectedly deleted entry from scoreboard");
610   fail_unless(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT,
611     strerror(errno), errno);
612 
613   res = pr_scoreboard_entry_add();
614   fail_unless(res == 0, "Failed to add entry to scoreboard: %s",
615     strerror(errno));
616 
617   res = pr_scoreboard_entry_del(FALSE);
618   fail_unless(res == 0, "Failed to delete entry from scoreboard: %s",
619     strerror(errno));
620 
621   res = pr_scoreboard_entry_del(FALSE);
622   fail_unless(res < 0, "Unexpectedly deleted entry from scoreboard");
623   fail_unless(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT,
624     strerror(errno), errno);
625 
626   (void) unlink(test_mutex);
627   (void) unlink(test_file);
628   (void) rmdir(test_dir);
629 }
630 END_TEST
631 
START_TEST(scoreboard_entry_read_test)632 START_TEST (scoreboard_entry_read_test) {
633   int res;
634   pr_scoreboard_entry_t *score;
635 
636   res = mkdir(test_dir, 0775);
637   fail_unless(res == 0, "Failed to create directory '%s': %s", test_dir,
638     strerror(errno));
639 
640   res = chmod(test_dir, 0775);
641   fail_unless(res == 0, "Failed to set perms on '%s' to 0775': %s", test_dir,
642     strerror(errno));
643 
644   res = pr_set_scoreboard(test_file);
645   fail_unless(res == 0, "Failed to set scoreboard to '%s': %s", test_file,
646     strerror(errno));
647 
648   score = pr_scoreboard_entry_read();
649   fail_unless(score == NULL, "Unexpectedly read scoreboard entry");
650   fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
651     strerror(errno), errno);
652 
653   res = pr_open_scoreboard(O_RDWR);
654   fail_unless(res == 0, "Failed to open scoreboard: %s", strerror(errno));
655 
656   /* We expect NULL here because the scoreboard file should be empty. */
657   score = pr_scoreboard_entry_read();
658   fail_unless(score == NULL, "Unexpectedly read scoreboard entry");
659 
660   res = pr_scoreboard_entry_add();
661   fail_unless(res == 0, "Failed to add entry to scoreboard: %s",
662     strerror(errno));
663 
664   score = pr_scoreboard_entry_read();
665   fail_unless(score != NULL, "Failed to read scoreboard entry: %s",
666     strerror(errno));
667 
668   if (score->sce_pid != getpid()) {
669     fail("Failed to read expected scoreboard entry (expected PID %lu, got %lu)",
670       (unsigned long) getpid(), (unsigned long) score->sce_pid);
671   }
672 
673   score = pr_scoreboard_entry_read();
674   fail_unless(score == NULL, "Unexpectedly read scoreboard entry");
675 
676   (void) unlink(test_mutex);
677   (void) unlink(test_file);
678   (void) rmdir(test_dir);
679 }
680 END_TEST
681 
START_TEST(scoreboard_entry_get_test)682 START_TEST (scoreboard_entry_get_test) {
683   register unsigned int i;
684   int res;
685   const char *val;
686   int scoreboard_fields[] = {
687     PR_SCORE_USER,
688     PR_SCORE_CLIENT_ADDR,
689     PR_SCORE_CLIENT_NAME,
690     PR_SCORE_CLASS,
691     PR_SCORE_CWD,
692     PR_SCORE_CMD,
693     PR_SCORE_CMD_ARG,
694     PR_SCORE_PROTOCOL,
695     -1
696   };
697 
698   res = mkdir(test_dir, 0775);
699   fail_unless(res == 0, "Failed to create directory '%s': %s", test_dir,
700     strerror(errno));
701 
702   res = chmod(test_dir, 0775);
703   fail_unless(res == 0, "Failed to set perms on '%s' to 0775': %s", test_dir,
704     strerror(errno));
705 
706   res = pr_set_scoreboard(test_file);
707   fail_unless(res == 0, "Failed to set scoreboard to '%s': %s", test_file,
708     strerror(errno));
709 
710   val = pr_scoreboard_entry_get(-1);
711   fail_unless(val == NULL, "Unexpectedly read value from scoreboard entry");
712   fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
713     strerror(errno), errno);
714 
715   res = pr_open_scoreboard(O_RDWR);
716   fail_unless(res == 0, "Failed to open scoreboard: %s", strerror(errno));
717 
718   val = pr_scoreboard_entry_get(-1);
719   fail_unless(val == NULL, "Unexpectedly read value from scoreboard entry");
720   fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM,
721     strerror(errno), errno);
722 
723   res = pr_scoreboard_entry_add();
724   fail_unless(res == 0, "Failed to add entry to scoreboard: %s",
725     strerror(errno));
726 
727   val = pr_scoreboard_entry_get(-1);
728   fail_unless(val == NULL, "Unexpectedly read value from scoreboard entry");
729   fail_unless(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT,
730     strerror(errno), errno);
731 
732   for (i = 0; scoreboard_fields[i] != -1; i++) {
733     val = pr_scoreboard_entry_get(scoreboard_fields[i]);
734     fail_unless(val != NULL, "Failed to read scoreboard field %d: %s",
735       scoreboard_fields[i], strerror(errno));
736   }
737 
738   (void) unlink(test_mutex);
739   (void) unlink(test_file);
740   (void) rmdir(test_dir);
741 }
742 END_TEST
743 
START_TEST(scoreboard_entry_update_test)744 START_TEST (scoreboard_entry_update_test) {
745   int num, res;
746   const char *val;
747   pid_t pid = getpid();
748   const pr_netaddr_t *addr;
749   time_t now;
750   off_t len;
751   unsigned long elapsed;
752 
753   res = mkdir(test_dir, 0775);
754   fail_unless(res == 0, "Failed to create directory '%s': %s", test_dir,
755     strerror(errno));
756 
757   res = chmod(test_dir, 0775);
758   fail_unless(res == 0, "Failed to set perms on '%s' to 0775': %s", test_dir,
759     strerror(errno));
760 
761   res = pr_set_scoreboard(test_file);
762   fail_unless(res == 0, "Failed to set scoreboard to '%s': %s", test_file,
763     strerror(errno));
764 
765   res = pr_scoreboard_entry_update(pid, 0);
766   fail_unless(res < 0, "Unexpectedly updated scoreboard entry");
767   fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
768     strerror(errno), errno);
769 
770   res = pr_open_scoreboard(O_RDWR);
771   fail_unless(res == 0, "Failed to open scoreboard: %s", strerror(errno));
772 
773   res = pr_scoreboard_entry_update(pid, 0);
774   fail_unless(res < 0, "Unexpectedly updated scoreboard entry");
775   fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM,
776     strerror(errno), errno);
777 
778   res = pr_scoreboard_entry_add();
779   fail_unless(res == 0, "Failed to add entry to scoreboard: %s",
780     strerror(errno));
781 
782   res = pr_scoreboard_entry_update(pid, -1);
783   fail_unless(res < 0, "Unexpectedly updated scoreboard entry");
784   fail_unless(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT,
785     strerror(errno), errno);
786 
787   val = "cwd";
788   res = pr_scoreboard_entry_update(pid, PR_SCORE_CWD, val, NULL);
789   fail_unless(res == 0, "Failed to update PR_SCORE_CWD: %s", strerror(errno));
790 
791   val = pr_scoreboard_entry_get(PR_SCORE_CWD);
792   fail_unless(val != NULL, "Failed to get entry PR_SCORE_CWD: %s",
793     strerror(errno));
794   fail_unless(strcmp(val, "cwd") == 0, "Expected 'cwd', got '%s'", val);
795 
796   val = "user";
797   res = pr_scoreboard_entry_update(pid, PR_SCORE_USER, val, NULL);
798   fail_unless(res == 0, "Failed to update PR_SCORE_USER: %s", strerror(errno));
799 
800   addr = pr_netaddr_get_addr(p, "127.0.0.1", NULL);
801   fail_unless(addr != NULL, "Failed to resolve '127.0.0.1': %s",
802     strerror(errno));
803 
804   res = pr_scoreboard_entry_update(pid, PR_SCORE_CLIENT_ADDR, addr, NULL);
805   fail_unless(res == 0, "Failed to update PR_SCORE_CLIENT_ADDR: %s",
806     strerror(errno));
807 
808   val = "remote_name";
809   res = pr_scoreboard_entry_update(pid, PR_SCORE_CLIENT_NAME, val, NULL);
810   fail_unless(res == 0, "Failed to update PR_SCORE_CLIENT_NAME: %s",
811     strerror(errno));
812 
813   val = "session_class";
814   res = pr_scoreboard_entry_update(pid, PR_SCORE_CLASS, val, NULL);
815   fail_unless(res == 0, "Failed to update PR_SCORE_CLASS: %s", strerror(errno));
816 
817   val = "USER";
818   res = pr_scoreboard_entry_update(pid, PR_SCORE_CMD, "%s", val, NULL, NULL);
819   fail_unless(res == 0, "Failed to update PR_SCORE_CMD: %s", strerror(errno));
820 
821   val = "foo bar";
822   res = pr_scoreboard_entry_update(pid, PR_SCORE_CMD_ARG, "%s", val, NULL,
823     NULL);
824   fail_unless(res == 0, "Failed to update PR_SCORE_CMD_ARG: %s",
825     strerror(errno));
826 
827   num = 77;
828   res = pr_scoreboard_entry_update(pid, PR_SCORE_SERVER_PORT, num, NULL);
829   fail_unless(res == 0, "Failed to update PR_SCORE_SERVER_PORT: %s",
830     strerror(errno));
831 
832   res = pr_scoreboard_entry_update(pid, PR_SCORE_SERVER_ADDR, addr, num, NULL);
833   fail_unless(res == 0, "Failed to update PR_SCORE_SERVER_ADDR: %s",
834     strerror(errno));
835 
836   val = "label";
837   res = pr_scoreboard_entry_update(pid, PR_SCORE_SERVER_LABEL, val, NULL);
838   fail_unless(res == 0, "Failed to update PR_SCORE_SERVER_LABEL: %s",
839     strerror(errno));
840 
841   now = 1;
842   res = pr_scoreboard_entry_update(pid, PR_SCORE_BEGIN_IDLE, now, NULL);
843   fail_unless(res == 0, "Failed to update PR_SCORE_BEGIN_IDLE: %s",
844     strerror(errno));
845 
846   now = 2;
847   res = pr_scoreboard_entry_update(pid, PR_SCORE_BEGIN_SESSION, now, NULL);
848   fail_unless(res == 0, "Failed to update PR_SCORE_BEGIN_SESSION: %s",
849     strerror(errno));
850 
851   len = 7;
852   res = pr_scoreboard_entry_update(pid, PR_SCORE_XFER_DONE, len, NULL);
853   fail_unless(res == 0, "Failed to update PR_SCORE_XFER_DONE: %s",
854     strerror(errno));
855 
856   len = 8;
857   res = pr_scoreboard_entry_update(pid, PR_SCORE_XFER_SIZE, len, NULL);
858   fail_unless(res == 0, "Failed to update PR_SCORE_XFER_SIZE: %s",
859     strerror(errno));
860 
861   len = 9;
862   res = pr_scoreboard_entry_update(pid, PR_SCORE_XFER_LEN, len, NULL);
863   fail_unless(res == 0, "Failed to update PR_SCORE_XFER_LEN: %s",
864     strerror(errno));
865 
866   elapsed = 1;
867   res = pr_scoreboard_entry_update(pid, PR_SCORE_XFER_ELAPSED, elapsed, NULL);
868   fail_unless(res == 0, "Failed to update PR_SCORE_XFER_ELAPSED: %s",
869     strerror(errno));
870 
871   val = "protocol";
872   res = pr_scoreboard_entry_update(pid, PR_SCORE_PROTOCOL, val, NULL);
873   fail_unless(res == 0, "Failed to update PR_SCORE_PROTOCOL: %s",
874     strerror(errno));
875 
876   (void) unlink(test_mutex);
877   (void) unlink(test_file);
878   (void) rmdir(test_dir);
879 }
880 END_TEST
881 
START_TEST(scoreboard_entry_kill_test)882 START_TEST (scoreboard_entry_kill_test) {
883   int res;
884   pr_scoreboard_entry_t sce;
885 
886   res = pr_scoreboard_entry_kill(NULL, 0);
887   fail_unless(res < 0, "Failed to handle null arguments");
888   fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
889     strerror(errno), errno);
890 
891   sce.sce_pid = getpid();
892   res = pr_scoreboard_entry_kill(&sce, 0);
893   fail_unless(res == 0, "Failed to send signal 0 to PID %lu: %s",
894     (unsigned long) sce.sce_pid, strerror(errno));
895 }
896 END_TEST
897 
START_TEST(scoreboard_entry_lock_test)898 START_TEST (scoreboard_entry_lock_test) {
899   int fd = -1, lock_type = -1, res;
900 
901   res = pr_scoreboard_entry_lock(fd, lock_type);
902   fail_unless(res < 0, "Failed to handle bad lock type");
903   fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
904     strerror(errno), errno);
905 
906   lock_type = F_RDLCK;
907   res = pr_scoreboard_entry_lock(fd, lock_type);
908   fail_unless(res < 0, "Failed to handle bad file descriptor");
909   fail_unless(errno == EBADF, "Expected EBADF (%d), got %s (%d)", EBADF,
910     strerror(errno), errno);
911 
912   fd = open(test_file2, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);
913   fail_unless(fd >= 0, "Failed to open '%s': %s", test_file2, strerror(errno));
914 
915   res = pr_scoreboard_entry_lock(fd, lock_type);
916   fail_unless(res == 0, "Failed to lock fd %d: %s", fd, strerror(errno));
917 
918   lock_type = F_WRLCK;
919   res = pr_scoreboard_entry_lock(fd, lock_type);
920   fail_unless(res == 0, "Failed to lock fd %d: %s", fd, strerror(errno));
921 
922   lock_type = F_UNLCK;
923   res = pr_scoreboard_entry_lock(fd, lock_type);
924   fail_unless(res == 0, "Failed to lock fd %d: %s", fd, strerror(errno));
925 
926   lock_type = F_WRLCK;
927   res = pr_scoreboard_entry_lock(fd, lock_type);
928   fail_unless(res == 0, "Failed to lock fd %d: %s", fd, strerror(errno));
929 
930   /* Note: apparently attempt to lock (again) a file on which a lock
931    * (of the same type) is already held will succeed.  Huh.
932    */
933   res = pr_scoreboard_entry_lock(fd, lock_type);
934   fail_unless(res == 0, "Failed to lock fd %d: %s", fd, strerror(errno));
935 
936   lock_type = F_RDLCK;
937   res = pr_scoreboard_entry_lock(fd, lock_type);
938   fail_unless(res == 0, "Failed to lock fd %d: %s", fd, strerror(errno));
939 
940   lock_type = F_UNLCK;
941   res = pr_scoreboard_entry_lock(fd, lock_type);
942   fail_unless(res == 0, "Failed to lock fd %d: %s", fd, strerror(errno));
943 
944   (void) unlink(test_file2);
945 }
946 END_TEST
947 
START_TEST(scoreboard_disabled_test)948 START_TEST (scoreboard_disabled_test) {
949   register unsigned int i = 0;
950   const char *paths[4] = {
951     "/dev/null",
952     "none",
953     "off",
954     NULL
955   };
956   const char *path;
957 
958   for (path = paths[i]; path != NULL; path = paths[i++]) {
959     int res;
960     const char *field, *ok;
961     pid_t scoreboard_pid;
962     time_t scoreboard_uptime;
963     pr_scoreboard_entry_t *score;
964 
965     res = pr_set_scoreboard(path);
966     fail_unless(res == 0, "Failed set to scoreboard to '%s': %s", path,
967       strerror(errno));
968 
969     ok = PR_RUN_DIR "/proftpd.scoreboard";
970 
971     path = pr_get_scoreboard();
972     fail_unless(path != NULL, "Failed to get scoreboard path: %s",
973       strerror(errno));
974     fail_unless(strcmp(path, ok) == 0,
975       "Expected path '%s', got '%s'", ok, path);
976 
977     res = pr_open_scoreboard(O_RDONLY);
978     fail_unless(res == 0, "Failed to open '%s' scoreboard: %s", path,
979       strerror(errno));
980 
981     res = pr_scoreboard_scrub();
982     fail_unless(res == 0, "Failed to scrub '%s' scoreboard: %s", path,
983       strerror(errno));
984 
985     scoreboard_pid = pr_scoreboard_get_daemon_pid();
986     fail_unless(scoreboard_pid == 0,
987       "Expected to get scoreboard PID 0, got %lu",
988       (unsigned long) scoreboard_pid);
989 
990     scoreboard_uptime = pr_scoreboard_get_daemon_uptime();
991     fail_unless(scoreboard_uptime == 0,
992       "Expected to get scoreboard uptime 0, got %lu",
993       (unsigned long) scoreboard_uptime);
994 
995     res = pr_scoreboard_entry_add();
996     fail_unless(res == 0, "Failed to add entry to '%s' scoreboard: %s", path,
997       strerror(errno));
998 
999     score = pr_scoreboard_entry_read();
1000     fail_unless(score == NULL, "Expected null entry");
1001 
1002     field = pr_scoreboard_entry_get(PR_SCORE_CMD_ARG);
1003     fail_unless(field == NULL, "Expected null CMD_ARG field");
1004 
1005     res = pr_scoreboard_entry_update(getpid(), PR_SCORE_CWD, "foo", NULL);
1006     fail_unless(res == 0, "Failed to update CWD field: %s", strerror(errno));
1007 
1008     res = pr_scoreboard_entry_del(FALSE);
1009     fail_unless(res == 0, "Failed to delete entry from '%s' scoreboard: %s",
1010       path, strerror(errno));
1011 
1012     res = pr_close_scoreboard(FALSE);
1013     fail_unless(res == 0, "Failed to close '%s' scoreboard: %s", path,
1014       strerror(errno));
1015 
1016     /* Internal hack: even calling pr_set_scoreboard() with a NULL
1017      * argument will set the Scoreboard API internal flag back to true.
1018      */
1019     pr_set_scoreboard(NULL);
1020   }
1021 }
1022 END_TEST
1023 
tests_get_scoreboard_suite(void)1024 Suite *tests_get_scoreboard_suite(void) {
1025   Suite *suite;
1026   TCase *testcase;
1027 
1028   suite = suite_create("scoreboard");
1029 
1030   testcase = tcase_create("base");
1031   tcase_add_checked_fixture(testcase, set_up, tear_down);
1032 
1033   tcase_add_test(testcase, scoreboard_get_test);
1034   tcase_add_test(testcase, scoreboard_set_test);
1035   tcase_add_test(testcase, scoreboard_set_mutex_test);
1036   tcase_add_test(testcase, scoreboard_open_close_test);
1037   tcase_add_test(testcase, scoreboard_lock_test);
1038   tcase_add_test(testcase, scoreboard_delete_test);
1039   tcase_add_test(testcase, scoreboard_restore_test);
1040   tcase_add_test(testcase, scoreboard_rewind_test);
1041   tcase_add_test(testcase, scoreboard_scrub_test);
1042   tcase_add_test(testcase, scoreboard_get_daemon_pid_test);
1043   tcase_add_test(testcase, scoreboard_get_daemon_uptime_test);
1044   tcase_add_test(testcase, scoreboard_entry_add_test);
1045   tcase_add_test(testcase, scoreboard_entry_del_test);
1046   tcase_add_test(testcase, scoreboard_entry_read_test);
1047   tcase_add_test(testcase, scoreboard_entry_get_test);
1048   tcase_add_test(testcase, scoreboard_entry_update_test);
1049   tcase_add_test(testcase, scoreboard_entry_kill_test);
1050   tcase_add_test(testcase, scoreboard_entry_lock_test);
1051   tcase_add_test(testcase, scoreboard_disabled_test);
1052 
1053   suite_add_tcase(suite, testcase);
1054   return suite;
1055 }
1056