├── LICENSE ├── README.md ├── ch1 ├── first-example.c ├── new-sum.c └── sum.c ├── ch10 ├── Makefile ├── fifo-receiver.c ├── fifo-sender.c ├── msg-reveiver.c ├── msg-sender.c ├── my-daemon-ctl.c ├── my-daemon-v2.c ├── pipe-example.c ├── read-memory.c ├── shm-parent-child.c ├── unix-client.c ├── unix-server.c └── write-memory.c ├── ch11 ├── Makefile ├── cond-var.c ├── efficient.c ├── first-threaded.c ├── locking.c ├── race.c └── second-threaded.c ├── ch12 ├── Makefile ├── area-of-circle.c ├── first-threaded.c ├── forking.c ├── leak.c ├── loop.c ├── memtest.c └── overflow.c ├── ch2 ├── ascii-table.c ├── ascii-table.md ├── avg-with-garbage.txt ├── avg.txt ├── case-changer.c ├── env-var-set.c ├── env-var.c ├── exist.sh ├── functions_ver1.c ├── functions_ver2.c ├── mph-to-kph.c ├── mph-to-kph_v2.c └── output.c ├── ch3 ├── Makefile ├── area │ ├── Makefile │ ├── area.c │ ├── area.h │ ├── circle.c │ ├── help.c │ ├── rectangle.c │ └── triangle.c ├── circumference.c ├── comments.c ├── cube │ ├── Makefile │ ├── cube-func.c │ ├── cube-prog.c │ └── cube.h ├── for-test.c ├── interest.c ├── is-it-a-prime.c ├── libprime.so ├── my-sys.c ├── no-return.c ├── prime.c ├── prime.h ├── str-posix.c ├── sys-write.c ├── which-c.c └── write-chars.c ├── ch4 ├── Makefile ├── huge-test.c ├── simple-touch-v1.c ├── simple-touch-v2.c ├── simple-touch-v3.c ├── simple-touch-v4.c ├── simple-touch-v5.c ├── simple-touch-v6.c ├── simple-touch-v7.c ├── str-safe.c └── str-unsafe.c ├── ch5 ├── Makefile ├── binary-read.c ├── binary-write.c ├── fd-read.c ├── fd-seek.c ├── fd-write.c ├── my-chmod.c ├── my-chown.c ├── my-stat-v1.c ├── my-stat-v2.c ├── new-name.c ├── new-symlink.c ├── remove.c ├── simple-touch-v8.c ├── stream-read.c ├── stream-seek.c ├── stream-write.c └── testfile1 ├── ch6 ├── Makefile ├── create-zombie.c ├── execdemo.c ├── forkdemo.c ├── my-daemon-v2.c ├── my-daemon.c ├── my-fork.c ├── no-zombie.c ├── orphan.c ├── signals.c └── sysdemo.c ├── ch7 ├── Makefile ├── my-daemon-v2.c ├── my-daemon.service └── new-style-daemon.c ├── ch8 ├── Makefile ├── convert.c ├── convert.h ├── get-public-ip.c └── temperature.c ├── ch9 ├── Makefile ├── my-pty.c ├── passprompt.c ├── terminal-size.c └── ttyinfo.c └── errata.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Linux System Programming Techniques 5 | 6 | Linux System Programming Techniques 7 | 8 | This is the code repository for [Linux System Programming Techniques](https://www.packtpub.com/product/linux-system-programming-techniques/9781789951288#:~:text=About%20this%20book&text=It%20enables%20you%20to%20extend,pages%2C%20and%20Linux%20system%20calls.?utm_source=github&utm_medium=repository&utm_campaign=9781789951288), published by Packt. 9 | 10 | **Linux System Programming Techniques, published by Packt** 11 | 12 | ## Errata 13 | An up-to-date errata can be found [here](errata.md). Please review this document if you encounter any problems! 14 | 15 | ## What is this book about? 16 | Linux is the world's most popular open-source operating system. Linux system programming is all about developing system programs for Linux and Unix operating systems. 17 | It enables you to extend the Linux operating system with your own system programs and to communicate with other programs on the system. The book begins by exploring the GCC compiler, a C program, the shell, the built-in manual pages, and Linux system calls. 18 | This book covers the following exciting features: 19 | * Discover how to write programs for the Linux system using a wide variety of system calls 20 | * Delve into the working of POSIX functions 21 | * Understand and use key concepts such as signals, pipes, IPC, and process management 22 | * Find out how to integrate programs with a Linux system 23 | * Explore advanced topics such as filesystem operations, creating shared libraries, and debugging your programs 24 | * Gain an overall understanding of how to debug your programs using Valgrind 25 | 26 | If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1789951283) today! 27 | 28 | https://www.packtpub.com/ 30 | 31 | ## Instructions and Navigations 32 | All of the code is organized into folders. For example, Chapter02. 33 | 34 | The code will look like the following: 35 | ``` 36 | #include 37 | int main(void) 38 | { 39 | printf("Hello, world!\n"); 40 | return 0; 41 | } 42 | ``` 43 | 44 | ### Code in Action 45 | Click on following link to see the Code in Action: 46 | 47 | [Youtube link](https://bit.ly/39ovGd6) 48 | 49 | **Following is what you need for this book:** 50 | This book is for anyone who wants to develop system programs for Linux and gain a deeper understanding of the Linux system. The book is beneficial for anyone who is facing issues related to a particular part of Linux system programming and is looking for specific recipes or solutions. 51 | 52 | With the following software and hardware list you can run all code files present in the book (Chapter 1-12). 53 | 54 | We also provide a PDF file that has color images of the screenshots/diagrams used in this book. [Click here to download it](http://www.packtpub.com/sites/default/files/downloads/9781789951288_ColorImages.pdf). 55 | 56 | ### Related products 57 | * Mastering Linux Device Driver Development [[Packt]](https://www.packtpub.com/product/mastering-linux-device-driver-development/9781789342048?utm_source=github&utm_medium=repository&utm_campaign=9781789342048) [[Amazon]](https://www.amazon.in/Mastering-Linux-Device-Driver-Development-ebook/dp/B08M6G6Q4N) 58 | 59 | * Linux Kernel Programming [[Packt]](https://www.packtpub.com/product/linux-kernel-programming/9781789953435?utm_source=github&utm_medium=repository&utm_campaign=9781789953435) [[Amazon]](https://www.amazon.in/Linux-Kernel-Development-Cookbook-programming-ebook/dp/B07RW915K4/ref=sr_1_2?dchild=1&keywords=Linux+Kernel+Programming&qid=1617710161&s=digital-text&sr=1-2) 60 | 61 | ## Get to Know the Author 62 | **Jack-Benny Persson** 63 | is a consultant and author based in Sweden. He has written several books about Linux and programming. His passion for Linux and other Unix-like systems started as a hobby more than 20 years ago. Since then, he has spent most of his spare time reading about Linux, tinkering with Linux servers, and writing about Linux administration. Today he has his own IT and media company in Sweden that focuses on Linux. 64 | Jack-Benny holds an Advanced Higher Vocational Education Diploma as a Linux system specialist. He has also studied electronics, networking, and security. 65 | 66 | ## Other books by the authors 67 | * [Hands-On Linux System Administration [Video]](https://www.packtpub.com/product/hands-on-linux-system-administration-video/9781789133219?utm_source=github&utm_medium=repository&utm_campaign=9781789133219) 68 | 69 | 70 | ### Download a free PDF 71 | 72 | If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.
Simply click on the link to claim your free PDF.
73 |

https://packt.link/free-ebook/9781789951288

-------------------------------------------------------------------------------- /ch1/first-example.c: -------------------------------------------------------------------------------- 1 | #include 2 | int main(void) 3 | { 4 | printf("Hello, world!\n"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /ch1/new-sum.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 500 2 | #include 3 | #include 4 | #include 5 | void printhelp(char progname[]); 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | int i, opt, sum; 10 | 11 | /* Simple sanity check */ 12 | if (argc == 1) 13 | { 14 | printhelp(argv[0]); 15 | return 1; 16 | } 17 | 18 | /* Parse command-line options */ 19 | while ((opt = getopt(argc, argv, "smh")) != -1) 20 | { 21 | switch (opt) 22 | { 23 | case 's': /* sum the integers */ 24 | sum = 0; 25 | for (i=2; i 2 | #include 3 | void printhelp(char progname[]); 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | int i; 8 | int sum = 0; 9 | 10 | /* Simple sanity check */ 11 | if (argc == 1) 12 | { 13 | printhelp(argv[0]); 14 | return 1; 15 | } 16 | 17 | for (i=1; i 2 | 3 | int main(void) 4 | { 5 | FILE *fp; 6 | signed char c; 7 | const char fifoname[] = "/tmp/my-2nd-fifo"; 8 | if ( (fp = fopen(fifoname, "r")) == NULL ) 9 | { 10 | perror("Can't open FIFO"); 11 | return 1; 12 | } 13 | while ( (c = getc(fp)) != EOF ) 14 | putchar(c); 15 | fclose(fp); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /ch10/fifo-sender.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 700 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | void cleanUp(int signum); 13 | 14 | int fd; /* the FIFO file descriptor */ 15 | const char fifoname[] = "/tmp/my-2nd-fifo"; 16 | 17 | int main(int argc, char *argv[]) 18 | { 19 | struct sigaction action; /* for sigaction */ 20 | if ( argc != 2 ) 21 | { 22 | fprintf(stderr, "Usage: %s 'the message'\n", 23 | argv[0]); 24 | return 1; 25 | } 26 | 27 | /* prepare for sigaction and register signals 28 | * (for cleanup when we exit) */ 29 | action.sa_handler = cleanUp; 30 | sigfillset(&action.sa_mask); 31 | action.sa_flags = SA_RESTART; 32 | sigaction(SIGTERM, &action, NULL); 33 | sigaction(SIGINT, &action, NULL); 34 | sigaction(SIGQUIT, &action, NULL); 35 | sigaction(SIGABRT, &action, NULL); 36 | sigaction(SIGPIPE, &action, NULL); 37 | 38 | if ( (mkfifo(fifoname, 0644)) != 0 ) 39 | { 40 | perror("Can't create FIFO"); 41 | return 1; 42 | } 43 | 44 | if ( (fd = open(fifoname, O_WRONLY)) == -1) 45 | { 46 | perror("Can't open FIFO"); 47 | return 1; 48 | } 49 | while(1) 50 | { 51 | dprintf(fd, "%s\n", argv[1]); 52 | sleep(1); 53 | } 54 | /* just in case, but we shouldn't reach this */ 55 | close(fd); 56 | unlink(fifoname); 57 | return 0; 58 | } 59 | 60 | void cleanUp(int signum) 61 | { 62 | if (signum == SIGPIPE) 63 | printf("The receiver stopped receiving\n"); 64 | else 65 | printf("Aborting...\n"); 66 | if ( (close(fd)) == -1 ) 67 | perror("Can't close file descriptor"); 68 | if ( (unlink(fifoname)) == -1) 69 | { 70 | perror("Can't remove FIFO"); 71 | exit(1); 72 | } 73 | exit(0); 74 | } 75 | -------------------------------------------------------------------------------- /ch10/msg-reveiver.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main(void) 10 | { 11 | int md; /* msg queue descriptor */ 12 | char *buffer; 13 | struct mq_attr msgattr; 14 | md = mq_open("/my_queue", O_RDONLY); 15 | if (md == -1 ) 16 | { 17 | perror("Open message queue"); 18 | return 1; 19 | } 20 | if ( (mq_getattr(md, &msgattr)) == -1 ) 21 | { 22 | perror("Get message attribute"); 23 | return 1; 24 | } 25 | buffer = calloc(msgattr.mq_msgsize, 26 | sizeof(char)); 27 | if (buffer == NULL) 28 | { 29 | fprintf(stderr, "Couldn't allocate memory"); 30 | return 1; 31 | } 32 | printf("%ld messages in queue\n", 33 | msgattr.mq_curmsgs); 34 | for (int i = 0; i 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #define MAX_MSG_SIZE 2048 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | int md; /* msg queue descriptor */ 12 | /* attributes for the message queue */ 13 | struct mq_attr msgattr; 14 | msgattr.mq_maxmsg = 10; 15 | msgattr.mq_msgsize = MAX_MSG_SIZE; 16 | 17 | if ( argc != 2) 18 | { 19 | fprintf(stderr, "Usage: %s 'my message'\n", 20 | argv[0]); 21 | return 1; 22 | } 23 | md = mq_open("/my_queue", O_CREAT|O_RDWR, 0644, 24 | &msgattr); 25 | if ( md == -1 ) 26 | { 27 | perror("Creating message queue"); 28 | return 1; 29 | } 30 | if ( (mq_send(md, argv[1], strlen(argv[1]), 1)) 31 | == -1 ) 32 | { 33 | perror("Message queue send"); 34 | return 1; 35 | } 36 | mq_close(md); 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /ch10/my-daemon-ctl.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 500 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | void printUsage(char progname[], FILE *fp); 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | FILE *fp; 14 | FILE *procfp; 15 | int pid, opt; 16 | int killit = 0; 17 | char procpath[PATH_MAX] = { 0 }; 18 | char cmdline[PATH_MAX] = { 0 }; 19 | const char pidfile[] = "/var/run/my-daemon.pid"; 20 | const char daemon[] = "my-daemon-v2"; 21 | 22 | /* Parse command-line options */ 23 | while ((opt = getopt(argc, argv, "kh")) != -1) 24 | { 25 | switch (opt) 26 | { 27 | case 'k': /* kill the daemon */ 28 | killit = 1; 29 | break; 30 | case 'h': /* help */ 31 | printUsage(argv[0], stdout); 32 | return 0; 33 | default: /* in case of invalid options */ 34 | printUsage(argv[0], stderr); 35 | return 1; 36 | } 37 | } 38 | 39 | if ( (fp = fopen(pidfile, "r")) == NULL ) 40 | { 41 | perror("Can't open PID-file (daemon isn't " 42 | "running?)"); 43 | return 1; 44 | } 45 | /* read the pid (and check if we could read an 46 | * integer) */ 47 | if ( (fscanf(fp, "%d", &pid)) != 1 ) 48 | { 49 | fprintf(stderr, "Can't read PID from %s\n", 50 | pidfile); 51 | return 1; 52 | } 53 | 54 | /* build the /proc path */ 55 | sprintf(procpath, "/proc/%d/cmdline", pid); 56 | 57 | /* open the /proc path */ 58 | if ( (procfp = fopen(procpath, "r")) == NULL ) 59 | { 60 | perror("Can't open /proc path" 61 | " (no /proc or wrong PID?)"); 62 | return 1; 63 | } 64 | /* read the cmd line path from proc */ 65 | fscanf(procfp, "%s", cmdline); 66 | 67 | /* check the PID against the cmd line */ 68 | if ( (strstr(cmdline, daemon)) == 0 ) 69 | { 70 | fprintf(stderr, "PID %d doesn't belong " 71 | "to %s\n", pid, daemon); 72 | return 1; 73 | } 74 | 75 | if ( killit == 1 ) 76 | { 77 | if ( (kill(pid, SIGTERM)) == 0 ) 78 | { 79 | printf("Successfully terminated " 80 | "daemon\n"); 81 | } 82 | else 83 | { 84 | perror("Couldn't terminate daemon"); 85 | return 1; 86 | } 87 | 88 | } 89 | else 90 | { 91 | printf("The daemon is running with PID %d " 92 | "and cmdline %s\n", pid, cmdline); 93 | } 94 | return 0; 95 | } 96 | 97 | void printUsage(char progname[], FILE *fp) 98 | { 99 | fprintf(fp, "Usage: %s [-k] [-h]\n", progname); 100 | fprintf(fp, "If no options are given, a status " 101 | "message is displayed.\n" 102 | "-k will terminate the daemon.\n" 103 | "-h will display this usage help.\n"); 104 | } 105 | -------------------------------------------------------------------------------- /ch10/my-daemon-v2.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | void sigHandler(int sig); 12 | 13 | /* moved these variables to the global scope 14 | since they need to be access/deleted/closed 15 | from the signal handler */ 16 | FILE *fp; 17 | const char pidfile[] = "/var/run/my-daemon.pid"; 18 | 19 | int main(void) 20 | { 21 | pid_t pid; 22 | time_t now; /* for the current time */ 23 | struct sigaction action; /* for sigaction */ 24 | const char daemonfile[] = "/tmp/my-daemon-is-alive.txt"; 25 | 26 | if ( (pid = fork()) == -1 ) 27 | { 28 | perror("Can't fork"); 29 | return 1; 30 | } 31 | else if ( (pid != 0) ) 32 | { 33 | exit(0); 34 | } 35 | 36 | /* the parent process has exited, which makes the rest of 37 | the code the child process */ 38 | setsid(); /* create a new session to lose the controlling 39 | terminal */ 40 | 41 | /* fork again, creating a grandchild, the actual daemon */ 42 | if ( (pid = fork()) == -1 ) 43 | { 44 | perror("Can't fork"); 45 | return 1; 46 | } 47 | /* the child process which will exit */ 48 | else if ( pid > 0 ) 49 | { 50 | /* open pid-file for writing and error check it */ 51 | if ( (fp = fopen(pidfile, "w")) == NULL ) 52 | { 53 | perror("Can't open file for writing"); 54 | return 1; 55 | } 56 | fprintf(fp, "%d\n", pid); /* write pid to file */ 57 | fclose(fp); /* close the file pointer */ 58 | exit(0); 59 | } 60 | 61 | umask(022); /* reset the umask to something sensible */ 62 | chdir("/"); /* change working directory to / */ 63 | /* open the "daemonfile" for writing */ 64 | if ( (fp = fopen(daemonfile, "w")) == NULL ) 65 | { 66 | perror("Can't open daemonfile"); 67 | return 1; 68 | } 69 | /* from here, we don't need stdin, stdout or, stderr 70 | anymore, so let's close them all, then re-open them 71 | to /dev/null */ 72 | close(STDIN_FILENO); 73 | close(STDOUT_FILENO); 74 | close(STDERR_FILENO); 75 | open("/dev/null", O_RDONLY); /* 0 = stdin */ 76 | open("/dev/null", O_WRONLY); /* 1 = stdout */ 77 | open("/dev/null", O_RDWR); /* 2 = stderr */ 78 | 79 | /* prepare for sigaction */ 80 | action.sa_handler = sigHandler; 81 | sigfillset(&action.sa_mask); 82 | action.sa_flags = SA_RESTART; 83 | /* register the signals we want to handle */ 84 | sigaction(SIGTERM, &action, NULL); 85 | sigaction(SIGINT, &action, NULL); 86 | sigaction(SIGQUIT, &action, NULL); 87 | sigaction(SIGABRT, &action, NULL); 88 | 89 | /* here we start the daemons "work" */ 90 | for (;;) 91 | { 92 | /* get the current time and write it to the 93 | "daemonfile" that we opened above */ 94 | time(&now); 95 | fprintf(fp, "Daemon alive at %s", ctime(&now)); 96 | fflush(fp); /* flush the stream */ 97 | sleep(30); 98 | } 99 | return 0; 100 | } 101 | 102 | void sigHandler(int sig) 103 | { 104 | int status = 0; 105 | if ( sig == SIGTERM || sig == SIGINT 106 | || sig == SIGQUIT 107 | || sig == SIGABRT ) 108 | { 109 | /* remove the pid-file */ 110 | if ( (unlink(pidfile)) == -1 ) 111 | status = 1; 112 | if ( (fclose(fp)) == EOF ) 113 | status = 1; 114 | exit(status); /* exit with the status set */ 115 | } 116 | else /* some other signal */ 117 | { 118 | exit(1); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /ch10/pipe-example.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | #include 4 | #include 5 | #include 6 | #define MAX 128 7 | 8 | int main(void) 9 | { 10 | int pipefd[2] = { 0 }; 11 | pid_t pid; 12 | char line[MAX]; 13 | 14 | if ( (pipe(pipefd)) == -1 ) 15 | { 16 | perror("Can't create pipe"); 17 | return 1; 18 | } 19 | 20 | if ( (pid = fork()) == -1 ) 21 | { 22 | perror("Can't fork"); 23 | return 1; 24 | } 25 | 26 | if (pid > 0) 27 | { 28 | /* inside the parent */ 29 | close(pipefd[0]); /* close the read end */ 30 | dprintf(pipefd[1], "Hello from parent"); 31 | } 32 | else 33 | { 34 | /* inside the child */ 35 | close(pipefd[1]); /* close the write end */ 36 | read(pipefd[0], line, MAX-1); 37 | printf("%s\n", line); /* print message from 38 | * the parent */ 39 | } 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /ch10/read-memory.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #define DATASIZE 128 9 | 10 | int main(void) 11 | { 12 | int fd; 13 | float *addr; 14 | const char memid[] = "/my_memory"; 15 | float numbers[3]; 16 | 17 | /* open memory file descriptor */ 18 | fd = shm_open(memid, O_RDONLY, 0600); 19 | if (fd == -1) 20 | { 21 | perror("Can't open file descriptor"); 22 | return 1; 23 | } 24 | 25 | /* map shared memory */ 26 | addr = mmap(NULL, DATASIZE, PROT_READ, 27 | MAP_SHARED, fd, 0); 28 | if (addr == MAP_FAILED) 29 | { 30 | perror("Memory mapping failed"); 31 | return 1; 32 | } 33 | 34 | /* read the memory and print the numbers */ 35 | memcpy(numbers, addr, sizeof(numbers)); 36 | for (int i = 0; i<3; i++) 37 | { 38 | printf("Number %d: %.3f\n", i, numbers[i]); 39 | } 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /ch10/shm-parent-child.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #define DATASIZE 128 10 | 11 | int main(void) 12 | { 13 | char *addr; 14 | int status; 15 | pid_t pid; 16 | const char startmsg[] = "Hello, we are running"; 17 | const char childmsg[] = "Hello from child"; 18 | const char parentmsg[] = "New msg from parent"; 19 | addr = mmap(NULL, DATASIZE, 20 | PROT_WRITE | PROT_READ, 21 | MAP_SHARED | MAP_ANONYMOUS, -1, 0); 22 | if (addr == MAP_FAILED) 23 | { 24 | perror("mmap"); 25 | return 1; 26 | } 27 | 28 | memcpy(addr, startmsg, strlen(startmsg) + 1); 29 | printf("Parent PID is %d\n", getpid()); 30 | printf("Original message: %s\n", addr); 31 | if ( (pid = fork()) == -1 ) 32 | { 33 | perror("Can't fork"); 34 | return 1; 35 | } 36 | if (pid == 0) 37 | { 38 | /* child */ 39 | memcpy(addr, childmsg, strlen(childmsg) + 1); 40 | } 41 | else if(pid > 0) 42 | { 43 | /* parent */ 44 | waitpid(pid, &status, 0); 45 | printf("Child executed with PID %d\n", pid); 46 | printf("Message from child: %s\n", addr); 47 | memcpy(addr, parentmsg, 48 | strlen(parentmsg) + 1); 49 | printf("Parent message: %s\n", addr); 50 | } 51 | munmap(addr, DATASIZE); 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /ch10/unix-client.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 700 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #define MAXLEN 128 12 | 13 | int main(void) 14 | { 15 | const char sockname[] = "/tmp/my_1st_socket"; 16 | int fd; 17 | struct sockaddr_un addr; 18 | char sendbuffer[MAXLEN]; 19 | char recvbuffer[MAXLEN]; 20 | 21 | /* create socket file descriptor */ 22 | fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); 23 | if ( fd == -1 ) 24 | { 25 | perror("Create socket failed"); 26 | return 1; 27 | } 28 | /* set address family and socket path */ 29 | addr.sun_family = AF_UNIX; 30 | strcpy(addr.sun_path, sockname); 31 | 32 | /* connect to the server */ 33 | if ( (connect(fd, (const struct sockaddr*) &addr, 34 | sizeof(struct sockaddr_un))) == -1 ) 35 | { 36 | perror("Can't connect"); 37 | fprintf(stderr, "The server is down?\n"); 38 | return 1; 39 | } 40 | while(1) /* main loop */ 41 | { 42 | /* send message to server */ 43 | printf("Message to send: "); 44 | fgets(sendbuffer, sizeof(sendbuffer), stdin); 45 | sendbuffer[strcspn(sendbuffer, "\n")] = '\0'; 46 | if ( (write(fd, sendbuffer, 47 | strlen(sendbuffer) + 1)) == -1 ) 48 | { 49 | perror("Couldn't write"); 50 | break; 51 | } 52 | 53 | /* read response from server */ 54 | if ( (read(fd, recvbuffer, MAXLEN)) == -1 ) 55 | { 56 | perror("Can't read"); 57 | return 1; 58 | } 59 | printf("Server said: %s\n", recvbuffer); 60 | } 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /ch10/unix-server.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 700 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #define MAXLEN 128 12 | 13 | void cleanUp(int signum); 14 | const char sockname[] = "/tmp/my_1st_socket"; 15 | int connfd; 16 | int datafd; 17 | 18 | int main(void) 19 | { 20 | int ret; 21 | struct sockaddr_un addr; 22 | char buffer[MAXLEN]; 23 | struct sigaction action; 24 | /* prepare for sigaction */ 25 | action.sa_handler = cleanUp; 26 | sigfillset(&action.sa_mask); 27 | action.sa_flags = SA_RESTART; 28 | /* register the signals we want to handle */ 29 | sigaction(SIGTERM, &action, NULL); 30 | sigaction(SIGINT, &action, NULL); 31 | sigaction(SIGQUIT, &action, NULL); 32 | sigaction(SIGABRT, &action, NULL); 33 | sigaction(SIGPIPE, &action, NULL); 34 | 35 | /* create socket file descriptor */ 36 | connfd = socket(AF_UNIX, SOCK_SEQPACKET, 0); 37 | if ( connfd == -1 ) 38 | { 39 | perror("Create socket failed"); 40 | return 1; 41 | } 42 | /* set address family and socket path */ 43 | addr.sun_family = AF_UNIX; 44 | strcpy(addr.sun_path, sockname); 45 | /* bind the socket (we must cast our sockaddr_un 46 | * to sockaddr) */ 47 | if ( (bind(connfd, (const struct sockaddr*)&addr, 48 | sizeof(struct sockaddr_un))) == -1 ) 49 | { 50 | perror("Binding socket failed"); 51 | return 1; 52 | } 53 | /* prepare for accepting connections */ 54 | if ( (listen(connfd, 20)) == -1 ) 55 | { 56 | perror("Listen error"); 57 | return 1; 58 | } 59 | /* accept connection and create new file desc */ 60 | datafd = accept(connfd, NULL, NULL); 61 | if (datafd == -1 ) 62 | { 63 | perror("Accept error"); 64 | return 1; 65 | } 66 | printf("Client connected\n"); 67 | while(1) /* main loop */ 68 | { 69 | while(1) /* receive message, line by line */ 70 | { 71 | ret = read(datafd, buffer, MAXLEN); 72 | if ( ret == -1 ) 73 | { 74 | perror("Error reading line"); 75 | cleanUp(1); 76 | } 77 | else if ( ret == 0 ) 78 | { 79 | printf("Client disconnected\n"); 80 | cleanUp(1); 81 | } 82 | else 83 | { 84 | printf("Message: %s\n", buffer); 85 | break; 86 | } 87 | } 88 | /* write a confirmation message */ 89 | write(datafd, "Message received\n", 18); 90 | } 91 | return 0; 92 | } 93 | 94 | void cleanUp(int signum) 95 | { 96 | printf("Quitting and cleaning up\n"); 97 | close(connfd); 98 | close(datafd); 99 | unlink(sockname); 100 | exit(0); 101 | } 102 | -------------------------------------------------------------------------------- /ch10/write-memory.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #define DATASIZE 128 9 | 10 | int main(void) 11 | { 12 | int fd; 13 | float *addr; 14 | const char memid[] = "/my_memory"; 15 | const float numbers[3] = { 3.14, 2.718, 1.202}; 16 | /* create shared memory file descriptor */ 17 | if ( (fd = shm_open(memid, 18 | O_RDWR | O_CREAT, 0600)) == -1) 19 | { 20 | perror("Can't open memory fd"); 21 | return 1; 22 | } 23 | 24 | /* truncate memory to DATASIZE */ 25 | if ( (ftruncate(fd, DATASIZE)) == -1 ) 26 | { 27 | perror("Can't truncate memory"); 28 | return 1; 29 | } 30 | 31 | /* map memory using our file descriptor */ 32 | addr = mmap(NULL, DATASIZE, PROT_WRITE, 33 | MAP_SHARED, fd, 0); 34 | if (addr == MAP_FAILED) 35 | { 36 | perror("Memory mapping failed"); 37 | return 1; 38 | } 39 | 40 | /* copy data to memory */ 41 | memcpy(addr, numbers, sizeof(numbers)); 42 | 43 | /* wait for enter */ 44 | printf("Hit enter when finished "); 45 | getchar(); 46 | /* clean up */ 47 | munmap(addr, DATASIZE); 48 | shm_unlink(memid); 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /ch11/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-Wall -Wextra -pedantic -std=c99 3 | LDFLAGS=-lpthread 4 | %: %.c 5 | $(CC) $< $(CFLAGS) -o $@ $(LDFLAGS) 6 | -------------------------------------------------------------------------------- /ch11/cond-var.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void *isprime(void *arg); 8 | void *progress(void *arg); 9 | pthread_mutex_t lock; 10 | pthread_cond_t ready; 11 | pthread_t primeid = 0; 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | long long number1; 16 | long long number2; 17 | pthread_t tid_prime1; 18 | pthread_t tid_prime2; 19 | pthread_t tid_progress; 20 | pthread_attr_t threadattr; 21 | void *prime1Return; 22 | void *prime2Return; 23 | 24 | if ( (pthread_mutex_init(&lock, NULL)) != 0 ) 25 | { 26 | fprintf(stderr, 27 | "Couldn't initialize mutex\n"); 28 | return 1; 29 | } 30 | 31 | if ( (pthread_cond_init(&ready, NULL)) != 0 ) 32 | { 33 | fprintf(stderr, 34 | "Couldn't initialize condition variable\n"); 35 | return 1; 36 | } 37 | 38 | if ( argc != 3 ) 39 | { 40 | fprintf(stderr, "Please supply two numbers.\n" 41 | "Example: %s 9 7\n", argv[0]); 42 | return 1; 43 | } 44 | number1 = atoll(argv[1]); 45 | number2 = atoll(argv[2]); 46 | pthread_attr_init(&threadattr); 47 | pthread_create(&tid_progress, &threadattr, 48 | progress, NULL); 49 | pthread_detach(tid_progress); 50 | pthread_create(&tid_prime1, &threadattr, 51 | isprime, &number1); 52 | pthread_create(&tid_prime2, &threadattr, 53 | isprime, &number2); 54 | 55 | pthread_mutex_lock(&lock); 56 | for (int i = 0; i < 2; i++) 57 | { 58 | while (primeid == 0) 59 | pthread_cond_wait(&ready, &lock); 60 | if (primeid == tid_prime1) 61 | { 62 | pthread_join(tid_prime1, &prime1Return); 63 | if ( (uintptr_t)prime1Return == 1 ) 64 | printf("\n%lld is a prime number\n", 65 | number1); 66 | else 67 | printf("\n%lld is not a prime number\n", 68 | number1); 69 | primeid = 0; 70 | } 71 | else 72 | { 73 | pthread_join(tid_prime2, &prime2Return); 74 | if ( (uintptr_t)prime2Return == 1 ) 75 | printf("\n%lld is a prime number\n", 76 | number2); 77 | else 78 | printf("\n%lld is not a prime number\n", 79 | number2); 80 | primeid = 0; 81 | } 82 | } 83 | pthread_mutex_unlock(&lock); 84 | 85 | pthread_attr_destroy(&threadattr); 86 | if ( pthread_cancel(tid_progress) != 0 ) 87 | fprintf(stderr, 88 | "Couldn't cancel progress thread\n"); 89 | 90 | return 0; 91 | } 92 | 93 | void *isprime(void *arg) 94 | { 95 | long long int number = *((long long*)arg); 96 | long long int j; 97 | int prime = 1; 98 | 99 | for(j=2; j 2 | #include 3 | 4 | void *add(void *arg); 5 | long long int i = 0; 6 | pthread_mutex_t i_mutex; 7 | 8 | int main(void) 9 | { 10 | pthread_attr_t threadattr; 11 | pthread_attr_init(&threadattr); 12 | pthread_t tid_add1, tid_add2, tid_add3, 13 | tid_add4, tid_add5; 14 | 15 | if ( (pthread_mutex_init(&i_mutex, NULL)) != 0 ) 16 | { 17 | fprintf(stderr, 18 | "Couldn't initialize mutex\n"); 19 | return 1; 20 | } 21 | pthread_create(&tid_add1, &threadattr, 22 | add, NULL); 23 | pthread_create(&tid_add2, &threadattr, 24 | add, NULL); 25 | pthread_create(&tid_add3, &threadattr, 26 | add, NULL); 27 | pthread_create(&tid_add4, &threadattr, 28 | add, NULL); 29 | pthread_create(&tid_add5, &threadattr, 30 | add, NULL); 31 | 32 | pthread_join(tid_add1, NULL); 33 | pthread_join(tid_add2, NULL); 34 | pthread_join(tid_add3, NULL); 35 | pthread_join(tid_add4, NULL); 36 | pthread_join(tid_add5, NULL); 37 | 38 | printf("Sum is %lld\n", i); 39 | if ( (pthread_mutex_destroy(&i_mutex)) != 0 ) 40 | { 41 | fprintf(stderr, "Couldn't destroy mutex\n"); 42 | return 1; 43 | } 44 | return 0; 45 | } 46 | 47 | void *add(void *arg) 48 | { 49 | long long int j = 1; 50 | while(j < 1000000000) 51 | { 52 | j = j + 1; 53 | } 54 | 55 | pthread_mutex_lock(&i_mutex); 56 | i = i + j; 57 | pthread_mutex_unlock(&i_mutex); 58 | return NULL; 59 | } 60 | -------------------------------------------------------------------------------- /ch11/first-threaded.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void *isprime(void *arg); 7 | void *progress(void *arg); 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | long long number1; 12 | long long number2; 13 | pthread_t tid_prime1; 14 | pthread_t tid_prime2; 15 | pthread_t tid_progress; 16 | pthread_attr_t threadattr; 17 | if ( argc != 3 ) 18 | { 19 | fprintf(stderr, "Please supply two numbers.\n" 20 | "Example: %s 9 7\n", argv[0]); 21 | return 1; 22 | } 23 | number1 = atoll(argv[1]); 24 | number2 = atoll(argv[2]); 25 | pthread_attr_init(&threadattr); 26 | pthread_create(&tid_progress, &threadattr, 27 | progress, NULL); 28 | pthread_detach(tid_progress); 29 | pthread_create(&tid_prime1, &threadattr, 30 | isprime, &number1); 31 | pthread_create(&tid_prime2, &threadattr, 32 | isprime, &number2); 33 | pthread_join(tid_prime1, NULL); 34 | pthread_join(tid_prime2, NULL); 35 | pthread_attr_destroy(&threadattr); 36 | if ( pthread_cancel(tid_progress) != 0 ) 37 | fprintf(stderr, 38 | "Couldn't cancel progress thread\n"); 39 | printf("Done!\n"); 40 | return 0; 41 | } 42 | 43 | void *isprime(void *arg) 44 | { 45 | long long int number = *((long long*)arg); 46 | long long int j; 47 | int prime = 1; 48 | 49 | /* Test if the number is divisible, starting 50 | * from 2 */ 51 | for(j=2; j 2 | #include 3 | 4 | void *add(void *arg); 5 | long long int i = 0; 6 | pthread_mutex_t i_mutex; 7 | 8 | int main(void) 9 | { 10 | pthread_attr_t threadattr; 11 | pthread_attr_init(&threadattr); 12 | pthread_t tid_add1, tid_add2, tid_add3, 13 | tid_add4, tid_add5; 14 | 15 | if ( (pthread_mutex_init(&i_mutex, NULL)) != 0 ) 16 | { 17 | fprintf(stderr, 18 | "Couldn't initialize mutex\n"); 19 | return 1; 20 | } 21 | pthread_create(&tid_add1, &threadattr, 22 | add, NULL); 23 | pthread_create(&tid_add2, &threadattr, 24 | add, NULL); 25 | pthread_create(&tid_add3, &threadattr, 26 | add, NULL); 27 | pthread_create(&tid_add4, &threadattr, 28 | add, NULL); 29 | pthread_create(&tid_add5, &threadattr, 30 | add, NULL); 31 | 32 | pthread_join(tid_add1, NULL); 33 | pthread_join(tid_add2, NULL); 34 | pthread_join(tid_add3, NULL); 35 | pthread_join(tid_add4, NULL); 36 | pthread_join(tid_add5, NULL); 37 | 38 | printf("Sum is %lld\n", i); 39 | if ( (pthread_mutex_destroy(&i_mutex)) != 0 ) 40 | { 41 | fprintf(stderr, "Couldn't destroy mutex\n"); 42 | return 1; 43 | } 44 | return 0; 45 | } 46 | 47 | void *add(void *arg) 48 | { 49 | for (long long int j = 1; j <= 1000000000; j++) 50 | { 51 | pthread_mutex_lock(&i_mutex); 52 | i = i + 1; 53 | pthread_mutex_unlock(&i_mutex); 54 | } 55 | return NULL; 56 | } 57 | -------------------------------------------------------------------------------- /ch11/race.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void *add(void *arg); 5 | long long int i = 0; 6 | 7 | int main(void) 8 | { 9 | pthread_attr_t threadattr; 10 | pthread_attr_init(&threadattr); 11 | pthread_t tid_add1, tid_add2, tid_add3, 12 | tid_add4, tid_add5; 13 | 14 | pthread_create(&tid_add1, &threadattr, 15 | add, NULL); 16 | pthread_create(&tid_add2, &threadattr, 17 | add, NULL); 18 | pthread_create(&tid_add3, &threadattr, 19 | add, NULL); 20 | pthread_create(&tid_add4, &threadattr, 21 | add, NULL); 22 | pthread_create(&tid_add5, &threadattr, 23 | add, NULL); 24 | 25 | pthread_join(tid_add1, NULL); 26 | pthread_join(tid_add2, NULL); 27 | pthread_join(tid_add3, NULL); 28 | pthread_join(tid_add4, NULL); 29 | pthread_join(tid_add5, NULL); 30 | 31 | printf("Sum is %lld\n", i); 32 | return 0; 33 | } 34 | 35 | void *add(void *arg) 36 | { 37 | for (long long int j = 1; j <= 1000000000; j++) 38 | { 39 | i = i + 1; 40 | } 41 | return NULL; 42 | } 43 | -------------------------------------------------------------------------------- /ch11/second-threaded.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void *isprime(void *arg); 8 | void *progress(void *arg); 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | long long number1; 13 | long long number2; 14 | pthread_t tid_prime1; 15 | pthread_t tid_prime2; 16 | pthread_t tid_progress; 17 | pthread_attr_t threadattr; 18 | void *prime1Return; 19 | void *prime2Return; 20 | if ( argc != 3 ) 21 | { 22 | fprintf(stderr, "Please supply two numbers.\n" 23 | "Example: %s 9 7\n", argv[0]); 24 | return 1; 25 | } 26 | number1 = atoll(argv[1]); 27 | number2 = atoll(argv[2]); 28 | pthread_attr_init(&threadattr); 29 | pthread_create(&tid_progress, &threadattr, 30 | progress, NULL); 31 | pthread_detach(tid_progress); 32 | pthread_create(&tid_prime1, &threadattr, 33 | isprime, &number1); 34 | pthread_create(&tid_prime2, &threadattr, 35 | isprime, &number2); 36 | 37 | pthread_join(tid_prime1, &prime1Return); 38 | if ( (uintptr_t)prime1Return == 1 ) 39 | printf("\n%lld is a prime number\n", 40 | number1); 41 | else 42 | printf("\n%lld is not a prime number\n", 43 | number1); 44 | 45 | pthread_join(tid_prime2, &prime2Return); 46 | if ( (uintptr_t)prime2Return == 1 ) 47 | printf("\n%lld is a prime number\n", 48 | number2); 49 | else 50 | printf("\n%lld is not a prime number\n", 51 | number2); 52 | 53 | pthread_attr_destroy(&threadattr); 54 | if ( pthread_cancel(tid_progress) != 0 ) 55 | fprintf(stderr, 56 | "Couldn't cancel progress thread\n"); 57 | 58 | return 0; 59 | } 60 | 61 | void *isprime(void *arg) 62 | { 63 | long long int number = *((long long*)arg); 64 | long long int j; 65 | int prime = 1; 66 | 67 | /* Test if the number is divisible, starting 68 | * from 2 */ 69 | for(j=2; j 2 | #include 3 | float area(float radius); 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | float number; 8 | float answer; 9 | if (argc != 2) 10 | { 11 | fprintf(stderr, "Type the radius of a " 12 | "circle\n"); 13 | return 1; 14 | } 15 | number = atof(argv[1]); 16 | answer = area(number); 17 | printf("The area of a circle with a radius of " 18 | "%.2f is %.2f\n", number, answer); 19 | return 0; 20 | } 21 | 22 | float area(float radius) 23 | { 24 | static float pi = 3.14159; 25 | return pi*radius*radius; 26 | } 27 | -------------------------------------------------------------------------------- /ch12/first-threaded.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void *isprime(void *arg); 7 | void *progress(void *arg); 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | long long number1; 12 | long long number2; 13 | pthread_t tid_prime1; 14 | pthread_t tid_prime2; 15 | pthread_t tid_progress; 16 | pthread_attr_t threadattr; 17 | if ( argc != 3 ) 18 | { 19 | fprintf(stderr, "Please supply two numbers.\n" 20 | "Example: %s 9 7\n", argv[0]); 21 | return 1; 22 | } 23 | number1 = atoll(argv[1]); 24 | number2 = atoll(argv[2]); 25 | pthread_attr_init(&threadattr); 26 | pthread_create(&tid_progress, &threadattr, 27 | progress, NULL); 28 | pthread_create(&tid_prime1, &threadattr, 29 | isprime, &number1); 30 | pthread_create(&tid_prime2, &threadattr, 31 | isprime, &number2); 32 | pthread_join(tid_prime1, NULL); 33 | pthread_join(tid_prime2, NULL); 34 | pthread_attr_destroy(&threadattr); 35 | printf("Done!\n"); 36 | return 0; 37 | } 38 | 39 | void *isprime(void *arg) 40 | { 41 | long long int number = *((long long*)arg); 42 | long long int j; 43 | int prime = 1; 44 | 45 | /* Test if the number is divisible, starting 46 | * from 2 */ 47 | for(j=2; j 2 | #include 3 | #include 4 | #include 5 | 6 | int main(void) 7 | { 8 | pid_t pid; 9 | printf("My PID is %d\n", getpid()); 10 | /* fork, save the PID, and check for errors */ 11 | if ( (pid = fork()) == -1 ) 12 | { 13 | perror("Can't fork"); 14 | return 1; 15 | } 16 | if (pid == 0) 17 | { 18 | /* if pid is 0 we are in the child process */ 19 | printf("Hello from the child process!\n"); 20 | for(int i = 0; i<10; i++) 21 | { 22 | printf("Counter in child: %d\n", i); 23 | } 24 | } 25 | else if(pid > 0) 26 | { 27 | /* parent process */ 28 | printf("My child has PID %d\n", pid); 29 | wait(&pid); 30 | } 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /ch12/leak.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(void) 6 | { 7 | char *c; 8 | c = calloc(sizeof(char), 20); 9 | strcpy(c, "Hello!"); 10 | printf("%s\n", c); 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /ch12/loop.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | int x; 6 | int y = 5; 7 | char text[20] = "Hello, world"; 8 | for (x = 1; y < 100; x++) 9 | { 10 | y = (y*3)-x; 11 | } 12 | printf("%s\n", text); 13 | printf("y = %d\n", y); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /ch12/memtest.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | int main(void) 4 | { 5 | char text[20]; 6 | memset(text, 'x', 20); 7 | strcpy(text, "Hello"); 8 | printf("%s\n", text); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /ch12/overflow.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(void) 6 | { 7 | char *c; 8 | c = calloc(sizeof(char), 20); 9 | strcpy(c, "Hello, how are you doing?"); 10 | printf("%s\n", c); 11 | free(c); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /ch2/ascii-table.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | char c; 6 | for (c = 65; c<=90; c++) 7 | { 8 | printf("%c = %d ", c, c); /* upper case */ 9 | printf("%c = %d\n", c+32, c+32); /* lower case */ 10 | } 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /ch2/ascii-table.md: -------------------------------------------------------------------------------- 1 | # ASCII-table 2 | 3 | | DEC | OCT | HEX | BIN | Character | 4 | |------|------|------|----------|----------------------------| 5 | | 0 | 000 | 00 | 00000000 | NUL (null) | 6 | | 1 | 001 | 01 | 00000001 | SOH (start of heading) | 7 | | 2 | 002 | 02 | 00000010 | STX (start of text) | 8 | | 3 | 003 | 03 | 00000011 | ETX (end of text) | 9 | | 4 | 004 | 04 | 00000100 | EOT (end of transmission | 10 | | 5 | 005 | 05 | 00000101 | ENQ (enquiry) | 11 | | 6 | 006 | 06 | 00000110 | ACK (acknowledge | 12 | | 7 | 007 | 07 | 00000111 | BEL (bell) | 13 | | 8 | 010 | 08 | 00001000 | BS (backspace) | 14 | | 9 | 011 | 09 | 00001001 | HT (horzintal tab) | 15 | | 10 | 012 | 0A | 00001010 | LF (line feed / newline) | 16 | | 11 | 013 | 0B | 00001011 | VT (vertical tab) | 17 | | 12 | 014 | 0C | 00001100 | FF (form feed / new page) | 18 | | 13 | 015 | 0D | 00001101 | CR (carriage return) | 19 | | 14 | 016 | 0E | 00001110 | SO (shift out) | 20 | | 15 | 017 | 0F | 00001111 | SI (shift in) | 21 | | 16 | 020 | 10 | 00010000 | DLE (data link escape) | 22 | | 17 | 021 | 11 | 00010001 | DC1 (device control 1) | 23 | | 18 | 022 | 12 | 00010010 | DC2 (device control 2) | 24 | | 19 | 023 | 13 | 00010011 | DC3 (device control 3) | 25 | | 20 | 024 | 14 | 00010100 | DC4 (device control 4) | 26 | | 21 | 025 | 15 | 00010101 | NAK (negative acknowledge) | 27 | | 22 | 026 | 16 | 00010110 | SYN (synchrnous idle) | 28 | | 23 | 027 | 17 | 00010111 | ETB (end of trans. block) | 29 | | 24 | 030 | 18 | 00011000 | CAN (cancel) | 30 | | 25 | 031 | 19 | 00011001 | EM (end of medium) | 31 | | 26 | 032 | 1A | 00011010 | SUB (substitute) | 32 | | 27 | 033 | 1B | 00011011 | ESC (esacpe) | 33 | | 28 | 034 | 1C | 00011100 | FS (file separator) | 34 | | 29 | 035 | 1D | 00011101 | GS (group separator) | 35 | | 30 | 036 | 1E | 00011110 | RS (record separator) | 36 | | 31 | 037 | 1F | 00011111 | US (unit separator) | 37 | | 32 | 040 | 20 | 00100000 | (space) | 38 | | 33 | 041 | 21 | 00100001 | ! | 39 | | 34 | 042 | 22 | 00100010 | " | 40 | | 35 | 043 | 23 | 00100011 | # | 41 | | 36 | 044 | 24 | 00100100 | $ | 42 | | 37 | 045 | 25 | 00100101 | % | 43 | | 38 | 046 | 26 | 00100110 | & | 44 | | 39 | 047 | 27 | 00100111 | ' | 45 | | 40 | 050 | 28 | 00101000 | ( | 46 | | 41 | 051 | 29 | 00101001 | ) | 47 | | 42 | 052 | 2A | 00101010 | * | 48 | | 43 | 053 | 2B | 00101011 | + | 49 | | 44 | 054 | 2C | 00101100 | , | 50 | | 45 | 055 | 2D | 00101101 | - | 51 | | 46 | 056 | 2E | 00101110 | . | 52 | | 47 | 057 | 2F | 00101111 | / | 53 | | 48 | 060 | 30 | 00110000 | 0 | 54 | | 49 | 061 | 31 | 00110001 | 1 | 55 | | 50 | 062 | 32 | 00110010 | 2 | 56 | | 51 | 063 | 33 | 00110011 | 3 | 57 | | 52 | 064 | 34 | 00110100 | 4 | 58 | | 53 | 065 | 35 | 00110101 | 5 | 59 | | 54 | 066 | 36 | 00110110 | 6 | 60 | | 55 | 067 | 37 | 00110111 | 7 | 61 | | 56 | 070 | 38 | 00111000 | 8 | 62 | | 57 | 071 | 39 | 00111001 | 9 | 63 | | 58 | 072 | 3A | 00111010 | : | 64 | | 59 | 073 | 3B | 00111011 | ; | 65 | | 60 | 074 | 3C | 00111100 | < | 66 | | 61 | 075 | 3D | 00111101 | = | 67 | | 62 | 076 | 3E | 00111110 | > | 68 | | 63 | 077 | 3F | 00111111 | ? | 69 | | 64 | 100 | 40 | 01000000 | @ | 70 | | 65 | 101 | 41 | 01000001 | A | 71 | | 66 | 102 | 42 | 01000010 | B | 72 | | 67 | 103 | 43 | 01000011 | C | 73 | | 68 | 104 | 44 | 01000100 | D | 74 | | 69 | 105 | 45 | 01000101 | E | 75 | | 70 | 106 | 46 | 01000110 | F | 76 | | 71 | 107 | 47 | 01000111 | G | 77 | | 72 | 110 | 48 | 01001000 | H | 78 | | 73 | 111 | 49 | 01001001 | I | 79 | | 74 | 112 | 4A | 01001010 | J | 80 | | 75 | 113 | 4B | 01001011 | K | 81 | | 76 | 114 | 4C | 01001100 | L | 82 | | 77 | 115 | 4D | 01001101 | M | 83 | | 78 | 116 | 4E | 01001110 | N | 84 | | 79 | 117 | 4F | 01001111 | O | 85 | | 80 | 120 | 50 | 01010000 | P | 86 | | 81 | 121 | 51 | 01010001 | Q | 87 | | 82 | 122 | 52 | 01010010 | R | 88 | | 83 | 123 | 53 | 01010011 | S | 89 | | 84 | 124 | 54 | 01010100 | T | 90 | | 85 | 125 | 55 | 01010101 | U | 91 | | 86 | 126 | 56 | 01010110 | V | 92 | | 87 | 127 | 57 | 01010111 | W | 93 | | 88 | 130 | 58 | 01011000 | X | 94 | | 89 | 131 | 59 | 01011001 | Y | 95 | | 90 | 132 | 5A | 01011010 | Z | 96 | | 91 | 133 | 5B | 01011011 | \[ | 97 | | 92 | 134 | 5C | 01011100 | \\ | 98 | | 93 | 135 | 5D | 01011101 | \] | 99 | | 94 | 136 | 5E | 01011110 | ^ | 100 | | 95 | 137 | 5F | 01011111 | _ | 101 | | 96 | 140 | 60 | 01100000 | ` | 102 | | 97 | 141 | 61 | 01100001 | a | 103 | | 98 | 142 | 62 | 01100010 | b | 104 | | 99 | 143 | 63 | 01100011 | c | 105 | | 100 | 144 | 64 | 01100100 | d | 106 | | 101 | 145 | 65 | 01100101 | e | 107 | | 102 | 146 | 66 | 01100110 | f | 108 | | 103 | 147 | 67 | 01100111 | g | 109 | | 104 | 150 | 68 | 01101000 | h | 110 | | 105 | 151 | 69 | 01101001 | i | 111 | | 106 | 152 | 6A | 01101010 | j | 112 | | 107 | 153 | 6B | 01101011 | k | 113 | | 108 | 154 | 6C | 01101100 | l | 114 | | 109 | 155 | 6D | 01101101 | m | 115 | | 110 | 156 | 6E | 01101110 | n | 116 | | 111 | 157 | 6F | 01101111 | o | 117 | | 112 | 160 | 70 | 01110000 | p | 118 | | 113 | 161 | 71 | 01110001 | q | 119 | | 114 | 162 | 72 | 01110010 | r | 120 | | 115 | 163 | 73 | 01110011 | s | 121 | | 116 | 164 | 74 | 01110100 | t | 122 | | 117 | 165 | 75 | 01110101 | u | 123 | | 118 | 166 | 76 | 01110110 | v | 124 | | 119 | 167 | 77 | 01110111 | w | 125 | | 120 | 170 | 78 | 01111000 | x | 126 | | 121 | 171 | 79 | 01111001 | y | 127 | | 122 | 172 | 7A | 01111010 | z | 128 | | 123 | 173 | 7B | 01111011 | { | 129 | | 124 | 174 | 7C | 01111100 | \| | 130 | | 125 | 175 | 7D | 01111101 | } | 131 | | 126 | 176 | 7E | 01111110 | ~ | 132 | | 127 | 177 | 7F | 01111111 | DEL (delete) | 133 | -------------------------------------------------------------------------------- /ch2/avg-with-garbage.txt: -------------------------------------------------------------------------------- 1 | 10-minute average: 61 mph 2 | 30-minute average: 55 mph 3 | 45-minute average: 54 mph 4 | 60-minute average: 52 mph 5 | 90-minute average: 52 mph 6 | 99-minute average: nn mph 7 | 120-minute average: 49 mph 8 | 160-minute average: 47 mph 9 | 180-minute average: nn mph 10 | error reading data from interface 11 | 200-minute average: 43 mph 12 | -------------------------------------------------------------------------------- /ch2/avg.txt: -------------------------------------------------------------------------------- 1 | 10-minute average: 61 mph 2 | 30-minute average: 55 mph 3 | 45-minute average: 54 mph 4 | 60-minute average: 52 mph 5 | 90-minute average: 52 mph 6 | 99-minute average: nn mph 7 | -------------------------------------------------------------------------------- /ch2/case-changer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(void) 5 | { 6 | char c[20] = { 0 }; 7 | char newcase[20] = { 0 }; 8 | int i; 9 | while(fgets(c, sizeof(c), stdin) != NULL) 10 | { 11 | for(i=0; i<=sizeof(c); i++) 12 | { 13 | /* Upper case to lower case */ 14 | if ( (c[i] >= 65) && (c[i] <= 90) ) 15 | { 16 | newcase[i] = c[i] + 32; 17 | } 18 | /* Lower case to upper case */ 19 | if ( (c[i] >= 97 && c[i] <= 122) ) 20 | { 21 | newcase[i] = c[i] - 32; 22 | } 23 | } 24 | printf("%s\n", newcase); 25 | /* zero out the arrays so there are no 26 | left-overs in the next run */ 27 | memset(c, 0, sizeof(c)); 28 | memset(newcase, 0, sizeof(newcase)); 29 | } 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /ch2/env-var-set.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200112L 2 | #include 3 | #include 4 | 5 | int main(void) 6 | { 7 | setenv("FULLNAME", "Jack-Benny", 1); 8 | printf("Your full name is %s\n", getenv("FULLNAME")); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /ch2/env-var.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(void) 6 | { 7 | /* Using getenv() to fetch env. variables */ 8 | printf("Your username is %s\n", getenv("USER")); 9 | printf("Your home directory is %s\n", 10 | getenv("HOME")); 11 | printf("Your preferred editor is %s\n", 12 | getenv("EDITOR")); 13 | printf("Your shell is %s\n", getenv("SHELL")); 14 | 15 | /* Check if the current terminal support colors*/ 16 | if ( strstr(getenv("TERM"), "256color") ) 17 | { 18 | /* Color the output with \033 + colorcode */ 19 | printf("\033[0;31mYour \033[0;32mterminal " 20 | "\033[0;35msupport " 21 | "\033[0;33mcolors\033[0m\n"); 22 | } 23 | else 24 | { 25 | printf("Your terminal doesn't support" 26 | " colors\n"); 27 | } 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /ch2/exist.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Check if the user supplied exactly one argument 4 | if [ "$#" -ne 1 ]; then 5 | echo "You must supply exactly one argument." 6 | echo "Example: $0 /etc" 7 | exit 1 # Return with value 1 8 | fi 9 | 10 | # Check if the file/directory exists 11 | test -e "$1" # Perform the actual test 12 | if [ "$?" -eq 0 ]; then 13 | echo "File or directory exists" 14 | elif [ "$?" -eq 1 ]; then 15 | echo "File or directory does not exist" 16 | exit 3 # Return with a special code so other 17 | # programs can use the value to see if a 18 | # file dosen't exist 19 | else 20 | echo "Unknown return value from test..." 21 | exit 1 # Unknown error occured, so exit with 1 22 | fi 23 | exit 0 # If the file or directory exists, we exit 24 | # with 0 25 | 26 | -------------------------------------------------------------------------------- /ch2/functions_ver1.c: -------------------------------------------------------------------------------- 1 | #include 2 | int func1(void); 3 | int func2(void); 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | printf("Inside main\n"); 8 | printf("Calling function one\n"); 9 | if (func1()) 10 | { 11 | printf("Everything ok from function one\n"); 12 | printf("Return with 0 from main - all ok\n"); 13 | return 0; 14 | } 15 | else 16 | { 17 | printf("Caught an error from function one\n"); 18 | printf("Return with 1 from main - error\n"); 19 | return 1; 20 | } 21 | return 0; /* We shouldn't reach this, but 22 | just in case */ 23 | } 24 | 25 | int func1(void) 26 | { 27 | printf("Inside function one\n"); 28 | printf("Calling function two\n"); 29 | if (func2()) 30 | { 31 | printf("Everything ok from function two\n"); 32 | return 1; 33 | } 34 | else 35 | { 36 | printf("Caught an error from function two\n"); 37 | return 0; 38 | } 39 | } 40 | 41 | int func2(void) 42 | { 43 | printf("Inside function two\n"); 44 | printf("Returning with 0 (error) from " 45 | "function two\n"); 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /ch2/functions_ver2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | int func1(void); 4 | int func2(void); 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | printf("Inside main\n"); 9 | printf("Calling function one\n"); 10 | if (func1()) 11 | { 12 | printf("Everything ok from function one\n"); 13 | printf("Return with 0 from main - all ok\n"); 14 | return 0; 15 | } 16 | else 17 | { 18 | printf("Caught an error from funtcion one\n"); 19 | printf("Return with 1 from main - error\n"); 20 | return 1; 21 | } 22 | return 0; /* We shouldn't reach this, but just 23 | in case */ 24 | } 25 | 26 | int func1(void) 27 | { 28 | printf("Inside function one\n"); 29 | printf("Calling function two\n"); 30 | if (func2()) 31 | { 32 | printf("Everything ok from function two\n"); 33 | exit(0); 34 | } 35 | else 36 | { 37 | printf("Caught an error from function two\n"); 38 | exit(1); 39 | } 40 | } 41 | 42 | int func2(void) 43 | { 44 | printf("Inside function two\n"); 45 | printf("Returning with (error) from " 46 | "function two\n"); 47 | exit(1); 48 | } 49 | -------------------------------------------------------------------------------- /ch2/mph-to-kph.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(void) 6 | { 7 | char mph[10] = { 0 }; 8 | 9 | while(fgets(mph, sizeof(mph), stdin) != NULL) 10 | { 11 | /* Check if mph is numeric 12 | * (and do conversion) */ 13 | if( strspn(mph, "0123456789.-\n") == 14 | strlen(mph) ) 15 | { 16 | printf("%.1f\n", (atof(mph)*1.60934) ); 17 | } 18 | /* If mph is NOT numeric, print error 19 | * and return */ 20 | else 21 | { 22 | fprintf(stderr, "Found non-numeric" 23 | " value\n"); 24 | return 1; 25 | } 26 | } 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /ch2/mph-to-kph_v2.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 500 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void printHelp(FILE *stream, char progname[]); 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | char mph[10] = { 0 }; 12 | int opt; 13 | int cont = 0; 14 | 15 | /* Parse command-line options */ 16 | while ((opt = getopt(argc, argv, "ch")) != -1) 17 | { 18 | switch(opt) 19 | { 20 | case 'h': 21 | printHelp(stdout, argv[0]); 22 | return 0; 23 | case 'c': 24 | cont = 1; 25 | break; 26 | default: 27 | printHelp(stderr, argv[0]); 28 | return 1; 29 | } 30 | } 31 | 32 | while(fgets(mph, sizeof(mph), stdin) != NULL) 33 | { 34 | /* Check if mph is numeric 35 | * (and do conversion) */ 36 | if( strspn(mph, "0123456789.-\n") == 37 | strlen(mph) ) 38 | { 39 | printf("%.1f\n", (atof(mph)*1.60934) ); 40 | } 41 | /* If mph is NOT numeric, print error 42 | * and return */ 43 | else 44 | { 45 | fprintf(stderr, "Found non-numeric " 46 | "value\n"); 47 | if (cont == 1) /* Check if -c is set */ 48 | { 49 | continue; /* Skip and continue if 50 | * -c is set */ 51 | } 52 | else 53 | { 54 | return 1; /* Abort if -c is not set */ 55 | } 56 | } 57 | } 58 | return 0; 59 | } 60 | 61 | void printHelp(FILE *stream, char progname[]) 62 | { 63 | fprintf(stream, "%s [-c] [-h]\n", progname); 64 | fprintf(stream, " -c continues even though a non" 65 | "-numeric value was detected in the input\n" 66 | " -h print help\n"); 67 | } 68 | -------------------------------------------------------------------------------- /ch2/output.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | 4 | int main(void) 5 | { 6 | printf("A regular message on stdout\n"); 7 | 8 | /* Using streams with fprintf() */ 9 | fprintf(stdout, "Also a regular message on " 10 | "stdout\n"); 11 | fprintf(stderr, "An error message on stderr\n"); 12 | 13 | /* Using file descriptors with dprintf(). 14 | * This requires _POSIX_C_SOURCE 200809L 15 | * (man 3 dprintf)*/ 16 | dprintf(1, "A regular message, printed to " 17 | "fd 1\n"); 18 | dprintf(2, "An error message, printed to " 19 | "fd 2\n"); 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /ch3/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-Wall -Wextra -pedantic -std=c99 3 | -------------------------------------------------------------------------------- /ch3/area/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-std=c99 -Wall -Wextra -pedantic 3 | LIBS=-lm 4 | OBJS=area.o help.o rectangle.o triangle.o circle.o 5 | DEPS=area.h 6 | bindir=/usr/local/bin 7 | 8 | area: $(OBJS) 9 | $(CC) -o area $(OBJS) $(LIBS) 10 | 11 | area.o: $(DEPS) 12 | 13 | clean: 14 | rm area $(OBJS) 15 | 16 | install: area 17 | install -g root -o root area $(bindir)/area 18 | 19 | uninstall: $(bindir)/area 20 | rm $(bindir)/area 21 | -------------------------------------------------------------------------------- /ch3/area/area.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 700 2 | #include 3 | #include 4 | #include "area.h" 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | int opt; 9 | /* Sanity check number of options */ 10 | if (argc != 2) 11 | { 12 | printHelp(stderr, argv[0]); 13 | return 1; 14 | } 15 | /* Parse command-line options */ 16 | while ((opt = getopt(argc, argv, "crth")) != -1) 17 | { 18 | switch(opt) 19 | { 20 | case 'c': 21 | if (circle() == -1) 22 | { 23 | printHelp(stderr, argv[0]); 24 | return 1; 25 | } 26 | break; 27 | case 'r': 28 | if (rectangle() == -1) 29 | { 30 | printHelp(stderr, argv[0]); 31 | return 1; 32 | } 33 | break; 34 | case 't': 35 | if (triangle() == -1) 36 | { 37 | printHelp(stderr, argv[0]); 38 | return 1; 39 | } 40 | break; 41 | case 'h': 42 | printHelp(stdout, argv[0]); 43 | return 0; 44 | default: 45 | printHelp(stderr, argv[0]); 46 | return 1; 47 | } 48 | } 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /ch3/area/area.h: -------------------------------------------------------------------------------- 1 | void printHelp(FILE *stream, char progname[]); 2 | int circle(void); 3 | int rectangle(void); 4 | int triangle(void); 5 | -------------------------------------------------------------------------------- /ch3/area/circle.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 700 2 | #include 3 | #include 4 | 5 | int circle(void) 6 | { 7 | float radius; 8 | printf("Enter the radius of the circle: "); 9 | if (scanf("%f", &radius)) 10 | { 11 | printf("%.3f\n", M_PI*pow(radius, 2)); 12 | return 1; 13 | } 14 | else 15 | { 16 | return -1; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ch3/area/help.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void printHelp(FILE *stream, char progname[]) 4 | { 5 | fprintf(stream, "\nUsage: %s [-c] [-t] [-r] " 6 | "[-h]\n" 7 | "-c calculates the area of a circle\n" 8 | "-t calculates the area of a triangle\n" 9 | "-r calculates the area of a rectangle\n" 10 | "-h shows this help\n" 11 | "Example: %s -t\n" 12 | "Enter the height and width of the " 13 | "triangle: 5 9\n" 14 | "22.500\n", progname, progname); 15 | } 16 | -------------------------------------------------------------------------------- /ch3/area/rectangle.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int rectangle(void) 4 | { 5 | float length, width; 6 | printf("Enter the length and width of " 7 | "the rectangle: "); 8 | if (scanf("%f %f", &length, &width)) 9 | { 10 | printf("%.3f\n", length*width); 11 | return 1; 12 | } 13 | else 14 | { 15 | return -1; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ch3/area/triangle.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int triangle(void) 4 | { 5 | float height, width; 6 | printf("Enter the height and width of " 7 | "the triangle: "); 8 | if (scanf("%f %f", &height, &width)) 9 | { 10 | printf("%.3f\n", height*width/2); 11 | return 1; 12 | } 13 | else 14 | { 15 | return -1; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ch3/circumference.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #define PI 3.14159 5 | 6 | int main(void) 7 | { 8 | char radius[20] = { 0 }; 9 | 10 | while(fgets(radius, sizeof(radius), stdin) 11 | != NULL) 12 | { 13 | /* Check if radius is numeric 14 | * (and do conversion) */ 15 | if( strspn(radius,"0123456789.\n") == 16 | strlen(radius) ) 17 | { 18 | printf("%.5f\n", PI*(atof(radius)*2) ); 19 | } 20 | /* If radius is NOT numeric, print error 21 | * and return */ 22 | else 23 | { 24 | fprintf(stderr, "Found non-numeric " 25 | "value\n"); 26 | return 1; 27 | } 28 | } 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /ch3/comments.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | // A C99 comment 6 | printf("hello, world\n"); 7 | return 0; 8 | } -------------------------------------------------------------------------------- /ch3/cube/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-Wall -Wextra -pedantic -std=c99 3 | 4 | cube: cube.h cube-prog.c cube-func.c 5 | $(CC) $(CFLAGS) -o cube cube-prog.c cube-func.c 6 | -------------------------------------------------------------------------------- /ch3/cube/cube-func.c: -------------------------------------------------------------------------------- 1 | int cube(int n) 2 | { 3 | return n*n*n; 4 | } 5 | -------------------------------------------------------------------------------- /ch3/cube/cube-prog.c: -------------------------------------------------------------------------------- 1 | #include "cube.h" 2 | #define NUMBER 4 3 | 4 | int main(void) 5 | { 6 | return cube(NUMBER); 7 | } 8 | -------------------------------------------------------------------------------- /ch3/cube/cube.h: -------------------------------------------------------------------------------- 1 | int cube(int n); 2 | -------------------------------------------------------------------------------- /ch3/for-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | for (int i = 10; i>0; i--) 6 | { 7 | printf("%d\n", i); 8 | } 9 | return 0; 10 | } -------------------------------------------------------------------------------- /ch3/interest.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(void) 5 | { 6 | int years = 15; /* The number of years you will 7 | * keep the money in the bank 8 | * account */ 9 | int savings = 99000; /* The inital amount */ 10 | float interest = 1.5; /* The interest in % */ 11 | 12 | printf("The total savings after %d years " 13 | "is %.2f\n", years, 14 | savings * pow(1+(interest/100), years)); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /ch3/is-it-a-prime.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "prime.h" 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | long int num; 9 | /* Only one argument is accepted */ 10 | if (argc != 2) 11 | { 12 | fprintf(stderr, "Usage: %s number\n", 13 | argv[0]); 14 | return 1; 15 | } 16 | /* Only numbers 0-9 are accepted */ 17 | if ( strspn(argv[1], "0123456789") != 18 | strlen(argv[1]) ) 19 | { 20 | fprintf(stderr, "Only numeric values are " 21 | "accepted\n"); 22 | return 1; 23 | } 24 | num = atol(argv[1]); /* String to long */ 25 | if (isprime(num)) /* Check if num is a prime */ 26 | { 27 | printf("%ld is a prime\n", num); 28 | } 29 | else 30 | { 31 | printf("%ld is not a prime\n", num); 32 | } 33 | 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /ch3/libprime.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Linux-System-Programming-Techniques/c1171207785323b14423961e935d95becab6486a/ch3/libprime.so -------------------------------------------------------------------------------- /ch3/my-sys.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(void) 7 | { 8 | char cwd[100] = { 0 }; /* for current dir */ 9 | struct sysinfo si; /* for system information */ 10 | 11 | getcwd(cwd, 100); /* get current working dir */ 12 | sysinfo(&si); /* get system information 13 | * (linux only) */ 14 | 15 | printf("Your user ID is %d\n", getuid()); 16 | printf("Your effective user ID is %d\n", 17 | geteuid()); 18 | printf("Your current working directory is %s\n", 19 | cwd); 20 | printf("Your machine has %ld megabytes of " 21 | "total RAM\n", si.totalram / 1024 / 1024); 22 | printf("Your machine has %ld megabytes of " 23 | "free RAM\n", si.freeram / 1024 / 1024); 24 | printf("Currently, there are %d processes " 25 | "running\n", si.procs); 26 | printf("This process ID is %d\n", getpid()); 27 | printf("The parent process ID is %d\n", 28 | getppid()); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /ch3/no-return.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | printf("Hello, world\n"); 6 | } 7 | -------------------------------------------------------------------------------- /ch3/prime.c: -------------------------------------------------------------------------------- 1 | int isprime(long int number) 2 | { 3 | long int j; 4 | int prime = 1; 5 | 6 | /* Test if the number is divisible, starting 7 | * from 2 */ 8 | for(j=2; j 2 | #include 3 | 4 | int main(void) 5 | { 6 | char a[] = "Hello"; 7 | char *b; 8 | b = strdup(a); 9 | printf("b = %s\n", b); 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /ch3/sys-write.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | write(1, "hello, world\n", 13); 6 | return 0; 7 | } -------------------------------------------------------------------------------- /ch3/which-c.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | #ifdef __STDC_VERSION__ 6 | printf("Standard C version: %ld\n", 7 | __STDC_VERSION__); 8 | #endif 9 | #ifdef _XOPEN_SOURCE 10 | printf("XOPEN_SOURCE: %d\n", 11 | _XOPEN_SOURCE); 12 | #endif 13 | #ifdef _POSIX_C_SOURCE 14 | printf("POSIX_C_SOURCE: %ld\n", 15 | _POSIX_C_SOURCE); 16 | #endif 17 | #ifdef _GNU_SOURCE 18 | printf("GNU_SOURCE: %d\n", 19 | _GNU_SOURCE); 20 | #endif 21 | #ifdef _BSD_SOURCE 22 | printf("BSD_SOURCE: %d\n", _BSD_SOURCE); 23 | #endif 24 | #ifdef _DEFAULT_SOURCE 25 | printf("DEFAULT_SOURCE: %d\n", 26 | _DEFAULT_SOURCE); 27 | #endif 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /ch3/write-chars.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | fputs("hello, world\n", stdout); 6 | return 0; 7 | } -------------------------------------------------------------------------------- /ch4/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-Wall -Wextra -pedantic -std=c99 3 | -------------------------------------------------------------------------------- /ch4/huge-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(void) 5 | { 6 | int number = 9999; 7 | double answer; 8 | if ( (answer = pow(number, number)) == HUGE_VAL ) 9 | { 10 | fprintf(stderr, "A huge value\n"); 11 | return 1; 12 | } 13 | else 14 | { 15 | printf("%lf\n", answer); 16 | } 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /ch4/simple-touch-v1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | char filename[PATH_MAX] = { 0 }; 9 | if (argc != 2) 10 | { 11 | fprintf(stderr, "You must supply a filename " 12 | "as an argument\n"); 13 | return 1; 14 | } 15 | strncpy(filename, argv[1], PATH_MAX-1); 16 | creat(filename, 00644); 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /ch4/simple-touch-v2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | char filename[PATH_MAX] = { 0 }; 9 | if (argc != 2) 10 | { 11 | fprintf(stderr, "You must supply a filename " 12 | "as an argument\n"); 13 | return 1; 14 | } 15 | strncpy(filename, argv[1], PATH_MAX-1); 16 | if ( creat(filename, 00644) == -1 ) 17 | { 18 | fprintf(stderr, "Can't create file %s\n", 19 | filename); 20 | return 1; 21 | } 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /ch4/simple-touch-v3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | char filename[PATH_MAX] = { 0 }; 10 | if (argc != 2) 11 | { 12 | fprintf(stderr, "You must supply a filename " 13 | "as an argument\n"); 14 | return 1; 15 | } 16 | strncpy(filename, argv[1], sizeof(filename)-1); 17 | if ( creat(filename, 00644) == -1 ) 18 | { 19 | fprintf(stderr, "Can't create file %s\n", 20 | filename); 21 | if (errno == EACCES) 22 | { 23 | fprintf(stderr, "Permission denied\n"); 24 | } 25 | else 26 | { 27 | fprintf(stderr, "Unknown error\n"); 28 | } 29 | return 1; 30 | } 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /ch4/simple-touch-v4.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | char filename[PATH_MAX] = { 0 }; 10 | if (argc != 2) 11 | { 12 | fprintf(stderr, "You must supply a filename " 13 | "as an argument\n"); 14 | return 1; 15 | } 16 | strncpy(filename, argv[1], sizeof(filename)-1); 17 | if ( creat(filename, 00644) == -1 ) 18 | { 19 | fprintf(stderr, "Can't create file %s\n", 20 | filename); 21 | if (errno == EACCES) 22 | fprintf(stderr, "Permission denied\n"); 23 | else if (errno == ENOENT) 24 | fprintf(stderr, "Parent directories does " 25 | "not exist\n"); 26 | else 27 | fprintf(stderr, "Unknown error\n"); 28 | return 1; 29 | } 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /ch4/simple-touch-v5.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | int errornum; 10 | char filename[PATH_MAX] = { 0 }; 11 | if (argc != 2) 12 | { 13 | fprintf(stderr, "You must supply a filename " 14 | "as an argument\n"); 15 | return 1; 16 | } 17 | strncpy(filename, argv[1], sizeof(filename)-1); 18 | if ( creat(filename, 00644) == -1 ) 19 | { 20 | errornum = errno; 21 | fprintf(stderr, "Can't create file %s\n", 22 | filename); 23 | fprintf(stderr, "%s\n", strerror(errornum)); 24 | return 1; 25 | } 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /ch4/simple-touch-v6.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | char filename[PATH_MAX] = { 0 }; 10 | if (argc != 2) 11 | { 12 | fprintf(stderr, "You must supply a filename " 13 | "as an argument\n"); 14 | return 1; 15 | } 16 | strncpy(filename, argv[1], sizeof(filename)-1); 17 | if ( creat(filename, 00644) == -1 ) 18 | { 19 | perror("Can't create file"); 20 | return 1; 21 | } 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /ch4/simple-touch-v7.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | char filename[PATH_MAX] = { 0 }; 10 | if (argc != 2) 11 | { 12 | fprintf(stderr, "You must supply a filename " 13 | "as an argument\n"); 14 | return 1; 15 | } 16 | strncpy(filename, argv[1], sizeof(filename)-1); 17 | if ( creat(filename, 00644) == -1 ) 18 | { 19 | perror("Can't create file"); 20 | return errno; 21 | } 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /ch4/str-safe.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | if (argc != 2) 7 | { 8 | fprintf(stderr, "Supply exactly one " 9 | "argument\n"); 10 | return 1; 11 | } 12 | char buf[10] = { 0 }; 13 | strncat(buf, argv[1], sizeof(buf)-1); 14 | printf("Test: %s\n", buf); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /ch4/str-unsafe.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | char buf[10] = { 0 }; 7 | strcat(buf, argv[1]); 8 | printf("Test: %s\n", buf); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /ch5/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-Wall -Wextra -pedantic -std=c99 3 | -------------------------------------------------------------------------------- /ch5/binary-read.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | FILE *fp; 6 | float x[2]; 7 | if ( (fp = fopen("my-binary-file", "rb")) == 0 ) 8 | { 9 | fprintf(stderr, "Can't open file for " 10 | "reading\n"); 11 | return 1; 12 | } 13 | fread(&x, sizeof(float), 14 | sizeof(x) / sizeof(float), fp); 15 | printf("The first number was: %f\n", x[0]); 16 | printf("The second number was: %f\n", x[1]); 17 | fclose(fp); 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /ch5/binary-write.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | FILE *fp; 6 | float x[2]; 7 | if ( (fp = fopen("my-binary-file", "wb")) == 0 ) 8 | { 9 | fprintf(stderr, "Can't open file for " 10 | "writing\n"); 11 | return 1; 12 | } 13 | printf("Type two floating point numbers, " 14 | "separated by a space: "); 15 | scanf("%f %f", &x[0], &x[1]); 16 | fwrite(&x, sizeof(float), 17 | sizeof(x) / sizeof(float), fp); 18 | fclose(fp); 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /ch5/fd-read.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #define MAXSIZE 4096 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | int fd; /* for the file descriptor */ 11 | int maxread; /* the maximum we want to read*/ 12 | off_t filesize; /* for the file size */ 13 | struct stat fileinfo; /* struct for fstat */ 14 | char rbuf[MAXSIZE] = { 0 }; /* the read buffer*/ 15 | 16 | if (argc != 2) 17 | { 18 | fprintf(stderr, "Usage: %s [path]\n", 19 | argv[0]); 20 | return 1; 21 | } 22 | 23 | /* open the file in read-only mode and get 24 | the file size */ 25 | if ( (fd = open(argv[1], O_RDONLY)) == -1 ) 26 | { 27 | perror("Can't open file for reading"); 28 | return 1; 29 | } 30 | fstat(fd, &fileinfo); 31 | filesize = fileinfo.st_size; 32 | 33 | /* determine the max size we want to read 34 | so we don't overflow the read buffer */ 35 | if ( filesize >= MAXSIZE ) 36 | maxread = MAXSIZE-1; 37 | else 38 | maxread = filesize; 39 | 40 | /* read the content and print it */ 41 | if ( (read(fd, rbuf, maxread)) == -1 ) 42 | { 43 | perror("Can't read file"); 44 | return 1; 45 | } 46 | printf("%s", rbuf); 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /ch5/fd-seek.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #define MAXSIZE 4096 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | int fd; /* for the file descriptor */ 12 | int maxread; /* the maximum we want to read*/ 13 | off_t filesize; /* for the file size */ 14 | struct stat fileinfo; /* struct for fstat */ 15 | char rbuf[MAXSIZE] = { 0 }; /* the read buffer */ 16 | 17 | if (argc < 3 || argc > 4) 18 | { 19 | fprintf(stderr, "Usage: %s [path] [from pos] " 20 | "[bytes to read]\n", argv[0]); 21 | return 1; 22 | } 23 | 24 | /* open the file in read-only mode and get 25 | the file size */ 26 | if ( (fd = open(argv[1], O_RDONLY)) == -1 ) 27 | { 28 | perror("Can't open file for reading"); 29 | return 1; 30 | } 31 | fstat(fd, &fileinfo); 32 | filesize = fileinfo.st_size; 33 | 34 | /* determine the max size we want to read 35 | so we don't overflow the read buffer */ 36 | if ( filesize >= MAXSIZE ) 37 | { 38 | maxread = MAXSIZE-1; 39 | } 40 | else if ( argv[3] != NULL ) 41 | { 42 | if ( atoi(argv[3]) >= MAXSIZE ) 43 | { 44 | fprintf(stderr, "To big size specified\n"); 45 | return 1; 46 | } 47 | maxread = atoi(argv[3]); 48 | } 49 | else 50 | { 51 | maxread = filesize; 52 | } 53 | 54 | /* move the read position */ 55 | lseek(fd, atoi(argv[2]), SEEK_SET); 56 | /* read the content and print it */ 57 | if ( (read(fd, rbuf, maxread)) == -1 ) 58 | { 59 | perror("Can't read file"); 60 | return 1; 61 | } 62 | printf("%s\n", rbuf); 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /ch5/fd-write.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | int fd; /* for the file descriptor */ 11 | 12 | if (argc != 3) 13 | { 14 | fprintf(stderr, "Usage: %s [path] [string]\n", 15 | argv[0]); 16 | return 1; 17 | } 18 | 19 | /* Open the file (argv[1]) and create it if it 20 | doesn't exist and set it in read-write mode. 21 | Set the access mode to 644 */ 22 | if ( (fd = open(argv[1], O_CREAT|O_RDWR, 00644)) 23 | == -1 ) 24 | { 25 | perror("Can't open file for writing"); 26 | return 1; 27 | } 28 | /* write content to file */ 29 | if ( (write(fd, argv[2], strlen(argv[2]))) 30 | == -1 ) 31 | { 32 | perror("Can't write to file"); 33 | return 1; 34 | } 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /ch5/my-chmod.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void printUsage(FILE *stream, char progname[]); 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | long int accessmode; /*To hold the access mode*/ 11 | /* Check that the user supplied two arguments */ 12 | if (argc != 3) 13 | { 14 | printUsage(stderr, argv[0]); 15 | return 1; 16 | } 17 | /* Simple check for octal numbers and 18 | correct length */ 19 | if( strspn(argv[1], "01234567\n") 20 | != strlen(argv[1]) 21 | || ( strlen(argv[1]) != 3 && 22 | strlen(argv[1]) != 4 ) ) 23 | { 24 | printUsage(stderr, argv[0]); 25 | return 1; 26 | } 27 | /* Convert to octal and set the permissions */ 28 | accessmode = strtol(argv[1], NULL, 8); 29 | if (chmod(argv[2], accessmode) == -1) 30 | { 31 | perror("Can't change permissions"); 32 | } 33 | return 0; 34 | } 35 | 36 | void printUsage(FILE *stream, char progname[]) 37 | { 38 | fprintf(stream, "Usage: %s \n", progname); 40 | } 41 | -------------------------------------------------------------------------------- /ch5/my-chown.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | struct passwd *user; /* struct for getpwnam */ 13 | struct group *grp; /* struct for getgrnam */ 14 | char *username = { 0 }; /* extracted username */ 15 | char *groupname = { 0 }; /*extracted groupname*/ 16 | unsigned int uid, gid; /* extracted UID/GID */ 17 | 18 | /* Check that the user supplied two arguments 19 | (filename and user or user:group) */ 20 | if (argc != 3) 21 | { 22 | fprintf(stderr, "Usage: %s [user][:group]" 23 | " [path]\n", argv[0]); 24 | return 1; 25 | } 26 | 27 | /* Extract username and groupname */ 28 | username = strtok(argv[1], ":"); 29 | groupname = strtok(NULL, ":"); 30 | 31 | if ( (user = getpwnam(username)) == NULL ) 32 | { 33 | fprintf(stderr, "Invalid username\n"); 34 | return 1; 35 | } 36 | uid = user->pw_uid; /* get the UID */ 37 | 38 | if (groupname != NULL) /* if we typed a group */ 39 | { 40 | if ( (grp = getgrnam(groupname)) == NULL ) 41 | { 42 | fprintf(stderr, "Invalid groupname\n"); 43 | return 1; 44 | } 45 | gid = grp->gr_gid; /* get the GID */ 46 | } 47 | else 48 | { 49 | /* if no group is specifed, -1 won't change 50 | it (man 2 chown) */ 51 | gid = -1; 52 | } 53 | 54 | /* update user/group (argv[2] is the filename)*/ 55 | if ( chown(argv[2], uid, gid) == -1 ) 56 | { 57 | perror("Can't change owner/group"); 58 | return 1; 59 | } 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /ch5/my-stat-v1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | struct stat filestat; 11 | if ( argc != 2 ) 12 | { 13 | fprintf(stderr, "Usage: %s \n", 14 | argv[0]); 15 | return 1; 16 | } 17 | if ( stat(argv[1], &filestat) == -1 ) 18 | { 19 | fprintf(stderr, "Can't read file %s: %s\n", 20 | argv[1], strerror(errno)); 21 | return errno; 22 | } 23 | printf("Inode: %lu\n", filestat.st_ino); 24 | printf("Size: %zd\n", filestat.st_size); 25 | printf("Links: %lu\n", filestat.st_nlink); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /ch5/my-stat-v2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | struct stat filestat; 13 | struct passwd *userinfo; 14 | struct group *groupinfo; 15 | if ( argc != 2 ) 16 | { 17 | fprintf(stderr, "Usage: %s \n", 18 | argv[0]); 19 | return 1; 20 | } 21 | if ( stat(argv[1], &filestat) == -1 ) 22 | { 23 | fprintf(stderr, "Can't read file %s: %s\n", 24 | argv[1], strerror(errno)); 25 | return errno; 26 | } 27 | if ( (userinfo = getpwuid(filestat.st_uid)) == 28 | NULL ) 29 | { 30 | perror("Can't get username"); 31 | return errno; 32 | } 33 | if ( (groupinfo = getgrgid(filestat.st_gid)) == 34 | NULL ) 35 | { 36 | perror("Can't get groupname"); 37 | return errno; 38 | } 39 | printf("Inode: %lu\n", filestat.st_ino); 40 | printf("Size: %zd\n", filestat.st_size); 41 | printf("Links: %lu\n", filestat.st_nlink); 42 | printf("Owner: %d (%s)\n", filestat.st_uid, 43 | userinfo->pw_name); 44 | printf("Group: %d (%s)\n", filestat.st_gid, 45 | groupinfo->gr_name); 46 | printf("File mode: %o\n", filestat.st_mode); 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /ch5/new-name.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | if (argc != 3) 9 | { 10 | fprintf(stderr, "Usage: %s [target] " 11 | "[new-name]\n", argv[0]); 12 | return 1; 13 | } 14 | if (link(argv[1], argv[2]) == -1) 15 | { 16 | perror("Can't create link"); 17 | return 1; 18 | } 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /ch5/new-symlink.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 700 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | if (argc != 3) 10 | { 11 | fprintf(stderr, "Usage: %s [target] " 12 | "[link]\n", argv[0]); 13 | return 1; 14 | } 15 | if (symlink(argv[1], argv[2]) == -1) 16 | { 17 | perror("Can't create link"); 18 | return 1; 19 | } 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /ch5/remove.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | if (argc != 2) 8 | { 9 | fprintf(stderr, "Usage: %s [path]\n", 10 | argv[0]); 11 | return 1; 12 | } 13 | if ( unlink(argv[1]) == -1 ) 14 | { 15 | perror("Can't remove file"); 16 | return errno; 17 | } 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /ch5/simple-touch-v8.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #define MAX_LENGTH 100 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | char filename[MAX_LENGTH] = { 0 }; 11 | /* Check number of arguments */ 12 | if (argc != 2) 13 | { 14 | fprintf(stderr, "You must supply a filename " 15 | "as an argument\n"); 16 | return 1; 17 | } 18 | strncat(filename, argv[1], sizeof(filename)-1); 19 | 20 | /* Update the access and modification time */ 21 | if ( utime(filename, NULL) == -1 ) 22 | { 23 | /* If the file doesn't exist, create it */ 24 | if (errno == ENOENT) 25 | { 26 | if ( creat(filename, 00644) == -1 ) 27 | { 28 | perror("Can't create file"); 29 | return errno; 30 | } 31 | } 32 | /* If we can't update the timestamp, 33 | something is wrong */ 34 | else 35 | { 36 | perror("Can't update timestamp"); 37 | return errno; 38 | } 39 | } 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /ch5/stream-read.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | FILE *fp; /* pointer to a file stream */ 6 | char linebuf[1024] = { 0 }; /* line buffer */ 7 | 8 | if ( argc != 2 ) 9 | { 10 | fprintf(stderr, "Usage: %s [path]\n", 11 | argv[0]); 12 | return 1; 13 | } 14 | 15 | /* open file with read mode */ 16 | if ( (fp = fopen(argv[1], "r")) == NULL ) 17 | { 18 | perror("Can't open file for reading"); 19 | return 1; 20 | } 21 | 22 | /* loop over each line and write it to stdout */ 23 | while(fgets(linebuf, sizeof(linebuf), fp) 24 | != NULL) 25 | { 26 | printf("%s", linebuf); 27 | } 28 | fclose(fp); /* close the stream */ 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /ch5/stream-seek.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | int ch; /* for each character */ 7 | FILE *fp; /* pointer to a file stream */ 8 | if ( argc < 3 || argc > 4 ) 9 | { 10 | fprintf(stderr, "Usage: %s [path] [from pos]" 11 | " [to pos]\n", argv[0]); 12 | return 1; 13 | } 14 | 15 | /* open file with read mode */ 16 | if ( (fp = fopen(argv[1], "r")) == NULL ) 17 | { 18 | perror("Can't open file for reading"); 19 | return 1; 20 | } 21 | 22 | fseek(fp, atoi(argv[2]), SEEK_SET); 23 | /* loop over each line and write it to stdout */ 24 | while( (ch = fgetc(fp)) != EOF ) 25 | { 26 | if ( argv[3] != NULL) 27 | { 28 | if ( ftell(fp) >= atoi(argv[3]) ) 29 | { 30 | break; 31 | } 32 | } 33 | putchar(ch); 34 | } 35 | printf("\n"); 36 | fclose(fp); /* close the stream */ 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /ch5/stream-write.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | FILE *fp; /* pointer to a file stream */ 6 | char linebuf[1024] = { 0 }; /* line buffer */ 7 | 8 | if ( argc != 2 ) 9 | { 10 | fprintf(stderr, "Usage: %s [path]\n", 11 | argv[0]); 12 | return 1; 13 | } 14 | 15 | /* open file with write mode */ 16 | if ( (fp = fopen(argv[1], "w")) == NULL ) 17 | { 18 | perror("Can't open file for writing"); 19 | return 1; 20 | } 21 | 22 | /*loop over each line and write it to the file*/ 23 | while(fgets(linebuf, sizeof(linebuf), stdin) 24 | != NULL) 25 | { 26 | fprintf(fp, "%s", linebuf); 27 | } 28 | fclose(fp); /* close the stream */ 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /ch5/testfile1: -------------------------------------------------------------------------------- 1 | This is just a small file we'll use 2 | -------------------------------------------------------------------------------- /ch6/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-Wall -Wextra -pedantic -std=c99 3 | -------------------------------------------------------------------------------- /ch6/create-zombie.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(void) 7 | { 8 | pid_t pid; 9 | printf("My PID is %d\n", getpid()); 10 | /* fork, save the PID, and check for errors */ 11 | if ( (pid = fork()) == -1 ) 12 | { 13 | perror("Can't fork"); 14 | return 1; 15 | } 16 | if (pid == 0) 17 | { 18 | /* if pid is 0 we are in the child process */ 19 | printf("Hello and goodbye from the child!\n"); 20 | exit(0); 21 | } 22 | else if(pid > 0) 23 | { 24 | /* if pid is greater than 0 we are in 25 | * the parent */ 26 | printf("Hello from the parent process! " 27 | "My child had PID %d\n", pid); 28 | sleep(120); 29 | } 30 | else 31 | { 32 | fprintf(stderr, "Something went wrong " 33 | "forking\n"); 34 | return 1; 35 | } 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /ch6/execdemo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(void) 7 | { 8 | printf("My PID is %d\n", getpid()); 9 | printf("Hit enter to continue "); 10 | getchar(); /* wait for enter key */ 11 | printf("Executing /usr/bin/less...\n"); 12 | /* execute less using execl and error check it */ 13 | if ( execl("/usr/bin/less", "less", 14 | "/etc/passwd", (char*)NULL) == -1 ) 15 | { 16 | perror("Can't execute program"); 17 | return 1; 18 | } 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /ch6/forkdemo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(void) 6 | { 7 | pid_t pid; 8 | printf("My PID is %d\n", getpid()); 9 | /* fork, save the PID, and check for errors */ 10 | if ( (pid = fork()) == -1 ) 11 | { 12 | perror("Can't fork"); 13 | return 1; 14 | } 15 | if (pid == 0) 16 | { 17 | /* if pid is 0 we are in the child process */ 18 | printf("Hello from the child process!\n"); 19 | sleep(120); 20 | } 21 | else if(pid > 0) 22 | { 23 | /* if pid is greater than 0 we are in 24 | * the parent */ 25 | printf("Hello from the parent process! " 26 | "My child has PID %d\n", pid); 27 | sleep(120); 28 | } 29 | else 30 | { 31 | fprintf(stderr, "Something went wrong " 32 | "forking\n"); 33 | return 1; 34 | } 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /ch6/my-daemon-v2.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | void sigHandler(int sig); 12 | 13 | /* moved these variables to the global scope 14 | since they need to be access/deleted/closed 15 | from the signal handler */ 16 | FILE *fp; 17 | const char pidfile[] = "/var/run/my-daemon.pid"; 18 | 19 | int main(void) 20 | { 21 | pid_t pid; 22 | time_t now; /* for the current time */ 23 | struct sigaction action; /* for sigaction */ 24 | const char daemonfile[] = 25 | "/tmp/my-daemon-is-alive.txt"; 26 | 27 | if ( (pid = fork()) == -1 ) 28 | { 29 | perror("Can't fork"); 30 | return 1; 31 | } 32 | else if ( (pid != 0) ) 33 | { 34 | exit(0); 35 | } 36 | 37 | /* the parent process has exited, which makes 38 | * the rest of the code the child process */ 39 | setsid(); /* create a new session to lose the 40 | controlling terminal */ 41 | 42 | /* fork again, creating a grandchild, the 43 | * actual daemon */ 44 | if ( (pid = fork()) == -1 ) 45 | { 46 | perror("Can't fork"); 47 | return 1; 48 | } 49 | /* the child process which will exit */ 50 | else if ( pid > 0 ) 51 | { 52 | /* open pid-file for writing and check it */ 53 | if ( (fp = fopen(pidfile, "w")) == NULL ) 54 | { 55 | perror("Can't open file for writing"); 56 | return 1; 57 | } 58 | /* write pid to file */ 59 | fprintf(fp, "%d\n", pid); 60 | fclose(fp); /* close the file pointer */ 61 | exit(0); 62 | } 63 | 64 | umask(022); /* set the umask to something ok */ 65 | chdir("/"); /* change working directory to / */ 66 | /* open the "daemonfile" for writing */ 67 | if ( (fp = fopen(daemonfile, "w")) == NULL ) 68 | { 69 | perror("Can't open daemonfile"); 70 | return 1; 71 | } 72 | /* from here, we don't need stdin, stdout or, 73 | * stderr anymore, so let's close them all, 74 | * then re-open them to /dev/null */ 75 | close(STDIN_FILENO); 76 | close(STDOUT_FILENO); 77 | close(STDERR_FILENO); 78 | open("/dev/null", O_RDONLY); /* 0 = stdin */ 79 | open("/dev/null", O_WRONLY); /* 1 = stdout */ 80 | open("/dev/null", O_RDWR); /* 2 = stderr */ 81 | 82 | /* prepare for sigaction */ 83 | action.sa_handler = sigHandler; 84 | sigfillset(&action.sa_mask); 85 | action.sa_flags = SA_RESTART; 86 | /* register the signals we want to handle */ 87 | sigaction(SIGTERM, &action, NULL); 88 | sigaction(SIGINT, &action, NULL); 89 | sigaction(SIGQUIT, &action, NULL); 90 | sigaction(SIGABRT, &action, NULL); 91 | 92 | /* here we start the daemons "work" */ 93 | for (;;) 94 | { 95 | /* get the current time and write it to the 96 | "daemonfile" that we opened above */ 97 | time(&now); 98 | fprintf(fp, "Daemon alive at %s", 99 | ctime(&now)); 100 | fflush(fp); /* flush the stream */ 101 | sleep(30); 102 | } 103 | return 0; 104 | } 105 | 106 | void sigHandler(int sig) 107 | { 108 | int status = 0; 109 | if ( sig == SIGTERM || sig == SIGINT 110 | || sig == SIGQUIT 111 | || sig == SIGABRT ) 112 | { 113 | /* remove the pid-file */ 114 | if ( (unlink(pidfile)) == -1 ) 115 | status = 1; 116 | if ( (fclose(fp)) == EOF ) 117 | status = 1; 118 | exit(status); /* exit with the status set*/ 119 | } 120 | else /* some other signal */ 121 | { 122 | exit(1); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /ch6/my-daemon.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main(void) 10 | { 11 | pid_t pid; 12 | FILE *fp; 13 | time_t now; /* for the current time */ 14 | const char pidfile[] = "/var/run/my-daemon.pid"; 15 | const char daemonfile[] = 16 | "/tmp/my-daemon-is-alive.txt"; 17 | 18 | if ( (pid = fork()) == -1 ) 19 | { 20 | perror("Can't fork"); 21 | return 1; 22 | } 23 | else if ( (pid != 0) ) 24 | { 25 | exit(0); 26 | } 27 | /* the parent process has exited, so this is the 28 | * child. create a new session to lose the 29 | * controlling terminal */ 30 | setsid(); 31 | 32 | /* fork again, creating a grandchild, 33 | * the actual daemon */ 34 | if ( (pid = fork()) == -1 ) 35 | { 36 | perror("Can't fork"); 37 | return 1; 38 | } 39 | /* the child process which will exit */ 40 | else if ( pid > 0 ) 41 | { 42 | /* open pid-file for writing and error 43 | * check it */ 44 | if ( (fp = fopen(pidfile, "w")) == NULL ) 45 | { 46 | perror("Can't open file for writing"); 47 | return 1; 48 | } 49 | /* write pid to file */ 50 | fprintf(fp, "%d\n", pid); 51 | fclose(fp); /* close the file pointer */ 52 | exit(0); 53 | } 54 | 55 | umask(022); /* set the umask to something ok */ 56 | chdir("/"); /* change working directory to / */ 57 | /* open the "daemonfile" for writing */ 58 | if ( (fp = fopen(daemonfile, "w")) == NULL ) 59 | { 60 | perror("Can't open daemonfile"); 61 | return 1; 62 | } 63 | /* from here, we don't need stdin, stdout or, 64 | * stderr anymore, so let's close them all, 65 | * then re-open them to /dev/null */ 66 | close(STDIN_FILENO); 67 | close(STDOUT_FILENO); 68 | close(STDERR_FILENO); 69 | open("/dev/null", O_RDONLY); /* 0 = stdin */ 70 | open("/dev/null", O_WRONLY); /* 1 = stdout */ 71 | open("/dev/null", O_RDWR); /* 2 = stderr */ 72 | 73 | /* here we start the daemons "work" */ 74 | for (;;) 75 | { 76 | /* get the current time and write it to the 77 | "daemonfile" that we opened above */ 78 | time(&now); 79 | fprintf(fp, "Daemon alive at %s", 80 | ctime(&now)); 81 | fflush(fp); /* flush the stream */ 82 | sleep(30); 83 | } 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /ch6/my-fork.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(void) 8 | { 9 | pid_t pid; 10 | int status; 11 | 12 | /* Get and print my own pid, then fork 13 | and check for errors */ 14 | printf("My PID is %d\n", getpid()); 15 | if ( (pid = fork()) == -1 ) 16 | { 17 | perror("Can't fork"); 18 | return 1; 19 | } 20 | if (pid == 0) 21 | { 22 | /* If pid is 0 we are in the child process, 23 | from here we execute 'man ls' */ 24 | if ( execl("/usr/bin/man", "man", "ls", 25 | (char*)NULL) == -1 ) 26 | { 27 | perror("Can't exec"); 28 | return 1; 29 | } 30 | } 31 | else if(pid > 0) 32 | { 33 | /* In the parent we must wait for the child 34 | to exit with waitpid(). Afterward, the 35 | child exit status is written to 'status' */ 36 | waitpid(pid, &status, 0); 37 | printf("Child executed with PID %d\n", pid); 38 | printf("Its return status was %d\n", status); 39 | } 40 | else 41 | { 42 | fprintf(stderr, "Something went wrong " 43 | "forking\n"); 44 | return 1; 45 | } 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /ch6/no-zombie.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(void) 8 | { 9 | pid_t pid; 10 | int status; 11 | printf("My PID is %d\n", getpid()); 12 | /* fork, save the PID, and check for errors */ 13 | if ( (pid = fork()) == -1 ) 14 | { 15 | perror("Can't fork"); 16 | return 1; 17 | } 18 | if (pid == 0) 19 | { 20 | /* if pid is 0 we are in the child process */ 21 | printf("Hello and goodbye from the child!\n"); 22 | exit(0); 23 | } 24 | else if(pid > 0) 25 | { 26 | /* if pid is greater than 0 we are in 27 | * the parent */ 28 | printf("Hello from the parent process! " 29 | "My child had PID %d\n", pid); 30 | waitpid(pid, &status, 0); /* wait for child */ 31 | sleep(120); 32 | } 33 | else 34 | { 35 | fprintf(stderr, "Something went wrong " 36 | "forking\n"); 37 | return 1; 38 | } 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /ch6/orphan.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(void) 7 | { 8 | pid_t pid; 9 | printf("Parent PID is %d\n", getpid()); 10 | /* fork, save the PID, and check for errors */ 11 | if ( (pid = fork()) == -1 ) 12 | { 13 | perror("Can't fork"); 14 | return 1; 15 | } 16 | if (pid == 0) 17 | { 18 | /* if pid is 0 we are in the child process */ 19 | printf("I am the child and will run for " 20 | "5 minutes\n"); 21 | sleep(300); 22 | exit(0); 23 | } 24 | else if(pid > 0) 25 | { 26 | /* if pid is greater than 0 we are in 27 | * the parent */ 28 | printf("My child has PID %d\n" 29 | "I, the parent, will exit when you " 30 | "press enter\n", pid); 31 | getchar(); 32 | return 0; 33 | } 34 | else 35 | { 36 | fprintf(stderr, "Something went wrong " 37 | "forking\n"); 38 | return 1; 39 | } 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /ch6/signals.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void sigHandler(int sig); 8 | 9 | int main(void) 10 | { 11 | pid_t pid; /* to store our pid in */ 12 | pid = getpid(); /* get the pid */ 13 | struct sigaction action; /* for sigaction */ 14 | sigset_t set; /* signals we want to ignore */ 15 | printf("Program running with PID %d\n", pid); 16 | /* prepare sigaction() */ 17 | action.sa_handler = sigHandler; 18 | sigfillset(&action.sa_mask); 19 | action.sa_flags = SA_RESTART; 20 | /* register two signal handlers, one for USR1 21 | and one for USR2 */ 22 | sigaction(SIGUSR1, &action, NULL); 23 | sigaction(SIGUSR2, &action, NULL); 24 | 25 | /* create a "signal set" for sigprocmask() */ 26 | sigemptyset(&set); 27 | sigaddset(&set, SIGINT); 28 | /* block SIGINT and run an infinite loop */ 29 | sigprocmask(SIG_BLOCK, &set, NULL); 30 | /* infinite loop to keep the program running */ 31 | for (;;) 32 | { 33 | sleep(10); 34 | } 35 | sigprocmask(SIG_UNBLOCK, &set, NULL); 36 | return 0; 37 | } 38 | /* the signal handling function */ 39 | void sigHandler(int sig) 40 | { 41 | if (sig == SIGUSR1) 42 | { 43 | printf("Received USR1 signal\n"); 44 | } 45 | else if (sig == SIGUSR2) 46 | { 47 | printf("Received USR2 signal\n"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ch6/sysdemo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(void) 5 | { 6 | if ( (system("man ls")) == -1 ) 7 | { 8 | fprintf(stderr, "Error forking or reading " 9 | "status\n"); 10 | return 1; 11 | } 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /ch7/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-Wall -Wextra -pedantic -std=c99 3 | -------------------------------------------------------------------------------- /ch7/my-daemon-v2.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | void sigHandler(int sig); 12 | 13 | /* moved these variables to the global scope 14 | since they need to be access/deleted/closed 15 | from the signal handler */ 16 | FILE *fp; 17 | const char pidfile[] = "/var/run/my-daemon.pid"; 18 | 19 | int main(void) 20 | { 21 | pid_t pid; 22 | time_t now; /* for the current time */ 23 | struct sigaction action; /* for sigaction */ 24 | const char daemonfile[] = 25 | "/tmp/my-daemon-is-alive.txt"; 26 | 27 | if ( (pid = fork()) == -1 ) 28 | { 29 | perror("Can't fork"); 30 | return 1; 31 | } 32 | else if ( (pid != 0) ) 33 | { 34 | exit(0); 35 | } 36 | 37 | /* the parent process has exited, which makes 38 | * the rest of the code the child process */ 39 | setsid(); /* create a new session to lose the 40 | controlling terminal */ 41 | 42 | /* fork again, creating a grandchild, the 43 | * actual daemon */ 44 | if ( (pid = fork()) == -1 ) 45 | { 46 | perror("Can't fork"); 47 | return 1; 48 | } 49 | /* the child process which will exit */ 50 | else if ( pid > 0 ) 51 | { 52 | /* open pid-file for writing and check it */ 53 | if ( (fp = fopen(pidfile, "w")) == NULL ) 54 | { 55 | perror("Can't open file for writing"); 56 | return 1; 57 | } 58 | /* write pid to file */ 59 | fprintf(fp, "%d\n", pid); 60 | fclose(fp); /* close the file pointer */ 61 | exit(0); 62 | } 63 | 64 | umask(022); /* set the umask to something ok */ 65 | chdir("/"); /* change working directory to / */ 66 | /* open the "daemonfile" for writing */ 67 | if ( (fp = fopen(daemonfile, "w")) == NULL ) 68 | { 69 | perror("Can't open daemonfile"); 70 | return 1; 71 | } 72 | /* from here, we don't need stdin, stdout or, 73 | * stderr anymore, so let's close them all, 74 | * then re-open them to /dev/null */ 75 | close(STDIN_FILENO); 76 | close(STDOUT_FILENO); 77 | close(STDERR_FILENO); 78 | open("/dev/null", O_RDONLY); /* 0 = stdin */ 79 | open("/dev/null", O_WRONLY); /* 1 = stdout */ 80 | open("/dev/null", O_RDWR); /* 2 = stderr */ 81 | 82 | /* prepare for sigaction */ 83 | action.sa_handler = sigHandler; 84 | sigfillset(&action.sa_mask); 85 | action.sa_flags = SA_RESTART; 86 | /* register the signals we want to handle */ 87 | sigaction(SIGTERM, &action, NULL); 88 | sigaction(SIGINT, &action, NULL); 89 | sigaction(SIGQUIT, &action, NULL); 90 | sigaction(SIGABRT, &action, NULL); 91 | 92 | /* here we start the daemons "work" */ 93 | for (;;) 94 | { 95 | /* get the current time and write it to the 96 | "daemonfile" that we opened above */ 97 | time(&now); 98 | fprintf(fp, "Daemon alive at %s", 99 | ctime(&now)); 100 | fflush(fp); /* flush the stream */ 101 | sleep(30); 102 | } 103 | return 0; 104 | } 105 | 106 | void sigHandler(int sig) 107 | { 108 | int status = 0; 109 | if ( sig == SIGTERM || sig == SIGINT 110 | || sig == SIGQUIT 111 | || sig == SIGABRT ) 112 | { 113 | /* remove the pid-file */ 114 | if ( (unlink(pidfile)) == -1 ) 115 | status = 1; 116 | if ( (fclose(fp)) == EOF ) 117 | status = 1; 118 | exit(status); /* exit with the status set*/ 119 | } 120 | else /* some other signal */ 121 | { 122 | exit(1); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /ch7/my-daemon.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=A small daemon for testing 3 | [Service] 4 | ExecStart=/usr/local/sbin/my-daemon-v2 5 | Restart=on-failure 6 | Type=forking 7 | PIDFile=/var/run/my-daemon.pid 8 | [Install] 9 | WantedBy=multi-user.target 10 | -------------------------------------------------------------------------------- /ch7/new-style-daemon.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void sigHandler(int sig); 9 | 10 | int main(void) 11 | { 12 | time_t now; /* for the current time */ 13 | struct sigaction action; /* for sigaction */ 14 | 15 | /* prepare for sigaction */ 16 | action.sa_handler = sigHandler; 17 | sigfillset(&action.sa_mask); 18 | action.sa_flags = SA_RESTART; 19 | 20 | /* register the signal handler */ 21 | sigaction(SIGTERM, &action, NULL); 22 | sigaction(SIGUSR1, &action, NULL); 23 | sigaction(SIGHUP, &action, NULL); 24 | 25 | for (;;) /* main loop */ 26 | { 27 | time(&now); /* get current date & time */ 28 | printf("Daemon alive at %s", ctime(&now)); 29 | fflush(stdout); 30 | sleep(30); 31 | } 32 | return 0; 33 | } 34 | 35 | void sigHandler(int sig) 36 | { 37 | if (sig == SIGUSR1) 38 | { 39 | printf("Hello world!\n"); 40 | } 41 | else if (sig == SIGTERM) 42 | { 43 | printf("Doing some cleanup...\n"); 44 | printf("Bye bye...\n"); 45 | exit(0); 46 | } 47 | else if (sig == SIGHUP) 48 | { 49 | printf("HUP is used to reload any " 50 | "configuration files\n"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ch8/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-Wall -Wextra -pedantic -std=c99 3 | -------------------------------------------------------------------------------- /ch8/convert.c: -------------------------------------------------------------------------------- 1 | float c_to_f(float celsius) 2 | { 3 | return (celsius*9/5+32); 4 | } 5 | 6 | float c_to_k(float celsius) 7 | { 8 | return (celsius + 273.15); 9 | } 10 | -------------------------------------------------------------------------------- /ch8/convert.h: -------------------------------------------------------------------------------- 1 | float c_to_f(float celsius); 2 | float c_to_k(float celsius); 3 | -------------------------------------------------------------------------------- /ch8/get-public-ip.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(void) 5 | { 6 | CURL *curl; 7 | 8 | curl = curl_easy_init(); 9 | if(curl) 10 | { 11 | curl_easy_setopt(curl, CURLOPT_URL, 12 | "https://ifconfig.me"); 13 | curl_easy_perform(curl); 14 | curl_easy_cleanup(curl); 15 | } 16 | else 17 | { 18 | fprintf(stderr, "Cannot initialize curl\n"); 19 | return 1; 20 | } 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /ch8/temperature.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "convert.h" 5 | 6 | void printUsage(FILE *stream, char progname[]); 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | if ( argc != 3 ) 11 | { 12 | printUsage(stderr, argv[0]); 13 | return 1; 14 | } 15 | if ( strcmp(argv[1], "-f") == 0 ) 16 | { 17 | printf("%.1f C = %.1f F\n", 18 | atof(argv[2]), c_to_f(atof(argv[2]))); 19 | } 20 | else if ( strcmp(argv[1], "-k") == 0 ) 21 | { 22 | printf("%.1f C = %.1f K\n", 23 | atof(argv[2]), c_to_k(atof(argv[2]))); 24 | } 25 | else 26 | { 27 | printUsage(stderr, argv[0]); 28 | return 1; 29 | } 30 | 31 | return 0; 32 | } 33 | 34 | void printUsage(FILE *stream, char progname[]) 35 | { 36 | fprintf(stream, "%s [-f] [-k] [temperature]\n" 37 | "Example: %s -f 25\n", progname, progname); 38 | } 39 | -------------------------------------------------------------------------------- /ch9/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-Wall -Wextra -pedantic -std=c99 3 | -------------------------------------------------------------------------------- /ch9/my-pty.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 600 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(void) 9 | { 10 | char rxbuf[1]; 11 | char txbuf[3]; 12 | int master; /* for the pts master fd */ 13 | int c; /* to catch read's return value */ 14 | 15 | master = posix_openpt(O_RDWR); 16 | grantpt(master); 17 | unlockpt(master); 18 | printf("Slave: %s\n", ptsname(master)); 19 | 20 | while(1) /* main loop */ 21 | { 22 | /* read from the master file descriptor */ 23 | c = read(master, rxbuf, 1); 24 | if (c == 1) 25 | { 26 | /* convert carriage return to '\n\r' */ 27 | if (rxbuf[0] == '\r') 28 | { 29 | printf("\n\r"); /* on master */ 30 | sprintf(txbuf, "\n\r"); /* on slave */ 31 | } 32 | else 33 | { 34 | printf("%c", rxbuf[0]); 35 | sprintf(txbuf, "%c", rxbuf[0]); 36 | } 37 | fflush(stdout); 38 | write(master, txbuf, strlen(txbuf)); 39 | } 40 | else /* if c is not 1, it has disconnected */ 41 | { 42 | printf("Disconnected\n\r"); 43 | return 0; 44 | } 45 | } 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /ch9/passprompt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(void) 7 | { 8 | char mypass[] = "super-secret"; 9 | char buffer[80]; 10 | struct termios term; 11 | 12 | /* get the current settings */ 13 | tcgetattr(STDIN_FILENO, &term); 14 | /* disable echoing */ 15 | term.c_lflag = term.c_lflag & ~ECHO; 16 | tcsetattr(STDIN_FILENO, TCSAFLUSH, &term); 17 | 18 | printf("Enter password: "); 19 | scanf("%s", buffer); 20 | if ( (strcmp(mypass, buffer) == 0) ) 21 | { 22 | printf("\nCorrect password, welcome!\n"); 23 | } 24 | else 25 | { 26 | printf("\nIncorrect password, go away!\n");; 27 | } 28 | 29 | /* re-enable echoing */ 30 | term.c_lflag = term.c_lflag | ECHO; 31 | tcsetattr(STDIN_FILENO, TCSAFLUSH, &term); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /ch9/terminal-size.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(void) 7 | { 8 | struct winsize termsize; 9 | while(1) 10 | { 11 | printf("\033[1;1H\033[2J"); 12 | ioctl(STDOUT_FILENO, TIOCGWINSZ, &termsize); 13 | printf("Height: %d rows\n", 14 | termsize.ws_row); 15 | printf("Width: %d columns\n", 16 | termsize.ws_col); 17 | sleep(0); 18 | } 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /ch9/ttyinfo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(void) 6 | { 7 | if ( (isatty(STDOUT_FILENO) == 1) ) 8 | { 9 | printf("It's a TTY with the name %s\n", 10 | ttyname(STDOUT_FILENO)); 11 | } 12 | else 13 | { 14 | perror("isatty"); 15 | } 16 | printf("Hello world\n"); 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /errata.md: -------------------------------------------------------------------------------- 1 | # Errata for Linux System Programming Techniques 2 | This is the official errata for Linux System Programming Techniques. If you 3 | find an error in the book, please open an issue here on GitHub and describe the 4 | error. I will then confirm it and add it to this document. 5 | 6 | ## Missing function code on page 32 7 | In the code listing for `functions_ver2.c` on page 32, the code for the 8 | function `func2()` is missing. 9 | 10 | The correct code is found on GitHub. Please see 11 | [functions_ver2.c](https://github.com/PacktPublishing/Linux-System-Programming-Techniques/blob/master/ch2/functions_ver2.c). 12 | 13 | ## Missing curly braces on page 108 14 | On page 108, where the code for `area.c` ends, the code listing is missing 15 | two curly braces and a `return 0;`. However, the code on GitHub is correct, 16 | please see 17 | [area.c](https://github.com/PacktPublishing/Linux-System-Programming-Techniques/blob/master/ch3/area/area.c). 18 | 19 | ## Page 162 20 | In point 8,9 and 10 (page 163), the regular Linux `chmod`-command is used to 21 | set permissions, although it should be the self-created `my-chmod`-command. 22 | 23 | ## Page 169, How it works 24 | The sentence *A file descriptor is just an integer, just as 0, 1, and 3 are 25 | stdin, stdout, and stderr* should be *A file descriptor is just an integer, 26 | just as 0, 1, and 2 are stdin, stdout, and stderr* 27 | 28 | ## fprintf on page 175 29 | The code i`fprintf(fp, linebuf);` in the recipe works, but is not safe. If the 30 | input text contains a format operator, like `%s` the output will be incorrect. 31 | 32 | Instead, the correct code should be `fprintf(fp, "%s", linebuf);`. 33 | 34 | For the corect program, see 35 | [stream-write.c](https://github.com/PacktPublishing/Linux-System-Programming-Techniques/blob/master/ch5/stream-write.c). 36 | 37 | ## Missing 'else if' on page 213 38 | *This error have been corrected in the e-book, but not yet in the printed 39 | book.* 40 | 41 | In the program `create-zombie.c` on page 213, there should be an `else 42 | if`-statement on line 22. That `else if`-statement should check if the 43 | `pid`-variable is greater than 0. The code on GitHub is correct, but not in the 44 | book. 45 | 46 | For the correct code, please see 47 | [create-zombie.c](https://github.com/PacktPublishing/Linux-System-Programming-Techniques/blob/master/ch6/create-zombie.c). 48 | 49 | ## Missing lines on page 227 50 | The code for `my-daemon-v2.c` on page 227 is missing the following lines at 51 | the top of the code: 52 | 53 | ``` 54 | #define _POSIX_C_SOURCE 200809L 55 | #include 56 | #include 57 | #include 58 | ``` 59 | 60 | The code on GitHub is correct though. 61 | 62 | ## Page 258, wrong temperature symbol 63 | On page 258, the code uses the wrong temperature symbol. Line 22 should read 64 | `printf("%.1f C = %.1f K\n",` instead of `printf("%.1f C = %.1f F\n",`. 65 | 66 | The same goes for the example in step 3 on page 259; it should read `15.0 C = 67 | 288.1 K`. 68 | 69 | ## Page 278, the sending user must also set mesg y 70 | On page 278, in step 7 and 8, the sending user must also allow messages with 71 | `mesg y` before they can send a message using `write`. 72 | 73 | ## Wrong version of my-daemon-ctl.c in the book 74 | *This error have been corrected in the e-book, but not yet in the printed 75 | book.* 76 | 77 | The book contains the wrong version of the program `my-daemon-ctl.c` on pages 78 | 295 to 298. The correct, working version is listed here on Github in 79 | chapter 10, filename 80 | [my-daemon-ctl.c](https://github.com/PacktPublishing/Linux-System-Programming-Techniques/blob/master/ch10/my-daemon-ctl.c). 81 | 82 | The most important difference between the versions is that the path to 83 | `my-daemon-v2` is hard coded in the book in `const char daemonPath[]`, which it 84 | shouldn't be. 85 | 86 | ## sleep() only take integer values, page 290 87 | On page 290, in the code listing for `terminal-size.c`, there's a line with 88 | `sleep(0.1)`. It's purpose is to prevent the program from exhausting the system 89 | resources. However, `sleep()` only take integer values. So in reality this 90 | will be `sleep(0)`, which also work. A sleep of 0 seconds will in fact 91 | prevent system exhaustion. A big thank you to [zhenya4880](https://github.com/zhenya4880) 92 | for pointing this out in [issue 3](https://github.com/PacktPublishing/Linux-System-Programming-Techniques/issues/3). 93 | 94 | The code on Github is updated with `sleep(0)`. 95 | 96 | ## Page 305/306 97 | The `#include ` appears twice. It should only appear once. 98 | 99 | ## Page 308, point 9 100 | The text says *CTRL+P*. It should be *CTRL+C*. 101 | 102 | ## Page 317 103 | The program `msg-receiever` has been accidentally been named `msg-reveiver`. 104 | 105 | ## Page 318, the See also section 106 | `man 3 mq_recevie` should be `man 3 mq_receive`. 107 | 108 | ## Page 396 109 | When compiling `overflow.c`, the compiler already throws a overflow warning, 110 | although it creates the executable. This should be mentioned in 111 | the book. 112 | 113 | --------------------------------------------------------------------------------