├── .gitignore ├── LICENSE ├── Makefile ├── README ├── abort_clone.c ├── acl_perms.c ├── adopted_processes.c ├── alarm_clone.c ├── append_seek_write.c ├── atomic_append.c ├── chattr_clone.c ├── child_status_waitid.c ├── chmod_a_plus_rX.c ├── cp_holes.c ├── dup_clone.c ├── dup_common_attrs.c ├── eaccess_clone.c ├── filebuff_perf ├── 1000000s.txt ├── 1024.txt ├── 1024s.txt ├── 128.txt ├── 256.txt ├── 512.txt ├── 64.txt └── README ├── free_and_sbrk.c ├── getcwd_clone.c ├── getcwd_clone_test.sh ├── getpwnam_clone.c ├── initgroups_clone.c ├── inotify_recursive.c ├── inotify_recursive_test.sh ├── large_file2.c ├── list_processes_for_user.c ├── longjmp_error.c ├── malloc_free_clone.c ├── my_env.c ├── nftw_statistics.c ├── processes_open_file.c ├── pstree.c ├── ptmr_sigev_signal_sigwaitinfo.c ├── readv_writev_clone.c ├── realpath_clone.c ├── realpath_clone_test.sh ├── realtime_plus_standard_signals.c ├── setfattr_clone.c ├── sig_speed_sigsuspend.txt ├── sig_speed_sigwaitinfo.c ├── sig_speed_sigwaitinfo.txt ├── sigaction_nodefer.c ├── sigaction_resethand.c ├── sigcont_blocked.c ├── signal_ign_demo.c ├── t_clock_nanosleep_abs.c ├── tail.c ├── tee.c ├── vfork_closing_file_descriptor.c └── zombie_parent.c /.gitignore: -------------------------------------------------------------------------------- 1 | tee 2 | cp_holes 3 | large_file2 4 | append_seek_write 5 | atomic_append 6 | dup_clone 7 | dup_common_attrs 8 | readv_writev_clone 9 | longjmp_error 10 | my_env 11 | free_and_sbrk 12 | malloc_free_clone 13 | getpwnam_clone 14 | initgroups_clone 15 | list_processes_for_user 16 | pstree 17 | processes_open_file 18 | tail 19 | eaccess_clone 20 | chmod_a_plus_rX 21 | chattr_clone 22 | setfattr_clone 23 | acl_perms 24 | acl_perms.o 25 | realpath_clone 26 | getcwd_clone 27 | nftw_statistics 28 | inotify_recursive 29 | signal_ign_demo 30 | sigaction_nodefer 31 | sigaction_resethand 32 | abort_clone 33 | sigcont_blocked 34 | realtime_plus_standard_signals 35 | sig_speed_sigwaitinfo 36 | alarm_clone 37 | t_clock_nanosleep_abs 38 | t_clock_nanosleep_abs.o 39 | ptmr_sigev_signal_sigwaitinfo 40 | ptmr_sigev_signal_sigwaitinfo.o 41 | vfork_closing_file_descriptor 42 | adopted_processes 43 | zombie_parent 44 | child_status_waitid -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2012 Tim Baumann, http://timbaumann.info 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include ../Makefile.inc 2 | 3 | GEN_EXE = tee cp_holes append_seek_write atomic_append dup_clone dup_common_attrs \ 4 | readv_writev_clone longjmp_error my_env free_and_sbrk malloc_free_clone \ 5 | getpwnam_clone initgroups_clone tail chmod_a_plus_rX realpath_clone \ 6 | getcwd_clone signal_ign_demo sigaction_nodefer sigaction_resethand \ 7 | abort_clone sigcont_blocked realtime_plus_standard_signals \ 8 | sig_speed_sigwaitinfo alarm_clone vfork_closing_file_descriptor \ 9 | adopted_processes zombie_parent 10 | 11 | LINUX_EXE = large_file2 list_processes_for_user pstree processes_open_file \ 12 | eaccess_clone chattr_clone setfattr_clone acl_perms nftw_statistics \ 13 | inotify_recursive t_clock_nanosleep_abs ptmr_sigev_signal_sigwaitinfo \ 14 | child_status_waitid 15 | 16 | EXE = ${GEN_EXE} ${LINUX_EXE} 17 | 18 | all : ${EXE} 19 | 20 | allgen : ${GEN_EXE} 21 | 22 | t_clock_nanosleep_abs : t_clock_nanosleep_abs.o 23 | ${CC} -o $@ t_clock_nanosleep_abs.o ${CFLAGS} ${LDLIBS} ${LINUX_LIBRT} 24 | 25 | acl_perms : acl_perms.o 26 | ${CC} -o $@ acl_perms.o ${CFLAGS} ${LDLIBS} ${LINUX_LIBACL} 27 | 28 | ptmr_sigev_signal_sigwaitinfo: ptmr_sigev_signal_sigwaitinfo.o 29 | ${CC} -o $@ ptmr_sigev_signal_sigwaitinfo.o ${CFLAGS} ${LDLIBS} ${LINUX_LIBRT} 30 | 31 | clean : 32 | ${RM} ${EXE} *.o 33 | 34 | showall : 35 | @ echo ${EXE} 36 | 37 | ${EXE} : ${LPLIB} # True as a rough approximation 38 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Building 2 | ======== 3 | 4 | 1. Download the source code for the example programs from http://man7.org/tlpi/code/index.html 5 | 2. Place this folder in the main directory 6 | 3. Run `make` -------------------------------------------------------------------------------- /abort_clone.c: -------------------------------------------------------------------------------- 1 | /* Exercise 21-1: Implement abort() */ 2 | 3 | #include 4 | #include "tlpi_hdr.h" 5 | 6 | void my_abort (void); 7 | void handler (int); 8 | 9 | void my_abort (void) { 10 | raise(SIGABRT); 11 | 12 | /* reset handling of SIGABRT */ 13 | struct sigaction action; 14 | action.sa_handler = SIG_DFL; 15 | sigemptyset(&action.sa_mask); 16 | action.sa_flags = 0; 17 | sigaction(SIGABRT, &action, NULL); 18 | 19 | raise(SIGABRT); 20 | } 21 | 22 | void handler (int s) { 23 | fprintf(stderr, "SIGABRT handler\n"); /* UNSAFE */ 24 | } 25 | 26 | int main (int argc, char *argv[]) { 27 | struct sigaction action; 28 | action.sa_handler = handler; 29 | sigemptyset(&action.sa_mask); 30 | action.sa_flags = 0; 31 | sigaction(SIGABRT, &action, NULL); 32 | 33 | fprintf(stderr, "aborting...\n"); 34 | my_abort(); 35 | fprintf(stderr, "abort didn't succeed!\n"); 36 | exit(EXIT_SUCCESS); 37 | } -------------------------------------------------------------------------------- /acl_perms.c: -------------------------------------------------------------------------------- 1 | /* Exercise 17-1 */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include "tlpi_hdr.h" 7 | #include "ugid_functions.h" 8 | 9 | void usage (char *); 10 | int maskPermset (acl_permset_t, acl_permset_t); 11 | int handleError(char *, int); 12 | void printPerms (acl_permset_t); 13 | 14 | void usage (char *prog_name) { 15 | usageErr("%s (u|g) (user|group) file\n", prog_name); 16 | } 17 | 18 | int maskPermset (acl_permset_t perms, acl_permset_t mask) { 19 | int a; 20 | 21 | a = acl_get_perm(mask, ACL_READ); 22 | if (a == -1) { return -1; } 23 | if (a == 0) { 24 | if (acl_delete_perm(perms, ACL_READ) == -1) { 25 | return -1; 26 | } 27 | } 28 | 29 | a = acl_get_perm(mask, ACL_WRITE); 30 | if (a == -1) { return -1; } 31 | if (a == 0) { 32 | if (acl_delete_perm(perms, ACL_WRITE) == -1) { 33 | return -1; 34 | } 35 | } 36 | 37 | a = acl_get_perm(mask, ACL_EXECUTE); 38 | if (a == -1) { return -1; } 39 | if (a == 0) { 40 | if (acl_delete_perm(perms, ACL_EXECUTE) == -1) { 41 | return -1; 42 | } 43 | } 44 | 45 | return 0; 46 | } 47 | 48 | int handleError(char *str, int a) { 49 | if (a == -1) { 50 | errExit(str); 51 | } 52 | return a; 53 | } 54 | 55 | void printPerms (acl_permset_t permset) { 56 | printf( 57 | "%c%c%c", 58 | (handleError("acl_get_perm", acl_get_perm(permset, ACL_READ)) ? 'r' : '-'), 59 | (handleError("acl_get_perm", acl_get_perm(permset, ACL_WRITE)) ? 'w' : '-'), 60 | (handleError("acl_get_perm", acl_get_perm(permset, ACL_EXECUTE)) ? 'x' : '-') 61 | ); 62 | } 63 | 64 | int main (int argc, char *argv[]) { 65 | if (argc != 4) { usage(argv[0]); } 66 | 67 | char mode; 68 | if (strcmp(argv[1], "u") == 0) { mode = 'u'; } 69 | else if (strcmp(argv[1], "g") == 0) { mode = 'g'; } 70 | else { usage(argv[0]); } 71 | 72 | uid_t uid; 73 | gid_t gid; 74 | if (mode == 'u') { 75 | uid = userIdFromName(argv[2]); 76 | if (uid == -1) { 77 | errExit("couldn't find user %s\n", argv[2]); 78 | } 79 | } else { 80 | // mode == 'g' 81 | gid = groupIdFromName(argv[2]); 82 | if (gid == -1) { 83 | errExit("couldn't find group %s\n", argv[2]); 84 | } 85 | } 86 | 87 | char *filepath = argv[3]; 88 | 89 | struct stat stats; 90 | if (stat(filepath, &stats) == -1) { errExit("stat\n"); } 91 | 92 | acl_t acl = acl_get_file(filepath, ACL_TYPE_ACCESS); 93 | if (acl == NULL) { errExit("acl_get_file\n"); } 94 | 95 | acl_entry_t entry; 96 | acl_tag_t tag; 97 | int entryId; 98 | 99 | int mask_found = 0; 100 | acl_entry_t mask; 101 | for (entryId = ACL_FIRST_ENTRY; ; entryId = ACL_NEXT_ENTRY) { 102 | if (acl_get_entry(acl, entryId, &entry) == 1) { 103 | break; 104 | } 105 | if ((tag = acl_get_tag_type(entry, &tag)) == -1) { 106 | errExit("acl_get_tag_type\n"); 107 | } 108 | if (tag == ACL_MASK) { 109 | mask_found = 1; 110 | mask = entry; 111 | break; 112 | } 113 | } 114 | 115 | acl_entry_t needle; 116 | uid_t *uid_p; 117 | gid_t *gid_p; 118 | for (entryId = ACL_FIRST_ENTRY; ; entryId = ACL_NEXT_ENTRY) { 119 | if (acl_get_entry(acl, entryId, &entry) != 1) { 120 | errExit( 121 | "couldn't find an entry for the specified %s\n", 122 | mode == 'u' ? "user" : "group" 123 | ); 124 | } 125 | if (acl_get_tag_type(entry, &tag) == -1) { 126 | errExit("acl_get_tag_type\n"); 127 | } 128 | 129 | if (mode == 'u') { 130 | if (uid == stats.st_uid && tag == ACL_USER_OBJ) { 131 | needle = entry; 132 | break; 133 | } 134 | if (tag != ACL_USER) { continue; } 135 | uid_p = acl_get_qualifier(entry); 136 | if (uid_p == NULL) { errExit("acl_get_qualifier\n"); } 137 | if (*uid_p == uid) { 138 | needle = entry; 139 | break; 140 | } 141 | } 142 | 143 | if (mode == 'g') { 144 | if (gid == stats.st_gid && tag == ACL_GROUP_OBJ) { 145 | needle = entry; 146 | break; 147 | } 148 | if (tag != ACL_GROUP) { continue; } 149 | gid_p = acl_get_qualifier(entry); 150 | if (gid_p == NULL) { errExit("acl_get_qualifier\n"); } 151 | if (*gid_p == gid) { 152 | needle = entry; 153 | break; 154 | } 155 | } 156 | } 157 | 158 | acl_permset_t needle_perms; 159 | if (acl_get_permset(needle, &needle_perms) == -1) { 160 | errExit("acl_get_permset\n"); 161 | } 162 | printPerms(needle_perms); 163 | printf("\n"); 164 | if (mask_found && !(mode == 'u' && uid == stats.st_uid && tag == ACL_USER_OBJ)) { 165 | acl_permset_t mask_perms; 166 | if (acl_get_permset(mask, &mask_perms) == -1) { 167 | errExit("acl_get_permset\n"); 168 | } 169 | printf("Effective permissions: "); 170 | if (maskPermset(needle_perms, mask_perms) == -1) { 171 | errExit("maskPermset\n"); 172 | } 173 | printPerms(needle_perms); 174 | printf("\n"); 175 | } 176 | 177 | exit(EXIT_SUCCESS); 178 | } -------------------------------------------------------------------------------- /adopted_processes.c: -------------------------------------------------------------------------------- 1 | /* Exercise 26-1: Write a program to verify that when a child’s parent terminates, a call to getppid() 2 | returns 1 (the process ID of init). */ 3 | 4 | #include "tlpi_hdr.h" 5 | #include 6 | 7 | int main (int argc, char *argv[]) { 8 | setbuf(stdout, NULL); 9 | 10 | pid_t pid; 11 | switch (pid = fork()) { 12 | case -1: 13 | errExit("fork\n"); 14 | break; 15 | case 0: 16 | printf("child started with PID %ld and parent %ld\n", (long) getpid(), (long) getppid()); 17 | sleep(3); 18 | printf("child's parent: %ld\n", (long) getppid()); 19 | exit(0); 20 | default: 21 | sleep(1); 22 | printf("parent exiting\n"); 23 | exit(0); 24 | } 25 | } -------------------------------------------------------------------------------- /alarm_clone.c: -------------------------------------------------------------------------------- 1 | /* Exercise 23-1: Implement alarm() using setitimer() */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include "tlpi_hdr.h" 7 | 8 | unsigned int my_alarm (unsigned int); 9 | void handler (int); 10 | 11 | unsigned int my_alarm (unsigned int secs) { 12 | struct itimerval new, old; 13 | new.it_value.tv_sec = (time_t) secs; 14 | new.it_value.tv_usec = 0; 15 | new.it_interval.tv_sec = 0; 16 | new.it_interval.tv_usec = 0; 17 | setitimer(ITIMER_REAL, &new, &old); 18 | return (unsigned int) old.it_value.tv_sec; 19 | } 20 | 21 | void handler (int sig) { 22 | printf("Got alarm\n"); /* UNSAFE */ 23 | } 24 | 25 | int main (int argc, char *argv[]) { 26 | struct sigaction alarm_action; 27 | sigemptyset(&alarm_action.sa_mask); 28 | alarm_action.sa_flags = 0; 29 | alarm_action.sa_handler = handler; 30 | if (sigaction(SIGALRM, &alarm_action, NULL) == -1) { 31 | errExit("sigaction"); 32 | } 33 | 34 | my_alarm(3); 35 | printf("%u\n", my_alarm(3)); 36 | pause(); 37 | my_alarm(2); 38 | pause(); 39 | my_alarm(1); 40 | pause(); 41 | 42 | exit(EXIT_SUCCESS); 43 | } -------------------------------------------------------------------------------- /append_seek_write.c: -------------------------------------------------------------------------------- 1 | /* Exercise 5-2 */ 2 | 3 | #include 4 | #include 5 | #include "tlpi_hdr.h" 6 | 7 | int main (int argc, char *argv[]) { 8 | if (argc != 2) { 9 | usageErr("%s filename", argv[0]); 10 | } 11 | 12 | int fd = open(argv[1], O_WRONLY | O_APPEND | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR); 13 | if (fd == -1) { errExit("open"); } 14 | char s[] = "abcdefghi"; 15 | char t[] = "jklmnopqr"; 16 | if (write(fd, s, strlen(s)) != strlen(s)) { errExit("write 1"); } 17 | if (lseek(fd, 0, SEEK_SET) == -1) { errExit("seeking"); } 18 | if (write(fd, t, strlen(t)) != strlen(t)) { errExit("write 2"); } 19 | if (close(fd) == -1) { errExit("close output"); } 20 | exit(EXIT_SUCCESS); 21 | } 22 | -------------------------------------------------------------------------------- /atomic_append.c: -------------------------------------------------------------------------------- 1 | /* Exercise 5-3 */ 2 | 3 | #include 4 | #include 5 | #include "tlpi_hdr.h" 6 | 7 | int main (int argc, char *argv[]) { 8 | if (argc < 3 || argc > 4) { 9 | usageErr("%s filename num-bytes [x]", argv[0]); 10 | } 11 | 12 | long n = getLong(argv[2], GN_NONNEG | GN_ANY_BASE, "num-bytes"); 13 | Boolean x = argc == 4 && strcmp(argv[3], "x") == 0; 14 | int flags = O_WRONLY | O_CREAT; 15 | if (!x) { flags |= O_APPEND; } 16 | int fd = open(argv[1], flags, S_IWUSR | S_IRUSR); 17 | if (fd == -1) { errExit("open"); } 18 | 19 | while (n-- > 0) { 20 | if (x) { 21 | if (lseek(fd, 0, SEEK_END) == -1) { errExit("seek"); } 22 | } 23 | if (write(fd, "a", 1) == -1) { 24 | errExit("write byte a"); 25 | } 26 | } 27 | 28 | if (close(fd) == -1) { 29 | errExit("close output"); 30 | } 31 | 32 | exit(EXIT_SUCCESS); 33 | } 34 | -------------------------------------------------------------------------------- /chattr_clone.c: -------------------------------------------------------------------------------- 1 | /* Exercise 15-7: partial implementation of chattr(1) */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include "tlpi_hdr.h" 7 | 8 | void usage (const char *); 9 | unsigned int char2flag (char); 10 | void chattr (char, unsigned int, const char *); 11 | 12 | void usage (const char *progname) { 13 | usageErr("%s mode files...\n", progname); 14 | } 15 | 16 | unsigned int char2flag (char ch) { 17 | switch (ch) { 18 | case 'a': return FS_APPEND_FL; 19 | case 'c': return FS_COMPR_FL; 20 | case 'd': return FS_NODUMP_FL; 21 | case 'e': return FS_EXTENT_FL; 22 | case 'i': return FS_IMMUTABLE_FL; 23 | case 'j': return FS_JOURNAL_DATA_FL; 24 | case 's': return FS_SECRM_FL; 25 | case 't': return FS_NOTAIL_FL; 26 | case 'u': return FS_UNRM_FL; 27 | case 'A': return FS_NOATIME_FL; 28 | case 'D': return FS_DIRSYNC_FL; 29 | case 'S': return FS_SYNC_FL; 30 | case 'T': return FS_TOPDIR_FL; 31 | default: 32 | cmdLineErr("unrecognized flag: %c\n", ch); 33 | } 34 | } 35 | 36 | void chattr (char action, unsigned int eattr, const char *pathname) { 37 | int fd = open(pathname, O_RDONLY); 38 | if (fd == -1) { errExit("open"); } 39 | 40 | if (action == '+' || action == '-') { 41 | unsigned int existing_eattr; 42 | if (ioctl(fd, FS_IOC_GETFLAGS, &existing_eattr) == -1) { 43 | errExit("ioctl(fd, FS_IOC_GETFLAGS, ...) failed\n"); 44 | } 45 | if (action == '+') { 46 | eattr |= existing_eattr; 47 | } else { 48 | eattr = existing_eattr & ~eattr; 49 | } 50 | } 51 | 52 | if (ioctl(fd, FS_IOC_SETFLAGS, &eattr) == -1) { 53 | errExit("ioctl(fd, FS_IOC_SETFLAGS, ...) failed\n"); 54 | } 55 | 56 | if (close(fd) == -1) { fprintf(stderr, "close failed\n"); } 57 | } 58 | 59 | int main (int argc, char *argv[]) { 60 | if (argc < 3) { usage(argv[0]); } 61 | 62 | char *mode = argv[1]; 63 | char action = *mode++; 64 | switch (action) { 65 | case '+': 66 | case '-': 67 | case '=': 68 | break; 69 | default: 70 | cmdLineErr("expected mode to begin with a +, - or = sign.\n"); 71 | } 72 | 73 | unsigned int eattr = 0; 74 | while (*mode != '\0') { 75 | eattr |= char2flag(*mode++); 76 | } 77 | 78 | for (int i = 2; i < argc; i++) { 79 | chattr(action, eattr, argv[i]); 80 | } 81 | 82 | exit(EXIT_SUCCESS); 83 | } -------------------------------------------------------------------------------- /child_status_waitid.c: -------------------------------------------------------------------------------- 1 | /* Exercise 26-3: Replace the use of waitpid() with waitid() in Listing 26-3 (child_status.c). The call to 2 | printWaitStatus() will need to be replaced by code that prints relevant fields from 3 | the siginfo_t structure returned by waitid(). */ 4 | 5 | /**********************************************************************\ 6 | * Copyright (C) Michael Kerrisk, 2010. * 7 | * * 8 | * This program is free software. You may use, modify, and redistribute * 9 | * it under the terms of the GNU Affero General Public License as * 10 | * published by the Free Software Foundation, either version 3 or (at * 11 | * your option) any later version. This program is distributed without * 12 | * any warranty. See the file COPYING for details. * 13 | \**********************************************************************/ 14 | 15 | /* child_status.c 16 | 17 | Demonstrate the use of wait() and the W* macros for analyzing the child 18 | status returned by wait() 19 | 20 | Usage: child_status [exit-status] 21 | 22 | If "exit-status" is supplied, then the child immediately exits with this 23 | status. If no command-line argument is supplied then the child loops waiting 24 | for signals that either cause it to stop or to terminate - both conditions 25 | can be detected and differentiated by the parent. The parent process 26 | repeatedly waits on the child until it detects that the child either exited 27 | normally or was killed by a signal. 28 | */ 29 | 30 | #define _GNU_SOURCE /* get strsignal() */ 31 | 32 | #include 33 | #include "print_wait_status.h" /* Declares printWaitStatus() */ 34 | #include "tlpi_hdr.h" 35 | #include 36 | 37 | int 38 | main(int argc, char *argv[]) 39 | { 40 | siginfo_t childInfo; 41 | 42 | if (argc > 1 && strcmp(argv[1], "--help") == 0) 43 | usageErr("%s [exit-status]\n", argv[0]); 44 | 45 | switch (fork()) { 46 | case -1: errExit("fork"); 47 | 48 | case 0: /* Child: either exits immediately with given 49 | status or loops waiting for signals */ 50 | printf("Child started with PID = %ld\n", (long) getpid()); 51 | if (argc > 1) /* Status supplied on command line? */ 52 | exit(getInt(argv[1], 0, "exit-status")); 53 | else /* Otherwise, wait for signals */ 54 | for (;;) 55 | pause(); 56 | exit(EXIT_FAILURE); /* Not reached, but good practice */ 57 | 58 | default: /* Parent: repeatedly wait on child until it 59 | either exits or is terminated by a signal */ 60 | for (;;) { 61 | if (waitid(P_ALL, 0, &childInfo, WEXITED | WSTOPPED | WCONTINUED) == -1) { 62 | errExit("waitid\n"); 63 | } 64 | 65 | /* Print status in hex, and as separate decimal bytes */ 66 | 67 | printf("waitpid() returned: PID=%ld; UID=%ld; status=%d\n", 68 | (long) childInfo.si_pid, (long) childInfo.si_uid, 69 | childInfo.si_status); 70 | 71 | if (childInfo.si_code == CLD_EXITED) { 72 | printf("child exited with code %d\n", childInfo.si_status); 73 | } else if (childInfo.si_code == CLD_KILLED) { 74 | printf("child killed with signal %d (%s)\n", childInfo.si_status, strsignal(childInfo.si_code)); 75 | } else if (childInfo.si_code == CLD_STOPPED) { 76 | printf("child stopped with signal %d (%s)\n", childInfo.si_status, strsignal(childInfo.si_code)); 77 | } else if (childInfo.si_code == CLD_CONTINUED) { 78 | printf("child continued with signal %d (%s)\n", childInfo.si_status, strsignal(childInfo.si_code)); 79 | } 80 | 81 | if (childInfo.si_code == CLD_EXITED || childInfo.si_code == CLD_KILLED) 82 | exit(EXIT_SUCCESS); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /chmod_a_plus_rX.c: -------------------------------------------------------------------------------- 1 | /* Exercise 15-6: implement `chmod a+rX` using stat(2) and chmod(2) */ 2 | 3 | #include 4 | #include "tlpi_hdr.h" 5 | 6 | void chmod_a_plus_rX (const char *); 7 | 8 | void chmod_a_plus_rX (const char *filename) { 9 | struct stat stats; 10 | if (stat(filename, &stats) == -1) { 11 | fatal("couldn't get file stats\n"); 12 | } 13 | 14 | mode_t curr_mode = stats.st_mode; 15 | mode_t new_mode = curr_mode | S_IRUSR | S_IRGRP | S_IROTH; 16 | 17 | if (S_ISDIR(curr_mode) || curr_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { 18 | new_mode |= S_IXUSR | S_IXGRP | S_IXOTH; 19 | } 20 | 21 | if (chmod(filename, new_mode) == -1) { 22 | fatal("couln't set new file permissions\n"); 23 | } 24 | } 25 | 26 | int main (int argc, char *argv[]) { 27 | if (argc < 2) { 28 | usageErr("%s file\n", argv[0]); 29 | } 30 | 31 | for (int i = 1; i < argc; i++) { 32 | chmod_a_plus_rX(argv[i]); 33 | } 34 | 35 | exit(EXIT_SUCCESS); 36 | } -------------------------------------------------------------------------------- /cp_holes.c: -------------------------------------------------------------------------------- 1 | /* Exercise 4-2 */ 2 | 3 | #include 4 | #include 5 | #include "tlpi_hdr.h" 6 | 7 | #ifndef BUF_SIZE 8 | #define BUF_SIZE 1024 9 | #endif 10 | 11 | int main (int argc, char *argv[]) { 12 | if (argc != 3 || strcmp(argv[1], "--help") == 0) { 13 | usageErr("%s old-file new-file\n", argv[0]); 14 | } 15 | 16 | int inputFd = open(argv[1], O_RDONLY); 17 | if (inputFd == -1) { 18 | errExit("opening file %s", argv[1]); 19 | } 20 | 21 | int openFlags = O_CREAT | O_WRONLY | O_TRUNC; 22 | mode_t filePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; /* rw-rw-rw- */ 23 | int outputFd = open(argv[2], openFlags, filePerms); 24 | if (outputFd == -1) { 25 | errExit("opening file %s", argv[2]); 26 | } 27 | 28 | ssize_t numRead; 29 | char buf[BUF_SIZE]; 30 | while ((numRead = read(inputFd, buf, BUF_SIZE)) > 0) { 31 | int i = 0, j; 32 | while (i < numRead) { 33 | for (j = i; j < numRead; j++) { 34 | if (buf[j] == '\0') { break; } 35 | } 36 | size_t nonzeroBytes = (size_t) j-i; 37 | if (nonzeroBytes > 0) { 38 | if (write(outputFd, &buf[i], nonzeroBytes) != nonzeroBytes) { 39 | fatal("couldn't write whole buffer"); 40 | } 41 | } 42 | 43 | for (i = j; j < numRead; j++) { 44 | if (buf[j] != '\0') { break; } 45 | } 46 | off_t zeroBytes = (off_t) j-i; 47 | if (zeroBytes > 0) { 48 | if (lseek(outputFd, zeroBytes, SEEK_CUR) == -1) { 49 | fatal("couldn't write hole"); 50 | } 51 | } 52 | i = j; 53 | } 54 | } 55 | if (numRead == -1) { errExit("read"); } 56 | 57 | if (close(inputFd) == -1) { errExit("close input"); } 58 | if (close(outputFd) == -1) { errExit("close output"); } 59 | 60 | exit(EXIT_SUCCESS); 61 | } -------------------------------------------------------------------------------- /dup_clone.c: -------------------------------------------------------------------------------- 1 | /* Exercise 5-4 */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "tlpi_hdr.h" 8 | 9 | /* guess */ 10 | #define OPEN_MAX 256 11 | 12 | int my_dup (int); 13 | int my_dup2 (int, int); 14 | off_t get_offset (int); 15 | 16 | int my_dup (int oldfd) { 17 | return fcntl(oldfd, F_DUPFD, 0); 18 | } 19 | 20 | int my_dup2 (int oldfd, int newfd) { 21 | // based on the information from `man -s2 dup2` 22 | 23 | if (newfd < 0 || newfd >= OPEN_MAX) { 24 | errno = EBADF; 25 | return -1; 26 | } 27 | 28 | if (fcntl(oldfd, F_GETFL) == -1) { 29 | return -1; 30 | } 31 | 32 | if (oldfd == newfd) { 33 | return oldfd; 34 | } 35 | 36 | close(newfd); 37 | return fcntl(oldfd, F_DUPFD, newfd); 38 | } 39 | 40 | off_t get_offset (int fd) { 41 | off_t o = lseek(fd, 0, SEEK_CUR); 42 | if (o == -1) { errExit("getting offset"); } 43 | return o; 44 | } 45 | 46 | int main (int argc, char *argv[]) { 47 | if (argc != 2) { 48 | usageErr("%s testfile\n", argv[0]); 49 | } 50 | 51 | int fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR); 52 | if (fd == -1) { errExit("open testfile"); } 53 | 54 | assert(my_dup(-1) == -1); 55 | assert(my_dup(42) == -1); 56 | 57 | int dup = my_dup(fd); 58 | assert(dup != -1); 59 | assert(dup != fd); 60 | assert(get_offset(fd) == 0); 61 | assert(get_offset(dup) == 0); 62 | char text[] = "abc"; 63 | size_t text_len = strlen(text); 64 | if (write(dup, text, text_len) != (ssize_t) text_len) { errExit("writing 'abc'"); } 65 | assert(get_offset(fd) == (off_t) text_len); 66 | assert(get_offset(dup) == (off_t) text_len); 67 | 68 | assert(my_dup2(42, 42) == -1); 69 | assert(my_dup2(STDOUT_FILENO, STDOUT_FILENO) == STDOUT_FILENO); 70 | assert(my_dup2(fd, -1) == -1); 71 | assert(my_dup2(fd, OPEN_MAX) == -1); 72 | assert(my_dup2(42, STDOUT_FILENO) == -1); 73 | 74 | dup = my_dup2(fd, STDOUT_FILENO); 75 | assert(dup == STDOUT_FILENO); 76 | char greeting[] = "Hello World!"; 77 | size_t combined_len = text_len + strlen(greeting); 78 | fprintf(stderr, "this should be the only output of this program\n"); 79 | printf(greeting); 80 | fflush(stdout); 81 | assert(get_offset(dup) == combined_len); 82 | assert(get_offset(fd) == combined_len); 83 | 84 | exit(EXIT_SUCCESS); 85 | } -------------------------------------------------------------------------------- /dup_common_attrs.c: -------------------------------------------------------------------------------- 1 | /* Exercise 5-5 */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include "tlpi_hdr.h" 7 | 8 | off_t get_offset (int); 9 | 10 | off_t get_offset (int fd) { 11 | off_t o = lseek(fd, 0, SEEK_CUR); 12 | if (o == -1) { errExit("get offset"); } 13 | return o; 14 | } 15 | 16 | int main (int argc, char *argv[]) { 17 | if (argc != 2) { 18 | usageErr("%s testfile\n", argv[0]); 19 | } 20 | 21 | int flags = O_RDWR | O_CREAT | O_TRUNC; 22 | mode_t filemode = S_IWUSR | S_IRUSR; 23 | int fd = open(argv[1], flags, filemode); 24 | if (fd == -1) { errExit("open testfile"); } 25 | int d = dup(fd); 26 | assert(d != -1); 27 | assert(fd != d); 28 | assert(fd != d); 29 | assert(get_offset(fd) == 0); 30 | assert(get_offset(d) == 0); 31 | char text[] = "lorem ipsum dolor sit amet"; 32 | size_t text_len = strlen(text); 33 | if (write(fd, text, text_len) == -1) { errExit("writing text"); } 34 | assert(get_offset(fd) == text_len); 35 | assert(get_offset(d) == text_len); 36 | int f = fcntl(fd, F_GETFL); 37 | assert(f == O_RDWR); /* O_CREAT and O_TRUNC are apparently excluded after opening the file */ 38 | assert(fcntl(d, F_GETFL) == O_RDWR); 39 | int new_flags = O_RDWR | O_APPEND; 40 | if (fcntl(fd, F_SETFL, new_flags) == -1) { errExit("setting file mode"); } 41 | assert(fcntl(fd, F_GETFL) == new_flags); 42 | assert(fcntl(d, F_GETFL) == new_flags); 43 | } -------------------------------------------------------------------------------- /eaccess_clone.c: -------------------------------------------------------------------------------- 1 | /* Exercise 15-4: implement eaccess (execute this program with sudo) */ 2 | 3 | #define _GNU_SOURCE 4 | 5 | #include 6 | #include 7 | #include 8 | #include "tlpi_hdr.h" 9 | 10 | #define ROOT_UID 0 11 | 12 | #ifndef OTHER_UID 13 | #define OTHER_UID 1000 14 | #endif 15 | 16 | int my_eaccess (const char *, int); 17 | int setresuid_checked (uid_t, uid_t, uid_t); 18 | 19 | int my_eaccess (const char *pathname, int mode) { 20 | uid_t ruid = getuid(); 21 | uid_t euid = geteuid(); 22 | setresuid(euid, -1, -1); 23 | gid_t rgid = getgid(); 24 | gid_t egid = getegid(); 25 | setresgid(egid, -1, -1); 26 | 27 | int a = access(pathname, mode); 28 | 29 | setresuid(ruid, -1, -1); 30 | setresgid(rgid, -1, -1); 31 | 32 | return a; 33 | } 34 | 35 | int setresuid_checked (uid_t ruid, uid_t euid, uid_t suid) { 36 | if (setresuid(ruid, euid, suid) == -1) { 37 | fatal("coulnd't set real, effective and saved user ids to %ld, %ld and " 38 | "%ld respectively.\n", (long) ruid, (long) euid, (long) suid); 39 | } 40 | return 0; 41 | } 42 | 43 | int main (int argc, char *argv[]) { 44 | if (argc != 2) { 45 | usageErr("%s testfile\n", argv[0]); 46 | } 47 | 48 | char *filename = argv[1]; 49 | 50 | setresuid_checked(ROOT_UID, ROOT_UID, ROOT_UID); 51 | 52 | mode_t mode = S_IRUSR | S_IWUSR; 53 | int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, mode); 54 | if (fd == -1) { 55 | fatal("couln't create file %s\n", filename); 56 | } 57 | close(fd); 58 | int access_mode = W_OK | R_OK; 59 | 60 | assert(access(filename, access_mode) == 0); 61 | assert(my_eaccess(filename, access_mode) == 0); 62 | 63 | setresuid_checked(OTHER_UID, OTHER_UID, -1); 64 | assert(access(filename, access_mode) == -1); 65 | assert(my_eaccess(filename, access_mode) == -1); 66 | 67 | setresuid_checked(ROOT_UID, OTHER_UID, -1); 68 | assert(access(filename, access_mode) == 0); 69 | assert(my_eaccess(filename, access_mode) == -1); 70 | 71 | setresuid_checked(OTHER_UID, ROOT_UID, -1); 72 | assert(access(filename, access_mode) == -1); 73 | assert(my_eaccess(filename, access_mode) == 0); 74 | 75 | exit(EXIT_SUCCESS); 76 | } -------------------------------------------------------------------------------- /filebuff_perf/1000000s.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timjb/tlpi-exercises/e1aeff37953602df8a82dec5eb41df2874f23600/filebuff_perf/1000000s.txt -------------------------------------------------------------------------------- /filebuff_perf/1024.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timjb/tlpi-exercises/e1aeff37953602df8a82dec5eb41df2874f23600/filebuff_perf/1024.txt -------------------------------------------------------------------------------- /filebuff_perf/1024s.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timjb/tlpi-exercises/e1aeff37953602df8a82dec5eb41df2874f23600/filebuff_perf/1024s.txt -------------------------------------------------------------------------------- /filebuff_perf/128.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timjb/tlpi-exercises/e1aeff37953602df8a82dec5eb41df2874f23600/filebuff_perf/128.txt -------------------------------------------------------------------------------- /filebuff_perf/256.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timjb/tlpi-exercises/e1aeff37953602df8a82dec5eb41df2874f23600/filebuff_perf/256.txt -------------------------------------------------------------------------------- /filebuff_perf/512.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timjb/tlpi-exercises/e1aeff37953602df8a82dec5eb41df2874f23600/filebuff_perf/512.txt -------------------------------------------------------------------------------- /filebuff_perf/64.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timjb/tlpi-exercises/e1aeff37953602df8a82dec5eb41df2874f23600/filebuff_perf/64.txt -------------------------------------------------------------------------------- /filebuff_perf/README: -------------------------------------------------------------------------------- 1 | Exercise 13-1: 2 | Measure the performance of copying a file with 100,000,000 bytes. 3 | The file 1024.txt contains the times for copying with a 1024 bytes large buffer. 4 | The 's' in 1024.txt means that O_SYNC was specified when opening the file. 5 | -------------------------------------------------------------------------------- /free_and_sbrk.c: -------------------------------------------------------------------------------- 1 | /* Exercise 7-1: Modification of one example in the book */ 2 | 3 | /**********************************************************************\ 4 | * Copyright (C) Michael Kerrisk, 2010. * 5 | * * 6 | * This program is free software. You may use, modify, and redistribute * 7 | * it under the terms of the GNU Affero General Public License as * 8 | * published by the Free Software Foundation, either version 3 or (at * 9 | * your option) any later version. This program is distributed without * 10 | * any warranty. See the file COPYING for details. * 11 | \**********************************************************************/ 12 | 13 | /* free_and_sbrk.c 14 | 15 | Test if free(3) actually lowers the program break. 16 | 17 | Usage: free_and_sbrk num-allocs block-size [step [min [max]]] 18 | 19 | Try: free_and_sbrk 1000 10240 2 1 1000 20 | free_and_sbrk 1000 10240 1 1 999 21 | free_and_sbrk 1000 10240 1 1 500 22 | 23 | (Only the last of these should see the program break lowered.) 24 | */ 25 | #define _BSD_SOURCE 26 | #include "tlpi_hdr.h" 27 | 28 | #define MAX_ALLOCS 1000000 29 | 30 | int 31 | main(int argc, char *argv[]) 32 | { 33 | char *ptr[MAX_ALLOCS]; 34 | int freeStep, freeMin, freeMax, blockSize, numAllocs, j; 35 | 36 | printf("\n"); 37 | 38 | if (argc < 3 || strcmp(argv[1], "--help") == 0) 39 | usageErr("%s num-allocs block-size [step [min [max]]]\n", argv[0]); 40 | 41 | numAllocs = getInt(argv[1], GN_GT_0, "num-allocs"); 42 | if (numAllocs > MAX_ALLOCS) 43 | cmdLineErr("num-allocs > %d\n", MAX_ALLOCS); 44 | 45 | blockSize = getInt(argv[2], GN_GT_0 | GN_ANY_BASE, "block-size"); 46 | 47 | freeStep = (argc > 3) ? getInt(argv[3], GN_GT_0, "step") : 1; 48 | freeMin = (argc > 4) ? getInt(argv[4], GN_GT_0, "min") : 1; 49 | freeMax = (argc > 5) ? getInt(argv[5], GN_GT_0, "max") : numAllocs; 50 | 51 | if (freeMax > numAllocs) 52 | cmdLineErr("free-max > num-allocs\n"); 53 | 54 | printf("Initial program break: %10p\n", sbrk(0)); 55 | 56 | printf("Allocating %d*%d bytes\n", numAllocs, blockSize); 57 | for (j = 0; j < numAllocs; j++) { 58 | ptr[j] = malloc(blockSize); 59 | if (ptr[j] == NULL) 60 | errExit("malloc"); 61 | printf("%10p\n", sbrk(0)); 62 | } 63 | 64 | printf("Program break is now: %10p\n", sbrk(0)); 65 | 66 | printf("Freeing blocks from %d to %d in steps of %d\n", 67 | freeMin, freeMax, freeStep); 68 | for (j = freeMin - 1; j < freeMax; j += freeStep) 69 | free(ptr[j]); 70 | 71 | printf("After free(), program break is: %10p\n", sbrk(0)); 72 | 73 | exit(EXIT_SUCCESS); 74 | } 75 | -------------------------------------------------------------------------------- /getcwd_clone.c: -------------------------------------------------------------------------------- 1 | /* Exercise 18-5: implement getcwd using a hacky workaround */ 2 | 3 | /* issue: doesn't work across file system boundaries */ 4 | 5 | #define _BSD_SOURCE 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "tlpi_hdr.h" 12 | 13 | char *my_getcwd (char *); 14 | 15 | char *my_getcwd (char *buf) { 16 | struct stat stat_buf; 17 | char tmp_buf[PATH_MAX]; 18 | char curr_dir[PATH_MAX] = "."; 19 | 20 | buf[0] = '\0'; 21 | while (1) { 22 | if (stat(curr_dir, &stat_buf) == -1) { 23 | return NULL; 24 | } 25 | 26 | strncat(curr_dir, "/..", PATH_MAX); 27 | DIR *parent = opendir(curr_dir); 28 | if (parent == NULL) { return NULL; } 29 | struct dirent *entry; 30 | while ((entry = readdir(parent)) != NULL) { 31 | if (entry->d_ino == stat_buf.st_ino) { 32 | break; 33 | } 34 | } 35 | if (entry == NULL) { return NULL; } 36 | 37 | if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { 38 | break; 39 | } 40 | 41 | strncpy(tmp_buf, buf, PATH_MAX); 42 | snprintf(buf, PATH_MAX, "/%s%s", entry->d_name, tmp_buf); 43 | } 44 | 45 | return buf; 46 | } 47 | 48 | int main (int argc, char *argv[]) { 49 | char pwd[PATH_MAX]; 50 | if (my_getcwd(pwd) == NULL) { 51 | exit(EXIT_FAILURE); 52 | } 53 | printf("%s", pwd); 54 | exit(EXIT_SUCCESS); 55 | } -------------------------------------------------------------------------------- /getcwd_clone_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | function assertEqual { 4 | if [ "$1" != "$2" ]; then 5 | echo "Expected \"$1\" to be \"$2\"" 6 | exit 1 7 | fi 8 | } 9 | 10 | CURR_DIR=$(pwd) 11 | 12 | function my_pwd { 13 | $CURR_DIR/getcwd_clone $@ 14 | } 15 | 16 | cd /usr/bin 17 | assertEqual "$(my_pwd)" "/usr/bin" 18 | cd /usr/share 19 | assertEqual "$(my_pwd)" "/usr/share" 20 | cd /etc 21 | assertEqual "$(my_pwd)" "/etc" 22 | -------------------------------------------------------------------------------- /getpwnam_clone.c: -------------------------------------------------------------------------------- 1 | /* Exercise 8-2: implement getpwnam */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include "tlpi_hdr.h" 7 | 8 | #define MAX_PASSWD_ENTRY_LEN 100 9 | 10 | struct passwd pwnam; 11 | char pwnam_name[MAX_PASSWD_ENTRY_LEN]; 12 | char pwnam_passwd[MAX_PASSWD_ENTRY_LEN]; 13 | char pwnam_gecos[MAX_PASSWD_ENTRY_LEN]; 14 | char pwnam_dir[MAX_PASSWD_ENTRY_LEN]; 15 | char pwnam_shell[MAX_PASSWD_ENTRY_LEN]; 16 | 17 | struct passwd *my_getpwnam(const char *); 18 | 19 | struct passwd *my_getpwnam(const char *name) { 20 | if (name == NULL || *name == '\0') { return NULL; } 21 | 22 | struct passwd *pw; 23 | while ((pw = getpwent()) != NULL) { 24 | if (strcmp(pw->pw_name, name) == 0) { 25 | setpwent(); 26 | pwnam = *pw; 27 | strncpy(pwnam_name, pw->pw_name, MAX_PASSWD_ENTRY_LEN); 28 | pwnam.pw_name = pwnam_name; 29 | strncpy(pwnam_passwd, pw->pw_passwd, MAX_PASSWD_ENTRY_LEN); 30 | pwnam.pw_passwd = pwnam_passwd; 31 | strncpy(pwnam_gecos, pw->pw_gecos, MAX_PASSWD_ENTRY_LEN); 32 | pwnam.pw_gecos = pwnam_gecos; 33 | strncpy(pwnam_dir, pw->pw_dir, MAX_PASSWD_ENTRY_LEN); 34 | pwnam.pw_dir = pwnam_dir; 35 | strncpy(pwnam_shell, pw->pw_shell, MAX_PASSWD_ENTRY_LEN); 36 | pwnam.pw_shell = pwnam_shell; 37 | return &pwnam; 38 | } 39 | } 40 | endpwent(); 41 | return NULL; 42 | } 43 | 44 | int main (int argc, char *argv[]) { 45 | if (argc != 2) { 46 | usageErr("%s login-name\n", argv[0]); 47 | } 48 | 49 | errno = 0; 50 | struct passwd *pw = my_getpwnam(argv[1]); 51 | if (pw == NULL) { 52 | if (errno != 0) { 53 | errExit("my_getpwnam\n"); 54 | } else { 55 | cmdLineErr("user %s doesn't exist\n", argv[1]); 56 | } 57 | } 58 | 59 | printf("Login name: %s\n", pw->pw_name); 60 | printf("Encrypted password: %s\n", pw->pw_passwd); 61 | printf("User ID: %ld\n", (long) pw->pw_uid); 62 | printf("Group ID: %ld\n", (long) pw->pw_gid); 63 | printf("Comment: %s\n", pw->pw_gecos); 64 | printf("Working directory: %s\n", pw->pw_dir); 65 | printf("Login shell: %s\n", pw->pw_shell); 66 | 67 | exit(EXIT_SUCCESS); 68 | } -------------------------------------------------------------------------------- /initgroups_clone.c: -------------------------------------------------------------------------------- 1 | /* Exercise 9-4: implement initgroups */ 2 | 3 | #define _BSD_SOURCE 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "tlpi_hdr.h" 10 | 11 | int my_initgroups(const char *, gid_t); 12 | 13 | int my_initgroups(const char *user, gid_t group) { 14 | size_t i = 0; 15 | gid_t groups[NGROUPS_MAX + 1]; 16 | 17 | struct group *gr; 18 | while ((gr = getgrent()) != NULL) { 19 | char **members = gr->gr_mem; 20 | while (*members != NULL) { 21 | if (strcmp(user, *members) == 0) { 22 | groups[i++] = gr->gr_gid; 23 | break; 24 | } 25 | members++; 26 | } 27 | } 28 | endgrent(); 29 | 30 | groups[i++] = group; 31 | 32 | return setgroups(i, groups); 33 | } 34 | 35 | int main (int argc, char *argv[]) { 36 | if (my_initgroups("tim", 42) == -1) { 37 | errExit("my_initgroups"); 38 | } 39 | 40 | gid_t grouplist[NGROUPS_MAX + 1]; 41 | int groups = getgroups(NGROUPS_MAX + 1, grouplist); 42 | if (groups == -1) { 43 | errExit("getgroups"); 44 | } 45 | 46 | printf("Groups: "); 47 | int first = 1; 48 | for (int i = 0; i < groups; i++) { 49 | if (!first) { printf(", "); } 50 | first = 0; 51 | printf("%ld", (long) grouplist[i]); 52 | } 53 | printf("\n"); 54 | 55 | exit(EXIT_SUCCESS); 56 | } -------------------------------------------------------------------------------- /inotify_recursive.c: -------------------------------------------------------------------------------- 1 | /* Exercise 19-1 */ 2 | 3 | #define _BSD_SOURCE 4 | 5 | #include 6 | #include 7 | #include 8 | #include "tlpi_hdr.h" 9 | 10 | #define MAX_WATCH_DESCRIPTORS 8192 11 | #define BUF_LEN (10 * (sizeof(struct inotify_event) + NAME_MAX + 1)) 12 | 13 | /* used to maintain a mapping between watch descriptors and filenames */ 14 | char *filenames[MAX_WATCH_DESCRIPTORS]; 15 | 16 | /* holds the file descriptor returned by inotify_init */ 17 | int infd; 18 | 19 | void usage (char *); 20 | void add_dir (char *); 21 | int add_tree_walker (const char *, const struct stat *, int, struct FTW *); 22 | void remove_dir (char *); 23 | int remove_tree_walker (const char *, const struct stat *, int, struct FTW *); 24 | void free_watch_descriptor(int); 25 | void handle_event (struct inotify_event *); 26 | 27 | void usage (char *program_name) { 28 | usageErr("%s directory...\n", program_name); 29 | } 30 | 31 | void add_dir (char *path) { 32 | /* 33 | Traverse the directory bottom-up ignoring symbolical links. 34 | We do this bottom-up because otherwise we would first call 35 | inotify_add_watcher on the parent directory, then ntwf would walk the child 36 | and thereby trigger the first inotify events on the parent directory. 37 | */ 38 | if (nftw(path, add_tree_walker, 10, FTW_PHYS | FTW_DEPTH) == -1) { 39 | fprintf(stderr, "nftw failed on %s\n", path); 40 | } 41 | } 42 | 43 | int add_tree_walker (const char *fpath, const struct stat *sb, 44 | int typeflags, struct FTW *tfwbuf) { 45 | if (typeflags & FTW_D) { 46 | int wd = inotify_add_watch(infd, fpath, IN_ALL_EVENTS | IN_ONLYDIR); 47 | if (wd == -1) { 48 | fprintf(stderr, "inotify_add_watch failed for %s\n", fpath); 49 | } 50 | char *path = (char *) malloc(strlen(fpath) + 1); 51 | strcpy(path, fpath); 52 | filenames[wd] = path; 53 | } 54 | return 0; 55 | } 56 | 57 | void remove_dir (char *oldpath) { 58 | for (int i = 0; i < MAX_WATCH_DESCRIPTORS; i++) { 59 | size_t oldlen = strlen(oldpath); 60 | if (filenames[i] != NULL && strncmp(oldpath, filenames[i], oldlen) == 0 && 61 | (filenames[i][oldlen] == '\0' || filenames[i][oldlen] == '/')) { 62 | free_watch_descriptor(i); 63 | } 64 | } 65 | } 66 | 67 | void free_watch_descriptor (int wd) { 68 | if (inotify_rm_watch(infd, wd) == -1) { 69 | fprintf(stderr, "inotify_rm_watch failed\n"); 70 | } 71 | if (filenames[wd] != NULL) { 72 | free(filenames[wd]); 73 | filenames[wd] = NULL; 74 | } 75 | } 76 | 77 | void handle_event (struct inotify_event *ine) { 78 | uint32_t mask = ine->mask; 79 | char *dir = filenames[ine->wd]; 80 | char full_name[NAME_MAX]; 81 | snprintf(full_name, NAME_MAX, "%s/%s", dir, ine->name); 82 | 83 | if (mask & IN_ACCESS) { 84 | printf("'%s' was accessed\n", full_name); 85 | } 86 | if (mask & IN_ATTRIB) { 87 | printf("metadata changed on '%s'\n", full_name); 88 | } 89 | if (mask & (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)) { 90 | printf("'%s' was closed\n", full_name); 91 | } 92 | if (mask & IN_CREATE) { 93 | printf("'%s' was created\n", full_name); 94 | } 95 | if (mask & IN_MOVED_TO) { 96 | printf("a file/directory was moved to '%s'\n", full_name); 97 | } 98 | if (mask & (IN_CREATE | IN_MOVED_TO)) { 99 | struct stat stat_buf; 100 | if (stat(full_name, &stat_buf) == -1) { 101 | fprintf(stderr, "stat failed\n"); 102 | } else if (S_ISDIR(stat_buf.st_mode)) { 103 | add_dir(full_name); 104 | } 105 | } 106 | if (mask & IN_DELETE) { 107 | printf("'%s' was deleted\n", full_name); 108 | } 109 | if (mask & IN_DELETE_SELF) { 110 | printf("directory '%s' was removed\n", dir); 111 | free_watch_descriptor(ine->wd); 112 | } 113 | if (mask & IN_MODIFY) { 114 | printf("'%s' was modified\n", full_name); 115 | } 116 | if (mask & IN_MOVE_SELF) {} 117 | if (mask & IN_MOVED_FROM) { 118 | printf("'%s' was moved out of parent directory\n", full_name); 119 | remove_dir(full_name); 120 | } 121 | if (mask & IN_OPEN) { 122 | printf("'%s' was opened\n", full_name); 123 | } 124 | if (mask & IN_Q_OVERFLOW) { 125 | fprintf(stderr, "inotify queue overflow\n"); 126 | } 127 | if (mask & IN_UNMOUNT) { 128 | printf("file system containing '%s' was unmounted\n", dir); 129 | } 130 | } 131 | 132 | int main (int argc, char *argv[]) { 133 | char buf[BUF_LEN]; 134 | 135 | if (argc < 2 || strcmp(argv[1], "--help") == 0) { 136 | usage(argv[0]); 137 | } 138 | 139 | infd = inotify_init1(0); 140 | if (infd == -1) { errExit("inotify_init\n"); } 141 | 142 | /* reset filenames to be an array of NULL pointers */ 143 | memset((void *) filenames, 0, sizeof(char *)*MAX_WATCH_DESCRIPTORS); 144 | 145 | for (int i = 1; i < argc; i++) { 146 | add_dir(argv[i]); 147 | } 148 | 149 | while (1) { 150 | ssize_t bytesRead = read(infd, buf, BUF_LEN); 151 | if (bytesRead <= 0) { 152 | errExit("read inotify descriptor\n"); 153 | } 154 | for (char *p = buf; p < buf + bytesRead;) { 155 | struct inotify_event *ine = (struct inotify_event *) p; 156 | handle_event(ine); 157 | p += sizeof(struct inotify_event) + ine->len; 158 | } 159 | } 160 | 161 | exit(EXIT_SUCCESS); 162 | } -------------------------------------------------------------------------------- /inotify_recursive_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | EXERCISES_DIR="$(pwd)" 4 | 5 | function echoDo { 6 | echo "\$" $@ 1>&2 7 | $@ 8 | } 9 | 10 | # we redirect stderr to stdout because ordering is important 11 | # to comprehend the output of this script 12 | 13 | echo "\$ cd /tmp" 1>&2 14 | cd /tmp 15 | echo "\$ [ -d dolores ] && rm -R dolores" 1>&2 16 | [ -d dolores ] && rm -R dolores 17 | echoDo mkdir dolores 18 | echoDo cd dolores 19 | echoDo mkdir adir 20 | echoDo mkdir bdir 21 | echo "\$ $EXERCISES_DIR/inotify_recursive adir bdir &" 1>&2 22 | $EXERCISES_DIR/inotify_recursive adir bdir 1>&2 & 23 | INOTIFY_RECURSIVE_PID=$! 24 | echo "\$ INOTIFY_RECURSIVE_PID=\$!" 1>&2 25 | sleep 1 26 | echoDo touch adir/lorem 27 | echoDo mkdir bdir/subdir 28 | echoDo touch bdir/subdir/file1 29 | echoDo rm adir/lorem 30 | echoDo mv bdir/subdir adir 31 | echo "\$ echo \"test\" > adir/subdir/file1" 1>&2 32 | echo "test" > adir/subdir/file1 33 | echoDo mv adir/subdir . 34 | echoDo touch subdir/file1 35 | echo "\$ kill \$INOTIFY_RECURSIVE_PID" 1>&2 36 | kill $INOTIFY_RECURSIVE_PID 37 | sleep 1 # make sure to kill child process -------------------------------------------------------------------------------- /large_file2.c: -------------------------------------------------------------------------------- 1 | /**********************************************************************\ 2 | * Copyright (C) Michael Kerrisk, 2010. * 3 | * * 4 | * This program is free software. You may use, modify, and redistribute * 5 | * it under the terms of the GNU Affero General Public License as * 6 | * published by the Free Software Foundation, either version 3 or (at * 7 | * your option) any later version. This program is distributed without * 8 | * any warranty. See the file COPYING for details. * 9 | \**********************************************************************/ 10 | 11 | /* large_file.c 12 | 13 | Modified by Tim Baumann to use _FILE_OFFSET_BITS 14 | instead of the (obsolete) Large File System API. 15 | 16 | (exercise 5-1) 17 | */ 18 | 19 | #define _FILE_OFFSET_BITS 64 20 | #include 21 | #include 22 | #include "tlpi_hdr.h" 23 | 24 | int 25 | main(int argc, char *argv[]) 26 | { 27 | int fd; 28 | off_t off; 29 | 30 | if (argc != 3 || strcmp(argv[1], "--help") == 0) 31 | usageErr("%s pathname offset\n", argv[0]); 32 | 33 | fd = open(argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); 34 | if (fd == -1) 35 | errExit("open"); 36 | 37 | off = atoll(argv[2]); 38 | if (lseek(fd, off, SEEK_SET) == -1) 39 | errExit("lseek"); 40 | 41 | if (write(fd, "test", 4) == -1) 42 | errExit("write"); 43 | exit(EXIT_SUCCESS); 44 | } 45 | -------------------------------------------------------------------------------- /list_processes_for_user.c: -------------------------------------------------------------------------------- 1 | /* Exercise 12-1 */ 2 | 3 | #define _BSD_SOURCE 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "tlpi_hdr.h" 11 | 12 | #define STATUS_FILE_SIZE 4000 13 | #define CMDLINE_SIZE 2048 14 | 15 | int isNumeric (char *); 16 | uid_t getUIDFromProcStatus(char *, size_t); 17 | 18 | int isNumeric (char *str) { 19 | while (*str != '\0') { 20 | if (!isdigit(*str)) { return 0; } 21 | str++; 22 | } 23 | return 1; 24 | } 25 | 26 | uid_t getUIDFromProcStatus (char *status, size_t n) { 27 | int sol = 1; /* start of line */ 28 | int i = 0; 29 | while (i < n) { 30 | if (sol && strncmp(&status[i], "Uid:", 4) == 0) { 31 | i += 4; 32 | while (isspace(status[i])) { i++; } 33 | return (uid_t) strtol(&status[i], NULL, 10); 34 | } 35 | sol = 0; 36 | if (status[i] == '\n') { sol = 1; } 37 | i++; 38 | } 39 | return -1; 40 | } 41 | 42 | int main (int argc, char *argv[]) { 43 | if (argc != 2 || strcmp(argv[1], "--help") == 0) { 44 | usageErr("%s login-name\n", argv[0]); 45 | } 46 | 47 | struct passwd *pw = getpwnam(argv[1]); 48 | if (pw == NULL) { 49 | cmdLineErr("%s is not a user", argv[1]); 50 | } 51 | uid_t uid = pw->pw_uid; 52 | 53 | DIR *proc = opendir("/proc"); 54 | if (proc == NULL) { 55 | errExit("opendir(\"/proc\")"); 56 | } 57 | 58 | struct dirent *dir; 59 | errno = 0; 60 | while ((dir = readdir(proc)) != NULL) { 61 | if (dir->d_type == DT_DIR && isNumeric(dir->d_name)) { 62 | char status_path[64]; 63 | snprintf(status_path, 64, "/proc/%s/status", dir->d_name); 64 | int status_file = open(status_path, O_RDONLY); 65 | if (status_file == -1) { 66 | errno = 0; 67 | continue; 68 | } 69 | 70 | char status[STATUS_FILE_SIZE]; 71 | ssize_t bytesRead = read(status_file, status, STATUS_FILE_SIZE); 72 | if (bytesRead == -1) { 73 | errno = 0; 74 | close(status_file); 75 | continue; 76 | } 77 | 78 | uid_t user = getUIDFromProcStatus(status, (size_t) bytesRead); 79 | if (user == -1) { 80 | fprintf(stderr, "couldn't find uid in %s\n", status_path); 81 | } 82 | 83 | close(status_file); 84 | 85 | if (user == uid) { 86 | printf("%s", dir->d_name); 87 | 88 | char cmdline_path[64]; 89 | snprintf(cmdline_path, 64, "/proc/%s/cmdline", dir->d_name); 90 | int cmdline_file = open(cmdline_path, O_RDONLY); 91 | if (cmdline_file == -1) { 92 | errno = 0; 93 | printf("\n"); 94 | continue; 95 | } 96 | char cmdline[CMDLINE_SIZE]; 97 | bytesRead = read(cmdline_file, cmdline, CMDLINE_SIZE); 98 | if (bytesRead != -1) { 99 | /* replace '\0's with spaces */ 100 | for (int i = 0; i < bytesRead-1; i++) { 101 | if (cmdline[i] == '\0') { 102 | cmdline[i] = ' '; 103 | } 104 | } 105 | printf(": %s", cmdline); 106 | } 107 | printf("\n"); 108 | errno = 0; 109 | } 110 | } 111 | errno = 0; 112 | } 113 | if (errno != 0) { 114 | errExit("readdir"); 115 | } 116 | } -------------------------------------------------------------------------------- /longjmp_error.c: -------------------------------------------------------------------------------- 1 | /* Exercise 6-2: deliberately broken longjmp */ 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | static jmp_buf jmp_env; 8 | 9 | void a (void); 10 | void b (void); 11 | 12 | void a (void) { 13 | if (setjmp(jmp_env) == 0) { 14 | printf("first time setjmp\n"); 15 | } else { 16 | printf("jumping with longjmp\n"); 17 | } 18 | } 19 | 20 | void b (void) { 21 | longjmp(jmp_env, 1); 22 | } 23 | 24 | int main (int argc, char *argv[]) { 25 | printf("before a()\n"); 26 | a(); 27 | printf("before b()\n"); 28 | b(); 29 | printf("after b()\n"); 30 | exit(EXIT_SUCCESS); 31 | } -------------------------------------------------------------------------------- /malloc_free_clone.c: -------------------------------------------------------------------------------- 1 | /* 2 | Exercise 7-2: implement malloc and free 3 | 4 | this is probably horrible c code. 5 | 6 | deficencies: 7 | 8 | * it doesn't increase the page break by huge sizes to avoid too many system calls 9 | * it doesn't lower the page break when memory gets freed 10 | * it requires space to store pointers to the last and next block even for non-free blocks 11 | * performance 12 | */ 13 | 14 | #define _SVID_SOURCE 15 | 16 | #include 17 | #include 18 | #include "tlpi_hdr.h" 19 | 20 | extern char end; 21 | 22 | void *my_malloc (size_t); 23 | void my_free(void *); 24 | 25 | struct blk { 26 | size_t size; 27 | struct blk *prev; 28 | struct blk *next; 29 | }; 30 | 31 | struct blk *first = NULL; 32 | struct blk *last = NULL; 33 | 34 | void *my_malloc (size_t size) { 35 | size_t required_size = size + sizeof(struct blk); 36 | 37 | struct blk *curr = first; 38 | while (curr != NULL && curr->size < required_size) { 39 | curr = curr->next; 40 | } 41 | 42 | if (curr == NULL) { 43 | void *new = sbrk((intptr_t) required_size); 44 | if (new == (void *) -1) { return NULL; } 45 | struct blk *new_blk = (struct blk *) new; 46 | new_blk->size = required_size; 47 | return (void *) (new_blk + 1); 48 | } 49 | 50 | if (curr == first) { first = first->next; } 51 | else { curr->prev->next = curr->next; } 52 | 53 | if (curr == last) { last = last->prev; } 54 | else { curr->next->prev = curr->prev; } 55 | 56 | if (curr->size > required_size + sizeof(struct blk)) { 57 | struct blk *left = (struct blk *) (((char *) curr) + required_size); 58 | left->size = curr->size - required_size; 59 | curr->size = required_size; 60 | my_free((char *) (left + 1)); 61 | } 62 | 63 | return (void *) (curr + 1); 64 | } 65 | 66 | void my_free (void *ptr) { 67 | struct blk *blk_ptr = ((struct blk *) ptr) - 1; 68 | 69 | if (first == NULL) { 70 | first = last = blk_ptr; 71 | return; 72 | } 73 | 74 | if (blk_ptr < first) { 75 | blk_ptr->prev = NULL; 76 | if (((char *) blk_ptr) + blk_ptr->size == (char *) first) { 77 | blk_ptr->size += first->size; 78 | blk_ptr->next = first->next; 79 | } else { 80 | first->prev = blk_ptr; 81 | blk_ptr->next = first; 82 | } 83 | first = blk_ptr; 84 | return; 85 | } 86 | 87 | if (blk_ptr > last) { 88 | if (((char *) last) + last->size == (char *) blk_ptr) { 89 | last->size += blk_ptr->size; 90 | } else { 91 | blk_ptr->next = NULL; 92 | blk_ptr->prev = last; 93 | last->next = blk_ptr; 94 | last = blk_ptr; 95 | } 96 | return; 97 | } 98 | 99 | struct blk *curr = first; 100 | while (curr < blk_ptr) { 101 | curr = curr->next; 102 | } 103 | 104 | struct blk *before = curr->prev; 105 | if (((char *) before) + before->size == (char *) blk_ptr) { 106 | before->size += blk_ptr->size; 107 | blk_ptr = before; 108 | } else { 109 | blk_ptr->prev = before; 110 | before->next = blk_ptr; 111 | } 112 | 113 | if (((char *) blk_ptr) + blk_ptr->size == (char *) curr) { 114 | blk_ptr->size += curr->size; 115 | blk_ptr->next = curr->next; 116 | curr->next->prev = blk_ptr; 117 | } else { 118 | blk_ptr->next = curr; 119 | curr->prev = blk_ptr; 120 | } 121 | } 122 | 123 | #define MAX_ALLOCS 1000000 124 | 125 | int main (int argc, char *argv[]) { 126 | /* copied from free_and_sbrk.c -- licensed by Michael Kerrisk under the GPLv3 */ 127 | 128 | char *ptr[MAX_ALLOCS]; 129 | int freeStep, freeMin, freeMax, blockSize, numAllocs, j; 130 | 131 | printf("\n"); 132 | 133 | if (argc < 3 || strcmp(argv[1], "--help") == 0) { 134 | usageErr("%s num-allocs block-size [step [min [max]]]\n", argv[0]); 135 | } 136 | 137 | numAllocs = getInt(argv[1], GN_GT_0, "num-allocs"); 138 | if (numAllocs > MAX_ALLOCS) { 139 | cmdLineErr("num-allocs > %d\n", MAX_ALLOCS); 140 | } 141 | 142 | blockSize = getInt(argv[2], GN_GT_0 | GN_ANY_BASE, "block-size"); 143 | 144 | freeStep = (argc > 3) ? getInt(argv[3], GN_GT_0, "step") : 1; 145 | freeMin = (argc > 4) ? getInt(argv[4], GN_GT_0, "min") : 1; 146 | freeMax = (argc > 5) ? getInt(argv[5], GN_GT_0, "max") : numAllocs; 147 | 148 | if (freeMax > numAllocs) { 149 | cmdLineErr("free-max > num-allocs\n"); 150 | } 151 | 152 | printf("Initial program break: %10p\n", sbrk(0)); 153 | 154 | printf("Allocating %d*%d bytes\n", numAllocs, blockSize); 155 | for (j = 0; j < numAllocs; j++) { 156 | ptr[j] = my_malloc(blockSize); 157 | if (ptr[j] == NULL) { 158 | errExit("malloc"); 159 | } 160 | printf("%10p\n", sbrk(0)); 161 | } 162 | 163 | printf("Program break is now: %10p\n", sbrk(0)); 164 | 165 | printf("Freeing blocks from %d to %d in steps of %d\n", 166 | freeMin, freeMax, freeStep); 167 | for (j = freeMin - 1; j < freeMax; j += freeStep) { 168 | my_free(ptr[j]); 169 | } 170 | 171 | printf("After my_free(), program break is: %10p\n", sbrk(0)); 172 | 173 | exit(EXIT_SUCCESS); 174 | } -------------------------------------------------------------------------------- /my_env.c: -------------------------------------------------------------------------------- 1 | /* Exercise 6-3 */ 2 | 3 | #include 4 | #include 5 | #include "tlpi_hdr.h" 6 | 7 | extern char **environ; 8 | 9 | int my_setenv (const char *, const char *, int); 10 | int my_unsetenv (const char *); 11 | 12 | int my_setenv (const char *name, const char *value, int overwrite) { 13 | if (name == NULL) { 14 | errno = EINVAL; 15 | return -1; 16 | } 17 | 18 | if (overwrite || getenv(name) == NULL) { 19 | size_t len = strlen(name) + 1 + strlen(value) + 1; 20 | char *env_entry = (char *) malloc(len); 21 | if (env_entry == NULL) { 22 | errno = ENOMEM; 23 | return -1; 24 | } 25 | strcpy(env_entry, name); 26 | env_entry[strlen(name)] = '='; 27 | strcpy(&env_entry[strlen(name) + 1], value); 28 | int r = putenv(env_entry); 29 | return r == 0 ? 0 : -1; 30 | } 31 | return 0; 32 | } 33 | 34 | int my_unsetenv (const char *name) { 35 | if (name == NULL) { 36 | errno = EINVAL; 37 | return -1; 38 | } 39 | 40 | size_t n = strlen(name); 41 | int i = 0, j = 0; 42 | while (environ[j] != NULL) { 43 | if (strncmp(environ[j], name, n) == 0 && environ[j][n] == '=') { 44 | j++; 45 | } else { 46 | if (j != i) { 47 | environ[i] = environ[j]; 48 | } 49 | i++; 50 | j++; 51 | } 52 | } 53 | environ[i] = NULL; 54 | return 0; 55 | } 56 | 57 | int main (int argc, char *argv[]) { 58 | char name[] = "GREET"; 59 | char value[] = "bonjour"; 60 | 61 | assert(getenv(name) == NULL); 62 | assert(my_setenv(NULL, value, 0) == -1); 63 | assert(my_setenv(name, value, 0) == 0); 64 | assert(strcmp(getenv(name), "bonjour") == 0); 65 | value[0] = 'B'; 66 | value[1] = 'O'; 67 | assert(strcmp(getenv(name), "bonjour") == 0); 68 | assert(my_setenv(name, "guten tag", 0) == 0); 69 | assert(strcmp(getenv(name), "bonjour") == 0); 70 | assert(my_setenv(name, "guten tag", 1) == 0); 71 | assert(strcmp(getenv(name), "guten tag") == 0); 72 | assert(my_unsetenv(NULL) == -1); 73 | assert(my_unsetenv(name) == 0); 74 | assert(my_setenv("lorem", "ipsum", 0) == 0); 75 | assert(getenv(name) == NULL); 76 | assert(strcmp(getenv("lorem"), "ipsum") == 0); 77 | 78 | exit(EXIT_SUCCESS); 79 | } -------------------------------------------------------------------------------- /nftw_statistics.c: -------------------------------------------------------------------------------- 1 | /* Exercise 18-7: walk a directory tree and gather various statistics */ 2 | 3 | #define _BSD_SOURCE /* file type test macros */ 4 | 5 | #include 6 | #include "tlpi_hdr.h" 7 | 8 | int walker (const char *, const struct stat *, int, struct FTW *); 9 | 10 | int max_depth = 0; 11 | int directories = 0; 12 | int unreadable_directories = 0; 13 | int normal_files = 0; 14 | int symbolic_links = 0; 15 | int broken_links = 0; 16 | int other_files = 0; 17 | int stat_failed = 0; 18 | 19 | int walker (const char *fpath, const struct stat *sb, 20 | int typeflag, struct FTW *ftwbuf) { 21 | max_depth = max(max_depth, ftwbuf->level); 22 | 23 | if (sb == NULL) { 24 | stat_failed++; 25 | return 0; 26 | } 27 | 28 | if (S_ISDIR(sb->st_mode)) { 29 | directories++; 30 | if (typeflag & FTW_DNR) { unreadable_directories++; } 31 | } else if (S_ISREG(sb->st_mode)) { 32 | normal_files++; 33 | } else if (S_ISLNK(sb->st_mode)) { 34 | symbolic_links++; 35 | if (typeflag & FTW_SL) { broken_links++; } 36 | } else { 37 | other_files++; 38 | } 39 | 40 | return 0; 41 | } 42 | 43 | int main (int argc, char *argv[]) { 44 | if (nftw(".", walker, 10, FTW_PHYS) != 0) { 45 | errExit("tree walk failed\n"); 46 | } 47 | printf("Maximum depth: %d\n", max_depth); 48 | printf("Directories: %d\n", directories); 49 | printf(" - unreadable: %d\n", unreadable_directories); 50 | printf("Regular files: %d\n", normal_files); 51 | printf("Symbolic links: %d\n", symbolic_links); 52 | printf(" - broken: %d\n", broken_links); 53 | printf("Other files: %d\n", other_files); 54 | printf("Stat failed: %d\n", stat_failed); 55 | exit(EXIT_SUCCESS); 56 | } -------------------------------------------------------------------------------- /processes_open_file.c: -------------------------------------------------------------------------------- 1 | /* Exercise 12-3: list processes that have a particular file opened (requires sudo) */ 2 | 3 | #define _BSD_SOURCE 4 | 5 | #include 6 | #include 7 | #include 8 | #include "tlpi_hdr.h" 9 | 10 | int isNumeric (char *); 11 | 12 | int isNumeric (char *str) { 13 | while (*str != '\0') { 14 | if (!isdigit(*str)) { return 0; } 15 | str++; 16 | } 17 | return 1; 18 | } 19 | 20 | int main (int argc, char *argv[]) { 21 | if (argc != 2 || strcmp(argv[1], "--help") == 0) { 22 | usageErr("%s file\n", argv[0]); 23 | } 24 | 25 | char *file = argv[1]; 26 | size_t file_len = strlen(file) + 1; 27 | char *other_file = (char *) malloc(file_len); 28 | if (other_file == NULL) { errExit("malloc\n"); } 29 | 30 | DIR *proc = opendir("/proc"); 31 | if (proc == NULL) { errExit("opendir(\"/proc\")\n"); } 32 | 33 | struct dirent *proc_ent; 34 | errno = 0; 35 | while ((proc_ent = readdir(proc)) != NULL) { 36 | if (!(proc_ent->d_type == DT_DIR && isNumeric(proc_ent->d_name))) { continue; } 37 | 38 | char fd_path[32]; 39 | snprintf(fd_path, 32, "/proc/%s/fd", proc_ent->d_name); 40 | DIR *fd_dir = opendir(fd_path); 41 | if (fd_dir == NULL) { errExit("opendir(\"%s\")\n", fd_path); } 42 | struct dirent *fd_ent; 43 | while ((fd_ent = readdir(fd_dir)) != NULL) { 44 | if (fd_ent->d_type != DT_LNK) { continue; } 45 | 46 | char full_fd_path[48]; 47 | snprintf(full_fd_path, 48, "%s/%s", fd_path, fd_ent->d_name); 48 | 49 | ssize_t bytesRead = readlink(full_fd_path, other_file, file_len); 50 | if (bytesRead == -1) { errExit("readlink(\"%s\")\n", full_fd_path); } 51 | if (bytesRead < file_len) { 52 | other_file[bytesRead] = '\0'; 53 | } 54 | if (strcmp(file, other_file) == 0) { 55 | /* print pid of process that has the file open */ 56 | printf("%s\n", proc_ent->d_name); 57 | } 58 | } 59 | } 60 | if (errno != 0) { /* TODO */ } 61 | 62 | exit(EXIT_SUCCESS); 63 | } -------------------------------------------------------------------------------- /pstree.c: -------------------------------------------------------------------------------- 1 | /* Exercise 12-2 */ 2 | 3 | #define _BSD_SOURCE 4 | 5 | #include 6 | #include 7 | #include 8 | #include "tlpi_hdr.h" 9 | 10 | #define STATUS_FILE_SIZE 4000 11 | #define CMDLINE_SIZE 1024 12 | 13 | int isNumeric (char *); 14 | pid_t getMaxPid (void); 15 | pid_t getPPidFromProcStatus (char *, size_t); 16 | void gatherProcessInfo (pid_t); 17 | void printTree (int, pid_t); 18 | 19 | struct p_info { 20 | pid_t p_ppid; 21 | char p_cmdline[CMDLINE_SIZE]; 22 | } **processes_by_pid; 23 | 24 | pid_t *processes; 25 | size_t n_processes = 0; 26 | 27 | int isNumeric (char *str) { 28 | while (*str != '\0') { 29 | if (!isdigit(*str)) { return 0; } 30 | str++; 31 | } 32 | return 1; 33 | } 34 | 35 | pid_t getMaxPid (void) { 36 | int fd = open("/proc/sys/kernel/pid_max", O_RDONLY); 37 | if (fd == -1) { errExit("open(\"/proc/sys/kernel/pid_max\")\n"); } 38 | #define PID_STR_LEN 16 39 | char pid_max_str[PID_STR_LEN]; 40 | ssize_t bytesRead = read(fd, pid_max_str, PID_STR_LEN); 41 | if (bytesRead == -1) { 42 | errExit("reading /proc/sys/kernel/pid_max"); 43 | } 44 | return (pid_t) strtol(pid_max_str, NULL, 10); 45 | } 46 | 47 | pid_t getPPidFromProcStatus (char *status, size_t n) { 48 | int sol = 1; /* start of line */ 49 | int i = 0; 50 | while (i < n) { 51 | if (sol && strncmp(&status[i], "PPid:", 5) == 0) { 52 | i += 5; 53 | while (isspace(status[i])) { i++; } 54 | return (uid_t) strtol(&status[i], NULL, 10); 55 | } 56 | sol = 0; 57 | if (status[i] == '\n') { sol = 1; } 58 | i++; 59 | } 60 | return -1; 61 | } 62 | 63 | void gatherProcessInfo (pid_t max_pid) { 64 | DIR *proc = opendir("/proc"); 65 | if (proc == NULL) { errExit("opendir(\"/proc\")\n"); } 66 | 67 | struct dirent *dir; 68 | errno = 0; 69 | while ((dir = readdir(proc)) != NULL) { 70 | if (!(dir->d_type == DT_DIR && isNumeric(dir->d_name))) { continue; } 71 | 72 | pid_t pid = (pid_t) strtol(dir->d_name, NULL, 10); 73 | if (pid > max_pid) { 74 | /* max_pid got increased during the running time of this program and 75 | there's a process that has a higher pid than the former limit */ 76 | continue; 77 | } 78 | 79 | char status_path[64]; 80 | snprintf(status_path, 64, "/proc/%s/status", dir->d_name); 81 | int status_file = open(status_path, O_RDONLY); 82 | if (status_file == -1) { 83 | errno = 0; 84 | continue; 85 | } 86 | 87 | char status[STATUS_FILE_SIZE]; 88 | ssize_t bytesRead = read(status_file, status, STATUS_FILE_SIZE); 89 | close(status_file); 90 | if (bytesRead == -1) { 91 | errno = 0; 92 | continue; 93 | } 94 | 95 | struct p_info *pi = (struct p_info *) malloc(sizeof(struct p_info)); 96 | if (pi == NULL) { errExit("malloc"); } 97 | pi->p_ppid = getPPidFromProcStatus(status, (size_t) bytesRead); 98 | 99 | char cmdline_path[64]; 100 | snprintf(cmdline_path, 64, "/proc/%s/cmdline", dir->d_name); 101 | int cmdline_file = open(cmdline_path, O_RDONLY); 102 | if (cmdline_file == -1) { 103 | errno = 0; 104 | continue; 105 | } 106 | 107 | char *cmdline = pi->p_cmdline; 108 | bytesRead = read(cmdline_file, cmdline, CMDLINE_SIZE); 109 | close(cmdline_file); 110 | if (bytesRead == -1) { 111 | errno = 0; 112 | continue; 113 | } 114 | 115 | /* replace '\0's with spaces in cmdline */ 116 | for (int j = 0; j < bytesRead-1; j++) { 117 | if (cmdline[j] == '\0') { 118 | cmdline[j] = ' '; 119 | } 120 | } 121 | 122 | processes_by_pid[pid] = pi; 123 | processes[n_processes++] = pid; 124 | } 125 | /*if (errno != 0) {}*/ 126 | } 127 | 128 | void printTree (int level, pid_t pid) { 129 | struct p_info *pi = processes_by_pid[pid]; 130 | if (pi == NULL) { return; } 131 | for (int i = 0; i < level; i++) { printf(" "); } 132 | printf("%ld %.80s\n", (long) pid, pi->p_cmdline); 133 | for (size_t i = 0; i < n_processes; i++) { 134 | struct p_info *other = processes_by_pid[processes[i]]; 135 | if (other->p_ppid == pid) { 136 | /* is a child of current process */ 137 | printTree(level + 1, processes[i]); 138 | } 139 | } 140 | //printf("%ld", (long) ) 141 | } 142 | 143 | int main (int argc, char *argv[]) { 144 | pid_t max_pid = getMaxPid(); 145 | processes_by_pid = (struct p_info **) calloc((size_t) max_pid + 1, sizeof(struct p_info *)); 146 | if (processes_by_pid == NULL) { errExit("calloc\n"); } 147 | processes = (pid_t *) malloc(sizeof(pid_t *) * (max_pid + 1)); 148 | if (processes == NULL) { errExit("malloc\n"); } 149 | gatherProcessInfo(max_pid); 150 | printTree(0, 1); 151 | 152 | exit(EXIT_SUCCESS); 153 | } -------------------------------------------------------------------------------- /ptmr_sigev_signal_sigwaitinfo.c: -------------------------------------------------------------------------------- 1 | /* Exercise 23-4: Modify the program in Listing 23-5 (ptmr_sigev_signal.c) to use sigwaitinfo() instead 2 | of a signal handler. 3 | */ 4 | 5 | /**********************************************************************\ 6 | * Copyright (C) Michael Kerrisk, 2010. * 7 | * * 8 | * This program is free software. You may use, modify, and redistribute * 9 | * it under the terms of the GNU Affero General Public License as * 10 | * published by the Free Software Foundation, either version 3 or (at * 11 | * your option) any later version. This program is distributed without * 12 | * any warranty. See the file COPYING for details. * 13 | \**********************************************************************/ 14 | 15 | /* ptmr_sigev_signal.c 16 | 17 | This program demonstrates the use of signals as the notification mechanism 18 | for expirations of a POSIX timer. Each of the program's command-line 19 | arguments specifies the initial value and interval for a POSIX timer. The 20 | format of these arguments is defined by the function itimerspecFromStr(). 21 | 22 | The program establishes a handler for the timer notification signal, creates 23 | and arms one timer for each command-line argument, and then pauses. Each 24 | timer expiration causes the generation of a signal, and, when invoked, the 25 | signal handler displays information about the timer expiration. 26 | 27 | Kernel support for Linux timers is provided since Linux 2.6. On older 28 | systems, an incomplete user-space implementation of POSIX timers 29 | was provided in glibc. 30 | */ 31 | #define _POSIX_C_SOURCE 199309 32 | #include 33 | #include 34 | #include "curr_time.h" /* Declares currTime() */ 35 | #include "itimerspec_from_str.h" /* Declares itimerspecFromStr() */ 36 | #include "tlpi_hdr.h" 37 | 38 | #define TIMER_SIG SIGUSR1 /* Our timer notification signal */ 39 | 40 | int 41 | main(int argc, char *argv[]) 42 | { 43 | struct itimerspec ts; 44 | struct sigevent sev; 45 | timer_t *tidlist; 46 | int j; 47 | sigset_t timer_set, blocked_set; 48 | siginfo_t si; 49 | 50 | if (argc < 2) 51 | usageErr("%s secs[/nsecs][:int-secs[/int-nsecs]]...\n", argv[0]); 52 | 53 | tidlist = calloc(argc - 1, sizeof(timer_t)); 54 | if (tidlist == NULL) 55 | errExit("malloc"); 56 | 57 | /* Create and start one timer for each command-line argument */ 58 | 59 | sev.sigev_notify = SIGEV_SIGNAL; /* Notify via signal */ 60 | sev.sigev_signo = TIMER_SIG; /* Notify using this signal */ 61 | 62 | for (j = 0; j < argc - 1; j++) { 63 | itimerspecFromStr(argv[j + 1], &ts); 64 | 65 | sev.sigev_value.sival_ptr = &tidlist[j]; 66 | /* Allows handler to get ID of this timer */ 67 | 68 | if (timer_create(CLOCK_REALTIME, &sev, &tidlist[j]) == -1) 69 | errExit("timer_create"); 70 | printf("Timer ID: %ld (%s)\n", (long) tidlist[j], argv[j + 1]); 71 | 72 | if (timer_settime(tidlist[j], 0, &ts, NULL) == -1) 73 | errExit("timer_settime"); 74 | } 75 | 76 | sigemptyset(&timer_set); 77 | sigaddset(&timer_set, TIMER_SIG); 78 | 79 | sigprocmask(SIG_BLOCK, &timer_set, NULL); 80 | 81 | for (;;) { 82 | if (sigwaitinfo(&timer_set, &si) == -1) { 83 | errExit("sigwaitinfo\n"); 84 | } 85 | timer_t *tidptr; 86 | 87 | tidptr = si.si_value.sival_ptr; 88 | 89 | printf("[%s] Got signal %d\n", currTime("%T"), si.si_signo); 90 | printf(" *sival_ptr = %ld\n", (long) *tidptr); 91 | printf(" timer_getoverrun() = %d\n", timer_getoverrun(*tidptr)); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /readv_writev_clone.c: -------------------------------------------------------------------------------- 1 | /* Exercise 5-7 */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "tlpi_hdr.h" 8 | 9 | size_t count_iovec (const struct iovec *, int); 10 | ssize_t my_readv (int, const struct iovec *, int); 11 | ssize_t my_writev (int, const struct iovec *, int); 12 | 13 | size_t count_iovec (const struct iovec *iov, int iovcnt) { 14 | size_t count = 0; 15 | for (int i = 0; i < iovcnt; i++) { 16 | count += iov[i].iov_len; 17 | } 18 | return count; 19 | } 20 | 21 | ssize_t my_readv (int fd, const struct iovec *iov, int iovcnt) { 22 | size_t count = count_iovec(iov, iovcnt); 23 | 24 | char *buf = (char *) malloc(count); 25 | ssize_t bytesRead = read(fd, buf, count); 26 | if (bytesRead == -1) { 27 | free((void *) buf); 28 | return -1; 29 | } 30 | 31 | size_t index = 0; 32 | for (int i = 0; i < iovcnt && index < bytesRead; i++) { 33 | size_t iovlen = min(iov[i].iov_len, bytesRead - index); 34 | memcpy(iov[i].iov_base, &buf[index], iovlen); 35 | index += iovlen; 36 | } 37 | 38 | free((void *) buf); 39 | return bytesRead; 40 | } 41 | 42 | ssize_t my_writev(int fd, const struct iovec *iov, int iovcnt) { 43 | size_t count = count_iovec(iov, iovcnt); 44 | 45 | char *buf = (char *) malloc(count); 46 | 47 | size_t index = 0; 48 | for (int i = 0; i < iovcnt; i++) { 49 | memcpy(&buf[index], iov[i].iov_base, iov[i].iov_len); 50 | index += iov[i].iov_len; 51 | } 52 | 53 | ssize_t bytesWritten = write(fd, buf, count); 54 | free(buf); 55 | return bytesWritten; 56 | } 57 | 58 | int main (int argc, char *argv[]) { 59 | if (argc != 2) { 60 | usageErr("%s testfile\n", argv[0]); 61 | } 62 | 63 | int fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR); 64 | if (fd == -1) { errExit("open testfile"); } 65 | 66 | struct iovec text[3]; 67 | char *lorem = "lorem\n"; 68 | text[0].iov_len = 6; 69 | text[0].iov_base = lorem; 70 | char *ipsum = "ipsum\n"; 71 | text[1].iov_len = 6; 72 | text[1].iov_base = ipsum; 73 | char *dolor = "dolor\n"; 74 | text[2].iov_len = 6; 75 | text[2].iov_base = dolor; 76 | 77 | char *total_text = "lorem\nipsum\ndolor\n"; 78 | 79 | ssize_t bytesWritten = my_writev(fd, text, 3); 80 | if (bytesWritten == -1) { errExit("my_writev"); } 81 | if (lseek(fd, 0, SEEK_SET) == -1) { errExit("lseek"); } 82 | char buf[18]; 83 | if (read(fd, buf, 18) != 18) { errExit("read"); } 84 | assert(memcmp(buf, total_text, 18) == 0); 85 | 86 | struct iovec read_vec[4]; 87 | char a[2]; 88 | read_vec[0].iov_len = 2; 89 | read_vec[0].iov_base = a; 90 | char b[3]; 91 | read_vec[1].iov_len = 3; 92 | read_vec[1].iov_base = b; 93 | char c[5]; 94 | read_vec[2].iov_len = 5; 95 | read_vec[2].iov_base = c; 96 | char d[8]; 97 | read_vec[3].iov_len = 8; 98 | read_vec[3].iov_base = d; 99 | 100 | if (lseek(fd, 0, SEEK_SET) == -1) { errExit("lseek"); } 101 | if (my_readv(fd, read_vec, 4) != 18) { errExit("my_readv"); } 102 | assert(memcmp(a, "lo", 2) == 0); 103 | assert(memcmp(b, "rem", 3) == 0); 104 | assert(memcmp(c, "\nipsu", 5) == 0); 105 | assert(memcmp(d, "m\ndolor\n", 8) == 0); 106 | 107 | exit(EXIT_SUCCESS); 108 | } -------------------------------------------------------------------------------- /realpath_clone.c: -------------------------------------------------------------------------------- 1 | /* Exercise 18-3: implement realpath */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "tlpi_hdr.h" 9 | 10 | char *my_realpath_relative (const char *, char *); 11 | char *my_realpath(const char *, char *); 12 | 13 | char *my_realpath_relative (const char *path, char *relative_path) { 14 | if (path[0] == '/') { 15 | relative_path[0] = '/'; 16 | relative_path[1] = '\0'; 17 | } 18 | 19 | int i = 0, j = 0; 20 | while (path[i] != '\0') { 21 | while (path[i] == '/') { i++; } 22 | j = i; 23 | while (path[j] != '/' && path[j] != '\0') { 24 | j++; 25 | } 26 | if (j > i) { 27 | if (j - i == 1 && path[i] == '.') { 28 | /* do nothing */ 29 | } else if (j - i == 2 && path[i] == '.' && path[i+1] == '.') { 30 | /* go one directory up */ 31 | char *dirn = dirname(relative_path); 32 | strncpy(relative_path, dirn, PATH_MAX); 33 | } else { 34 | if (relative_path[strlen(relative_path)-1] != '/') { 35 | strncat(relative_path, "/", PATH_MAX); 36 | } 37 | size_t relative_path_len = strlen(relative_path); 38 | strncat(relative_path, &path[i], j-i+1); 39 | struct stat stat_buf; 40 | if (lstat(relative_path, &stat_buf) == -1) { return NULL; } 41 | if (S_ISLNK(stat_buf.st_mode)) { 42 | char buf[PATH_MAX]; 43 | ssize_t len = readlink(relative_path, buf, PATH_MAX); 44 | if (len == -1) { return NULL; } 45 | relative_path[relative_path_len] = '\0'; 46 | if (my_realpath_relative(buf, relative_path) == NULL) { 47 | return NULL; 48 | } 49 | } 50 | } 51 | } 52 | i = j; 53 | } 54 | 55 | return relative_path; 56 | } 57 | 58 | char *my_realpath(const char *path, char *resolved_path) { 59 | if (path == NULL) { 60 | errno = EINVAL; 61 | return NULL; 62 | } 63 | 64 | if (resolved_path == NULL) { 65 | resolved_path = (char *) malloc(PATH_MAX); 66 | } 67 | 68 | if (path[0] != '/') { 69 | if (getcwd(resolved_path, PATH_MAX) == NULL) { 70 | return NULL; 71 | } 72 | strncat(resolved_path, "/", PATH_MAX); 73 | } 74 | 75 | return my_realpath_relative(path, resolved_path); 76 | } 77 | 78 | int main (int argc, char *argv[]) { 79 | if (argc != 2) { 80 | usageErr("%s path", argv[0]); 81 | } 82 | char *path = argv[1]; 83 | char resolved_path[PATH_MAX]; 84 | if (my_realpath(path, resolved_path) == resolved_path) { 85 | printf("%s", resolved_path); 86 | } else { 87 | errExit("my_realpath\n"); 88 | } 89 | exit(EXIT_SUCCESS); 90 | } -------------------------------------------------------------------------------- /realpath_clone_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | function assertEqual { 4 | if [ "$1" != "$2" ]; then 5 | echo "Expected \"$1\" to be \"$2\"" 6 | exit 1 7 | fi 8 | } 9 | 10 | function assertFail { 11 | $@ >/dev/null 2>/dev/null 12 | if [ $? == "0" ]; then 13 | echo "Expected \"$@\" to fail" 14 | exit 1 15 | fi 16 | } 17 | 18 | CURR_DIR=$(pwd) 19 | 20 | function my_realpath { 21 | $CURR_DIR/realpath_clone $@ 22 | } 23 | 24 | cd /tmp 25 | [ -d lorem_ipsum ] && rm -R lorem_ipsum 26 | mkdir lorem_ipsum 27 | cd lorem_ipsum 28 | touch a 29 | ln -s a b 30 | ln -s ../lorem_ipsum/a c 31 | ln -s /tmp/lorem_ipsum/c d 32 | 33 | assertEqual "$(my_realpath a)" "/tmp/lorem_ipsum/a" 34 | assertEqual "$(my_realpath b)" "/tmp/lorem_ipsum/a" 35 | assertEqual "$(my_realpath c)" "/tmp/lorem_ipsum/a" 36 | assertEqual "$(my_realpath d)" "/tmp/lorem_ipsum/a" 37 | assertEqual "$(my_realpath /tmp/lorem_ipsum/a)" "/tmp/lorem_ipsum/a" 38 | assertEqual "$(my_realpath ./a)" "/tmp/lorem_ipsum/a" 39 | assertEqual "$(my_realpath ././a)" "/tmp/lorem_ipsum/a" 40 | assertEqual "$(my_realpath ./../lorem_ipsum/a)" "/tmp/lorem_ipsum/a" 41 | assertEqual "$(my_realpath ../../../../../../../tmp/lorem_ipsum/a)" "/tmp/lorem_ipsum/a" 42 | assertFail my_realpath nosuchfile 43 | -------------------------------------------------------------------------------- /realtime_plus_standard_signals.c: -------------------------------------------------------------------------------- 1 | /* Exercise 22-2 */ 2 | 3 | #define _GNU_SOURCE 4 | 5 | #include "tlpi_hdr.h" 6 | #include 7 | #include 8 | 9 | void handler (int, siginfo_t *, void *); 10 | 11 | static volatile int gotSigint = 0; 12 | 13 | void handler (int signal, siginfo_t *si, void *ctx) { 14 | if (signal == SIGINT) { 15 | gotSigint = 1; 16 | return; 17 | } 18 | /* UNSAFE */ 19 | printf("caught %d (%s)\n", signal, strsignal(signal)); 20 | } 21 | 22 | int main (int argc, char *argv[]) { 23 | printf("%s: pid is %ld\n", argv[0], (long) getpid()); 24 | 25 | struct sigaction action; 26 | sigfillset(&action.sa_mask); 27 | action.sa_flags = SA_SIGINFO; 28 | action.sa_sigaction = handler; 29 | for (int n = 1; n < NSIG; n++) { 30 | if (n == SIGTSTP || n == SIGQUIT) { continue; } 31 | if (sigaction(n, &action, NULL) == -1) { 32 | /*errExit("sigaction\n");*/ 33 | } 34 | } 35 | 36 | sigset_t blocked, prevMask; 37 | sigfillset(&blocked); 38 | sigdelset(&blocked, SIGINT); 39 | sigdelset(&blocked, SIGTERM); 40 | printf("blocking all signals\n"); 41 | if (sigprocmask(SIG_SETMASK, &blocked, &prevMask) == -1) { 42 | errExit("sigprocmask\n"); 43 | } 44 | 45 | int sleepTime = 15; 46 | printf("sleeping for %d seconds\n", sleepTime); 47 | sleep(sleepTime); 48 | 49 | printf("unblocking all signals\n"); 50 | if (sigprocmask(SIG_SETMASK, &prevMask, NULL) == -1) { 51 | errExit("sigprocmask\n"); 52 | } 53 | 54 | while (!gotSigint) { 55 | pause(); 56 | } 57 | 58 | exit(EXIT_SUCCESS); 59 | } -------------------------------------------------------------------------------- /setfattr_clone.c: -------------------------------------------------------------------------------- 1 | /* Exercise 16-1: implement a setfattr-like program that can set extended attributes */ 2 | 3 | #include 4 | #include 5 | #include "tlpi_hdr.h" 6 | 7 | void usage (char *); 8 | 9 | void usage (char *program_name) { 10 | usageErr("%s -n key -v value pathname\n" 11 | "%s -x key pathname\n", 12 | program_name, program_name); 13 | } 14 | 15 | int main (int argc, char *argv[]) { 16 | char mode = '\0'; 17 | char *key; 18 | char *value = ""; 19 | 20 | int opt; 21 | while ((opt = getopt(argc, argv, "n:x:v:")) != -1) { 22 | switch (opt) { 23 | case 'n': mode = 'n'; key = optarg; break; 24 | case 'x': mode = 'x'; key = optarg; break; 25 | case 'v': value = optarg; break; 26 | case '?': usage(argv[0]); 27 | } 28 | } 29 | 30 | if (mode == '\0' || optind >= argc) { usage(argv[0]); } 31 | 32 | if (mode == 'n') { 33 | if (setxattr(argv[optind], key, (void *) value, strlen(value), 0) == -1) { 34 | errExit("setxattr\n"); 35 | } 36 | } else { 37 | if (removexattr(argv[optind], key) == -1) { 38 | errExit("removexattr\n"); 39 | } 40 | } 41 | 42 | exit(EXIT_SUCCESS); 43 | } -------------------------------------------------------------------------------- /sig_speed_sigsuspend.txt: -------------------------------------------------------------------------------- 1 | real 0m16.304s 2 | user 0m0.563s 3 | sys 0m8.859s 4 | -------------------------------------------------------------------------------- /sig_speed_sigwaitinfo.c: -------------------------------------------------------------------------------- 1 | /**********************************************************************\ 2 | * Copyright (C) Michael Kerrisk, 2010. * 3 | * * 4 | * This program is free software. You may use, modify, and redistribute * 5 | * it under the terms of the GNU Affero General Public License as * 6 | * published by the Free Software Foundation, either version 3 or (at * 7 | * your option) any later version. This program is distributed without * 8 | * any warranty. See the file COPYING for details. * 9 | \**********************************************************************/ 10 | 11 | /* sig_speed_sigsuspend.c 12 | 13 | This program times how fast signals are sent and received. 14 | 15 | The program forks to create a parent and a child process that alternately 16 | send signals to each other (the child starts first). Each process catches 17 | the signal with a handler, and waits for signals to arrive using 18 | sigsuspend(). 19 | 20 | Usage: $ time ./sig_speed_sigsuspend num-sigs 21 | 22 | The 'num-sigs' argument specifies how many times the parent and 23 | child send signals to each other. 24 | 25 | Child Parent 26 | 27 | for (s = 0; s < numSigs; s++) { for (s = 0; s < numSigs; s++) { 28 | send signal to parent wait for signal from child 29 | wait for a signal from parent send a signal to child 30 | } } 31 | */ 32 | #include 33 | #include "tlpi_hdr.h" 34 | 35 | #define TESTSIG SIGUSR1 36 | 37 | int 38 | main(int argc, char *argv[]) 39 | { 40 | int numSigs, scnt; 41 | pid_t childPid; 42 | sigset_t blockedMask, emptyMask, testsigMask; 43 | 44 | if (argc != 2 || strcmp(argv[1], "--help") == 0) 45 | usageErr("%s num-sigs\n", argv[0]); 46 | 47 | numSigs = getInt(argv[1], GN_GT_0, "num-sigs"); 48 | 49 | /* Block the signal before fork(), so that the child doesn't manage 50 | to send it to the parent before the parent is ready to catch it */ 51 | 52 | sigemptyset(&blockedMask); 53 | sigaddset(&blockedMask, TESTSIG); 54 | if (sigprocmask(SIG_SETMASK, &blockedMask, NULL) == -1) 55 | errExit("sigprocmask"); 56 | 57 | sigemptyset(&emptyMask); 58 | 59 | sigemptyset(&testsigMask); 60 | sigaddset(&testsigMask, TESTSIG); 61 | 62 | switch (childPid = fork()) { 63 | case -1: errExit("fork"); 64 | 65 | case 0: /* child */ 66 | for (scnt = 0; scnt < numSigs; scnt++) { 67 | if (kill(getppid(), TESTSIG) == -1) 68 | errExit("kill"); 69 | if (sigwaitinfo(&testsigMask, NULL) == -1 && errno != EINTR) 70 | errExit("sigwaitinfo"); 71 | } 72 | exit(EXIT_SUCCESS); 73 | 74 | default: /* parent */ 75 | for (scnt = 0; scnt < numSigs; scnt++) { 76 | if (sigwaitinfo(&testsigMask, NULL) == -1 && errno != EINTR) 77 | errExit("sigwaitinfo"); 78 | if (kill(childPid, TESTSIG) == -1) 79 | errExit("kill"); 80 | } 81 | exit(EXIT_SUCCESS); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /sig_speed_sigwaitinfo.txt: -------------------------------------------------------------------------------- 1 | real 0m16.068s 2 | user 0m0.397s 3 | sys 0m9.603s 4 | -------------------------------------------------------------------------------- /sigaction_nodefer.c: -------------------------------------------------------------------------------- 1 | /* Exercise 20-3 (part 2): verify effect of NO_DEFER flag for sigaction */ 2 | 3 | #include 4 | #include "tlpi_hdr.h" 5 | 6 | void handler (int); 7 | 8 | int nesting_level; 9 | int exited; 10 | 11 | void handler (int signal) { 12 | if (exited || nesting_level > 5) { return; } 13 | nesting_level++; 14 | if (raise(SIGINT) == -1) { 15 | errExit("raise\n"); 16 | } 17 | exited = 1; 18 | } 19 | 20 | int main (int argc, char *argv[]) { 21 | printf("Without SA_NODEFER\n"); 22 | 23 | struct sigaction sigint_action; 24 | sigint_action.sa_handler = handler; 25 | sigemptyset(&sigint_action.sa_mask); 26 | sigint_action.sa_flags = 0; 27 | 28 | if (sigaction(SIGINT, &sigint_action, NULL) == -1) { 29 | errExit("sigaction\n"); 30 | } 31 | 32 | exited = 0; 33 | nesting_level = 0; 34 | if (raise(SIGINT) == -1) { 35 | errExit("raise\n"); 36 | } 37 | printf(" nesting_level: %d\n", nesting_level); 38 | 39 | printf("With SA_NODEFER:\n"); 40 | 41 | sigint_action.sa_flags |= SA_NODEFER; 42 | 43 | if (sigaction(SIGINT, &sigint_action, NULL) == -1) { 44 | errExit("sigaction\n"); 45 | } 46 | 47 | exited = 0; 48 | nesting_level = 0; 49 | if (raise(SIGINT) == -1) { 50 | errExit("raise\n"); 51 | } 52 | printf(" nesting_level: %d\n", nesting_level); 53 | 54 | exit(EXIT_SUCCESS); 55 | } -------------------------------------------------------------------------------- /sigaction_resethand.c: -------------------------------------------------------------------------------- 1 | /* Exercise 20-3 (part 1): verify effect of SA_RESETHAND flag for sigaction */ 2 | 3 | #include 4 | #include "tlpi_hdr.h" 5 | 6 | void handler (int); 7 | 8 | int counter = 0; 9 | 10 | void handler (int signal) { 11 | counter++; 12 | } 13 | 14 | int main (int argc, char *argv[]) { 15 | struct sigaction sigint_action; 16 | sigint_action.sa_handler = handler; 17 | sigemptyset(&sigint_action.sa_mask); 18 | sigint_action.sa_flags = 0; 19 | 20 | /* uncomment to see effect of SA_RESETHAND */ 21 | sigint_action.sa_flags |= SA_RESETHAND; 22 | 23 | if (sigaction(SIGINT, &sigint_action, NULL) == -1) { 24 | errExit("sigaction\n"); 25 | } 26 | 27 | for (int i = 0; i < 5; i++) { 28 | printf("counter: %d\n", counter); 29 | if (raise(SIGINT) == -1) { 30 | errExit("raise\n"); 31 | } 32 | } 33 | printf("counter: %d\n", counter); 34 | 35 | exit(EXIT_SUCCESS); 36 | } -------------------------------------------------------------------------------- /sigcont_blocked.c: -------------------------------------------------------------------------------- 1 | /* Exercise 22-1 */ 2 | 3 | #include "tlpi_hdr.h" 4 | #include 5 | 6 | void cont_handler (int); 7 | 8 | void cont_handler (int signal) { 9 | printf("caught SIGCONT\n"); /* UNSAFE */ 10 | } 11 | 12 | int main (int argc, char *argv[]) { 13 | struct sigaction cont_action; 14 | sigemptyset(&cont_action.sa_mask); 15 | cont_action.sa_flags = 0; 16 | cont_action.sa_handler = cont_handler; 17 | if (sigaction(SIGCONT, &cont_action, NULL) == -1) { 18 | errExit("sigaction\n"); 19 | } 20 | 21 | sigset_t blocked; 22 | sigemptyset(&blocked); 23 | sigaddset(&blocked, SIGCONT); 24 | printf("blocking SIGCONT\n"); 25 | if (sigprocmask(SIG_BLOCK, &blocked, NULL) == -1) { 26 | errExit("sigprocmask\n"); 27 | } 28 | 29 | int sleepTime = 15; 30 | printf("sleeping for %d seconds\n", sleepTime); 31 | sleep(sleepTime); 32 | 33 | printf("unblocking SIGCONT\n"); 34 | if (sigprocmask(SIG_UNBLOCK, &blocked, NULL) == -1) { 35 | errExit("sigprocmask\n"); 36 | } 37 | 38 | exit(EXIT_SUCCESS); 39 | } -------------------------------------------------------------------------------- /signal_ign_demo.c: -------------------------------------------------------------------------------- 1 | /* Exercise 20-2: show that SIG_IGN makes the process ignore a signal */ 2 | 3 | #define _GNU_SOURCE 4 | 5 | #include "tlpi_hdr.h" 6 | #include "signal_functions.c" 7 | #include 8 | #include 9 | 10 | int main (int argc, char *argv[]) { 11 | sigset_t sigint_set; 12 | sigemptyset(&sigint_set); 13 | sigaddset(&sigint_set, SIGINT); 14 | 15 | /* temporarily disable SIGINT delivery */ 16 | if (sigprocmask(SIG_BLOCK, &sigint_set, NULL) == -1) { 17 | errExit("sigprocmask\n"); 18 | } 19 | 20 | /* send SIGINT -- uncomment to see the effect */ 21 | if (raise(SIGINT) != 0) { 22 | errExit("raise\n"); 23 | } 24 | 25 | /* print pending signals */ 26 | sigset_t pending; 27 | if (sigpending(&pending) == -1) { 28 | errExit("sigpending\n"); 29 | } 30 | printSigset(stdout, "pending signal: ", &pending); 31 | 32 | /* ignore SIGINT */ 33 | struct sigaction ign_action; 34 | ign_action.sa_handler = SIG_IGN; 35 | sigemptyset(&ign_action.sa_mask); 36 | ign_action.sa_flags = 0; 37 | if (sigaction (SIGINT, &ign_action, NULL) == -1) { 38 | errExit("sigaction\n"); 39 | } 40 | 41 | /* reenable SIGINT */ 42 | if (sigprocmask(SIG_UNBLOCK, &sigint_set, NULL) == -1) { 43 | errExit("sigprocmask\n"); 44 | } 45 | 46 | /* sleep for one second */ 47 | sleep(1); 48 | 49 | printf("Still running, not terminated by SIGINT.\n"); 50 | 51 | exit(EXIT_SUCCESS); 52 | } -------------------------------------------------------------------------------- /t_clock_nanosleep_abs.c: -------------------------------------------------------------------------------- 1 | /* Exercise 23-2: replace nanosleep() with clock_gettime() and clock_nanosleep in t_nanosleep.c */ 2 | 3 | /**********************************************************************\ 4 | * Copyright (C) Michael Kerrisk, 2010. * 5 | * * 6 | * This program is free software. You may use, modify, and redistribute * 7 | * it under the terms of the GNU Affero General Public License as * 8 | * published by the Free Software Foundation, either version 3 or (at * 9 | * your option) any later version. This program is distributed without * 10 | * any warranty. See the file COPYING for details. * 11 | \**********************************************************************/ 12 | 13 | /* t_nanosleep.c 14 | 15 | Demonstrate the use of nanosleep() to sleep for an interval 16 | specified in nanoseconds. 17 | 18 | See also t_clock_nanosleep.c. 19 | */ 20 | #define _POSIX_C_SOURCE 199309 21 | #include 22 | #include 23 | #include 24 | #include "tlpi_hdr.h" 25 | 26 | #define TRILLION 1000000000 27 | 28 | static void 29 | sigintHandler(int sig) 30 | { 31 | return; /* Just interrupt nanosleep() */ 32 | } 33 | 34 | int 35 | main(int argc, char *argv[]) 36 | { 37 | struct timeval start, finish; 38 | struct timespec beginning, end, remain; 39 | struct sigaction sa; 40 | int s; 41 | 42 | printf("PID: %ld\n", (long) getpid()); 43 | 44 | if (argc != 3 || strcmp(argv[1], "--help") == 0) 45 | usageErr("%s secs nanosecs\n", argv[0]); 46 | 47 | if (clock_gettime(CLOCK_MONOTONIC, &beginning) == -1) { 48 | errExit("clock_gettime\n"); 49 | } 50 | 51 | end.tv_sec = beginning.tv_sec + getLong(argv[1], 0, "secs"); 52 | end.tv_nsec = beginning.tv_nsec + getLong(argv[2], 0, "nanosecs"); 53 | if (end.tv_nsec >= TRILLION) { 54 | end.tv_sec += 1; 55 | end.tv_nsec -= TRILLION; 56 | } 57 | 58 | /* Allow SIGINT handler to interrupt nanosleep() */ 59 | 60 | sigemptyset(&sa.sa_mask); 61 | sa.sa_flags = 0; 62 | sa.sa_handler = sigintHandler; 63 | if (sigaction(SIGINT, &sa, NULL) == -1) 64 | errExit("sigaction"); 65 | 66 | if (gettimeofday(&start, NULL) == -1) 67 | errExit("gettimeofday"); 68 | 69 | for (;;) { 70 | s = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &end, NULL); 71 | if (s == -1 && errno != EINTR) 72 | errExit("nanosleep"); 73 | 74 | if (gettimeofday(&finish, NULL) == -1) 75 | errExit("gettimeofday"); 76 | printf("Slept for: %9.6f secs\n", finish.tv_sec - start.tv_sec + 77 | (finish.tv_usec - start.tv_usec) / 1000000.0); 78 | 79 | if (s == 0) 80 | break; /* nanosleep() completed */ 81 | 82 | printf("Remaining: %2ld.%09ld\n", (long) remain.tv_sec, remain.tv_nsec); 83 | } 84 | 85 | printf("Sleep complete\n"); 86 | exit(EXIT_SUCCESS); 87 | } 88 | -------------------------------------------------------------------------------- /tail.c: -------------------------------------------------------------------------------- 1 | /* 2 | Exercise 13-5: implement tail efficiently 3 | 4 | deficencies: 5 | * can fail when the file is modified concurrently 6 | * assumes that a file ends with '\n' 7 | */ 8 | 9 | #include 10 | #include 11 | #include "tlpi_hdr.h" 12 | 13 | #ifndef BUF_SIZE 14 | #define BUF_SIZE 1024 15 | #endif 16 | 17 | int main (int argc, char *argv[]) { 18 | int n = 10; 19 | 20 | int opt; 21 | while ((opt = getopt(argc, argv, "n:")) != -1) { 22 | if (opt == 'n') { 23 | n = getLong(optarg, GN_GT_0, "n"); 24 | } 25 | } 26 | 27 | n++; /* account for \n at the end of the file */ 28 | 29 | if (optind == argc) { 30 | usageErr("%s [-n num] file\n", argv[0]); 31 | } 32 | 33 | int fd = open(argv[optind], O_RDONLY); 34 | if (fd == -1) { errExit("open(\"%s\", O_RDONLY)\n", argv[optind]); } 35 | 36 | char buf[BUF_SIZE]; 37 | ssize_t numRead, numWritten; 38 | 39 | off_t cur = lseek(fd, 0, SEEK_END);; 40 | while (cur > 0) { 41 | size_t toRead; 42 | if (cur < BUF_SIZE) { 43 | toRead = (size_t) cur; 44 | cur = 0; 45 | } else { 46 | toRead = BUF_SIZE; 47 | cur = cur - BUF_SIZE; 48 | } 49 | if (lseek(fd, cur, SEEK_SET) == -1) { 50 | errExit("lseek\n"); 51 | } 52 | 53 | numRead = read(fd, buf, toRead); 54 | if (numRead != toRead) { errExit("couldn't read whole file\n"); } 55 | for (int i = numRead - 1; i >= 0; i--) { 56 | if (buf[i] == '\n' && --n == 0) { 57 | numWritten = write(STDOUT_FILENO, &buf[i+1], numRead - (i+1)); 58 | if (numWritten != numRead - (i+1)) { errExit("write"); } 59 | goto after_outer_loop; 60 | } 61 | } 62 | if (cur == 0) { 63 | numWritten = write(STDOUT_FILENO, buf, toRead); 64 | if (numWritten != toRead) { errExit("write"); } 65 | } 66 | } 67 | 68 | after_outer_loop: 69 | 70 | // read rest of file and print it to stdout 71 | while ((numRead = read(fd, buf, BUF_SIZE)) > 0) { 72 | numWritten = write(STDOUT_FILENO, buf, (size_t) numRead); 73 | if (numWritten != numRead) { errExit("couldn't write whole buffer"); } 74 | } 75 | if (numRead == -1) { errExit("read"); } 76 | 77 | exit(EXIT_SUCCESS); 78 | } -------------------------------------------------------------------------------- /tee.c: -------------------------------------------------------------------------------- 1 | /* Exercise 4-1 */ 2 | 3 | #include 4 | #include 5 | #include "tlpi_hdr.h" 6 | 7 | #ifndef BUF_SIZE 8 | #define BUF_SIZE 1024 9 | #endif 10 | 11 | int main (int argc, char *argv[]) { 12 | Boolean append = FALSE; 13 | 14 | int opt; 15 | while ((opt = getopt(argc, argv, "a")) != -1) { 16 | if ((unsigned char) opt == 'a') { append = TRUE; } 17 | } 18 | 19 | if (optind >= argc) { 20 | usageErr("%s [-a] file\n", argv[0]); 21 | } 22 | 23 | int fd = open( 24 | argv[optind], 25 | O_CREAT | O_WRONLY | (append ? O_APPEND : O_TRUNC), 26 | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH /* rw-rw-rw- */ 27 | ); 28 | if (fd == -1) { errExit("opening file %s", argv[optind]); } 29 | 30 | ssize_t numRead; 31 | char buf[BUF_SIZE]; 32 | while ((numRead = read(STDIN_FILENO, buf, BUF_SIZE)) > 0) { 33 | if (write(fd, buf, numRead) != numRead) { 34 | errExit("couldn't write whole buffer!"); 35 | } 36 | if (write(STDOUT_FILENO, buf, numRead) != numRead) { 37 | errExit("couldn't write whole buffer!"); 38 | } 39 | } 40 | if (numRead == -1) { 41 | errExit("read"); 42 | } 43 | if (close(fd) == -1) { 44 | errExit("close input"); 45 | } 46 | 47 | exit(EXIT_SUCCESS); 48 | } -------------------------------------------------------------------------------- /vfork_closing_file_descriptor.c: -------------------------------------------------------------------------------- 1 | /* Exercise 24-2: Write a program to demonstrate that after a vfork(), the child process can close a 2 | file descriptor (e.g., descriptor 0) without affecting the corresponding file descriptor 3 | in the parent. */ 4 | 5 | #include "tlpi_hdr.h" 6 | #include 7 | #include 8 | 9 | int main (int argc, char *argv[]) { 10 | setbuf(stdout, NULL); 11 | 12 | printf("opening temp file\n"); 13 | char temp_template[] = "vfork_testXXXXXX"; 14 | int temp_fd = mkstemp(temp_template); 15 | if (temp_fd == -1) { 16 | errExit("mkstemp\n"); 17 | } 18 | printf("seeking to position 10\n"); 19 | if (lseek(temp_fd, 10, SEEK_CUR) == -1) { 20 | errExit("lseek\n"); 21 | } 22 | 23 | pid_t child_pid; 24 | switch (child_pid = vfork()) { 25 | case -1: 26 | errExit("vfork\n"); 27 | break; 28 | case 0: /* child process */ 29 | printf("child process about to close temp file\n"); 30 | if (close(temp_fd) == -1) { 31 | errExit("close\n"); 32 | } 33 | _exit(0); 34 | break; 35 | default: /* parent process */ 36 | wait(NULL); 37 | printf("child exited\n"); 38 | off_t offset = lseek(temp_fd, 0, SEEK_CUR); 39 | if (offset == -1) { 40 | errExit("lseek\n"); 41 | } 42 | printf("current file position: %lld\n", (long long) offset); 43 | exit(EXIT_SUCCESS); 44 | break; 45 | } 46 | } -------------------------------------------------------------------------------- /zombie_parent.c: -------------------------------------------------------------------------------- 1 | /* Exercise 26-2: Suppose that we have three processes related as grandparent, parent, and child, 2 | and that the grandparent doesn’t immediately perform a wait() after the parent 3 | exits, so that the parent becomes a zombie. When do you expect the grandchild to 4 | be adopted by init (so that getppid() in the grandchild returns 1): after the parent 5 | terminates or after the grandparent does a wait()? Write a program to verify your 6 | answer. */ 7 | 8 | #include "tlpi_hdr.h" 9 | #include 10 | 11 | int main (int argc, char *argv[]) { 12 | setbuf(stdout, NULL); 13 | 14 | pid_t parent, child; 15 | switch (parent = fork()) { 16 | case -1: 17 | errExit("fork (1)\n"); 18 | break; 19 | case 0: 20 | switch (child = fork()) { 21 | case -1: 22 | errExit("fork (2)\n"); 23 | case 0: /* child */ 24 | printf("child PPID: %ld\n", (long) getppid()); 25 | sleep(2); 26 | printf("child PPID: %ld\n", (long) getppid()); 27 | break; 28 | default: /* parent */ 29 | sleep(1); 30 | printf("parent exiting -- going into zombie state\n"); 31 | } 32 | break; 33 | default: /* grandparent */ 34 | sleep(3); 35 | printf("grandparent exiting\n"); 36 | break; 37 | } 38 | } --------------------------------------------------------------------------------