├── ch13_pipes ├── fifo │ ├── fifo_serverclient │ │ ├── Makefile │ │ ├── run.sh │ │ ├── protocol.h │ │ ├── client.c │ │ └── server.c │ ├── fifo_rw │ │ ├── run_fifo_rw_demo.sh │ │ ├── fifo_reader_demo.c │ │ └── fifo_writer_demo.c │ ├── fifos_in_shell.sh │ └── fifo_open_demo.c ├── pipe │ ├── pipe_exec │ │ ├── run_pipe_exec.sh │ │ ├── pipe_exec_child.c │ │ └── pipe_exec_parent.c │ ├── pipe_in_one_process.c │ ├── a_note_on_closing_pipes │ ├── pipe_plus_fork.c │ └── pipe_dup_ex.c ├── popen │ ├── popen_write.c │ ├── popen_read.c │ └── popen_bufread.c ├── high_level_notes_on_popen_pipe_fifo └── cd_app_fifo │ ├── Makefile │ ├── cd_data.h │ └── cliserv.h ├── .gitignore ├── ch11_sigsprocs ├── upper ├── upper.c ├── signals.notes ├── zombie.notes ├── basic_signal_demo.c ├── basic_sigaction_demo.c ├── exec_fd_demo.c ├── alarm.c ├── exec_demo.c └── fork_wait_demo.c ├── ch8_mysql ├── cd_prog_ch8 │ ├── run.sh │ ├── makefile │ ├── schema.sql │ ├── app_mysql.h │ ├── app_testdriver.c │ └── sample_data.sql ├── manage_users.sql ├── run_examples.sh ├── children.sql ├── connect_ex1.c ├── install_mysql.sh ├── connect_ex2.c ├── mutate.c └── read_data.c ├── ch14_sem_shmem_mqs ├── mq │ ├── run.sh │ ├── producer.c │ └── consumer.c ├── shm │ ├── run.sh │ ├── common.h │ ├── producer.c │ ├── writer.c │ └── consumer_creator.c ├── sem │ ├── run.sh │ └── semun.h ├── cd_app_mq │ ├── Makefile │ ├── cd_data.h │ └── cliserv.h └── README ├── readme.md ├── ch15_sockets ├── daytime_examples │ ├── inetd_notes │ ├── tcp.c │ └── udp.c ├── note_on_run_scripts ├── unix_af │ ├── run.sh │ ├── client.c │ └── server.c ├── forking_inet_ex │ ├── run.sh │ ├── client.c │ └── server.c ├── simple_inet_ex │ ├── run.sh │ ├── server.c │ └── client.c └── select_inet_ex │ ├── run.sh │ ├── client.c │ └── server.c ├── ch7_data ├── dbm_runner.sh ├── cd_prog_dir │ ├── makefile │ └── cd_data.h ├── basic_filelock.c ├── mem_ex.c └── fnctl_ex.c ├── ch6_curses ├── debugging_notes ├── subwindows.c ├── pad_demo.c ├── colors.c ├── keypad.c ├── curses_pwdemo.c ├── macbeth.c └── multi_window_demo.c ├── ch4_env ├── hostinfo.c ├── env_environ.c ├── userinfo.c ├── getopt.c ├── getopt_long.c ├── tempfiles.c ├── syslog.c ├── resources.c └── times.c ├── ch3_files ├── file_cp_3.c ├── fdes_unbuffered_cp_1.c ├── fdes_buffered_cp_2.c ├── dir_scan_4.c └── mmap_ex_5.c ├── ch5_term ├── password.c ├── terminfo.c ├── dev_tty.c ├── basic_menu.c ├── read_immediately.c ├── kbhit.c └── magical_menu.c └── ch12_pthreads ├── run_all.sh ├── cancel_ex.c ├── simple_example.c ├── manythread_ex.c ├── attrs_ex.c ├── mutex_ex.c └── semaphore_ex.c /ch13_pipes/fifo/fifo_serverclient/Makefile: -------------------------------------------------------------------------------- 1 | all: server client 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | */*.o 2 | */*/*.o 3 | a.out 4 | */exe 5 | */*/exe 6 | *.cdb 7 | -------------------------------------------------------------------------------- /ch11_sigsprocs/upper: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stroxler/beginning_linux_programming/HEAD/ch11_sigsprocs/upper -------------------------------------------------------------------------------- /ch8_mysql/cd_prog_ch8/run.sh: -------------------------------------------------------------------------------- 1 | mysql -u trox -ptrox < schema.sql 2 | mysql -u trox -ptrox < sample_data.sql 3 | make 4 | ./app 5 | rm app 6 | -------------------------------------------------------------------------------- /ch14_sem_shmem_mqs/mq/run.sh: -------------------------------------------------------------------------------- 1 | gcc consumer.c -o c 2 | gcc producer.c -o p 3 | 4 | ./c & 5 | ./p 6 | 7 | sleep 5 8 | 9 | rm c 10 | rm p 11 | 12 | -------------------------------------------------------------------------------- /ch14_sem_shmem_mqs/shm/run.sh: -------------------------------------------------------------------------------- 1 | gcc consumer_creator.c -o cc 2 | gcc producer.c -o p 3 | 4 | ./cc & 5 | ./p 6 | 7 | sleep 5 8 | 9 | rm cc 10 | rm p 11 | 12 | -------------------------------------------------------------------------------- /ch13_pipes/fifo/fifo_serverclient/run.sh: -------------------------------------------------------------------------------- 1 | set -x 2 | 3 | make all 4 | 5 | ./server & 6 | 7 | sleep 1 8 | 9 | ./client 10 | 11 | rm server 12 | rm client 13 | -------------------------------------------------------------------------------- /ch8_mysql/cd_prog_ch8/makefile: -------------------------------------------------------------------------------- 1 | all: app 2 | 3 | app: app_mysql.c app_testdriver.c app_mysql.h 4 | gcc -g -o app -I/usr/include/mysql app_mysql.c app_testdriver.c -lmysqlclient -L/usr/lib/mysql 5 | 6 | 7 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Source code pulled mostly from the book, organized by chapters. I made 2 | some minor modifications to combine examples, and added a lot of code 3 | comments so that the code would be closer to self-explanatory. 4 | -------------------------------------------------------------------------------- /ch14_sem_shmem_mqs/shm/common.h: -------------------------------------------------------------------------------- 1 | /* A common header file to describe the shared memory we wish to pass about. */ 2 | 3 | #define TEXT_SZ 2048 4 | 5 | struct shared_use_st { 6 | int written_by_you; 7 | char some_text[TEXT_SZ]; 8 | }; 9 | 10 | -------------------------------------------------------------------------------- /ch11_sigsprocs/upper.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* Simple program that uppercases stdin and writes to stdout */ 4 | 5 | int main() { 6 | int ch; 7 | while((ch = getchar()) != EOF) { 8 | putchar(toupper(ch)); 9 | } 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /ch15_sockets/daytime_examples/inetd_notes: -------------------------------------------------------------------------------- 1 | Ubuntu 14.04 has inetd (xinetd) running by default, but it has the daytime 2 | service disabled. To run these examples, you first need to enable them: 3 | vim /etc/xinetd.d/daytime 4 | and then restart inetd: 5 | service xinetd reload 6 | -------------------------------------------------------------------------------- /ch15_sockets/note_on_run_scripts: -------------------------------------------------------------------------------- 1 | My attempt at killing the jobs isn't working. I tried awk with 2 | both {print $2} and {print $3}, and neither one seems to reliably 3 | kill the server 4 | 5 | This would be a nice thing to fix on a later pass... at the moment I'm 6 | not motivated heh 7 | -------------------------------------------------------------------------------- /ch14_sem_shmem_mqs/sem/run.sh: -------------------------------------------------------------------------------- 1 | gcc sem_ex.c 2 | 3 | echo "Starting first process, which creates semaphore..." 4 | ./a.out 1 & 5 | echo "Starting next process..." 6 | ./a.out & 7 | echo "Starting next process..." 8 | ./a.out 9 | 10 | echo "Waiting for things to finish" 11 | sleep 10 12 | -------------------------------------------------------------------------------- /ch11_sigsprocs/signals.notes: -------------------------------------------------------------------------------- 1 | Starting on page 489 there's a long discussion of signals. Since they are 2 | more of a general-purpose tool, and I'm mostly interested in C for math, I'm 3 | not going to write any detailed notes on it. But refer there first, then to the 4 | docs, if you feel you need to know more about signals. 5 | -------------------------------------------------------------------------------- /ch8_mysql/manage_users.sql: -------------------------------------------------------------------------------- 1 | USE mysql; 2 | 3 | -- delete the old users 4 | DELETE FROM user WHERE user != 'root'; 5 | DELETE FROM user WHERE host != 'localhost'; 6 | 7 | -- add a new user with full permissions 8 | -- only allowed from localhost, password required. 9 | GRANT ALL ON *.* TO trox@localhost IDENTIFIED BY 'trox' 10 | -------------------------------------------------------------------------------- /ch7_data/dbm_runner.sh: -------------------------------------------------------------------------------- 1 | # you probably will need to do something like this on ubuntu: 2 | # apt-get install -y libgdbm-dev 3 | 4 | set -x 5 | 6 | # on some linuxes, you may need -I/usr/include/gdbm 7 | # on ubuntu you *must* precede -lgdbm with -lgdbm_compat to get ndbm 8 | gcc -Wall dbm_ex.c -lgdbm_compat -lgdbm 9 | ./a.out 10 | -------------------------------------------------------------------------------- /ch13_pipes/fifo/fifo_rw/run_fifo_rw_demo.sh: -------------------------------------------------------------------------------- 1 | set -x 2 | 3 | gcc fifo_reader_demo.c -o fifo_reader 4 | gcc fifo_writer_demo.c -o fifo_writer 5 | 6 | # make sure to start writer first, only because the reader 7 | # doesn't mkfifo if it doesn't exist, whereas writer does 8 | ./fifo_writer & 9 | sleep 1 10 | ./fifo_reader 11 | 12 | rm fifo_reader 13 | rm fifo_writer 14 | -------------------------------------------------------------------------------- /ch6_curses/debugging_notes: -------------------------------------------------------------------------------- 1 | The key to debugging the cd collection app: use valgrind. 2 | 3 | To compile, do 4 | gcc -g -Wall cd_coll_ch6.c -lcurses 5 | To run, do 6 | valgrind ./a.out 2> stderr.out 7 | 8 | (curses uses stdout for all output, so the valgrind data is unaffected). 9 | (Also, you can print any log messages to stderr and they will similarly go 10 | to the file.) 11 | -------------------------------------------------------------------------------- /ch13_pipes/fifo/fifos_in_shell.sh: -------------------------------------------------------------------------------- 1 | echo "making fifo..." 2 | mkfifo /tmp/fifo 3 | 4 | echo "starting the cat ..." 5 | cat < /tmp/fifo & 6 | 7 | echo "listing jobs..." 8 | jobs 9 | 10 | echo "echoing into the fifo..." 11 | echo "Data going into fifo... when echo exits, fifo is \ 12 | closed and cat will also terminate" > /tmp/fifo 13 | sleep 1 14 | 15 | echo "listing jobs again..." 16 | jobs 17 | -------------------------------------------------------------------------------- /ch7_data/cd_prog_dir/makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-Wall 2 | # on some distros just -lgdbm would work 3 | LDFLAGS=-lgdbm_compat -lgdbm 4 | # on some distros you might need INCLUDE=/usr/include/gdbm 5 | 6 | # make by itself does whatever is at the top 7 | all: clean application 8 | 9 | clean: 10 | rm -f application *.o 11 | 12 | application: app_ui.o cd_access.o 13 | gcc $(CFLAGS) -o application app_ui.o cd_access.o $(LDFLAGS) 14 | 15 | app_ui.o: app_ui.c cd_data.h 16 | 17 | cd_access.o: cd_access.c cd_data.h 18 | -------------------------------------------------------------------------------- /ch13_pipes/fifo/fifo_serverclient/protocol.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* Common data and typedefs for the server and client */ 11 | 12 | #define SERVER_FIFO_NAME "/tmp/serv_fifo" 13 | #define CLIENT_FIFO_NAME "/tmp/cli_%d_fifo" 14 | 15 | #define BUFFER_SIZE 20 16 | 17 | struct data_to_pass_st { 18 | pid_t client_pid; 19 | char some_data[BUFFER_SIZE - 1]; 20 | }; 21 | -------------------------------------------------------------------------------- /ch15_sockets/unix_af/run.sh: -------------------------------------------------------------------------------- 1 | gcc client.c -o client 2 | gcc server.c -o server 3 | 4 | echo "Starting server... " 5 | ./server & 6 | sleep 1 # if you forget this, the clients might complain about the 7 | # socket not existing. 8 | 9 | echo "ls -lF /tmp/server.sock..." 10 | ls -lF /tmp/server.sock 11 | 12 | echo "Starting client..." 13 | ./client & 14 | ./client & 15 | ./client & 16 | ./client & 17 | 18 | sleep 1 19 | 20 | echo "Killing jobs..." 21 | jobs -l 22 | jobs -l | awk '{print $3}' | xargs kill -9 23 | 24 | rm client 25 | rm server 26 | -------------------------------------------------------------------------------- /ch15_sockets/forking_inet_ex/run.sh: -------------------------------------------------------------------------------- 1 | gcc client.c -o client 2 | gcc server.c -o server 3 | 4 | echo "Starting server... " 5 | ./server & 6 | sleep 1 # if you forget this, the clients might complain about the 7 | # socket not existing. 8 | 9 | echo "netstat -tulpn | grep 9734" 10 | netstat -tulpn | grep 9734 11 | 12 | echo "Starting client..." 13 | ./client & 14 | ./client & 15 | ./client & 16 | ./client & 17 | 18 | sleep 1 19 | 20 | echo "Killing jobs..." 21 | jobs -l 22 | jobs -l | awk '{print $3}' | xargs kill -9 23 | 24 | rm client 25 | rm server 26 | -------------------------------------------------------------------------------- /ch15_sockets/simple_inet_ex/run.sh: -------------------------------------------------------------------------------- 1 | gcc client.c -o client 2 | gcc server.c -o server 3 | 4 | echo "Starting server... " 5 | ./server & 6 | sleep 1 # if you forget this, the clients might complain about the 7 | # socket not existing. 8 | 9 | echo "netstat -tulpn | grep 9734" 10 | netstat -tulpn | grep 9734 11 | 12 | echo "Starting client..." 13 | ./client & 14 | ./client & 15 | ./client & 16 | ./client & 17 | 18 | sleep 1 19 | 20 | echo "Killing jobs..." 21 | jobs -l 22 | jobs -l | awk '{print $3}' | xargs kill -9 23 | 24 | rm client 25 | rm server 26 | -------------------------------------------------------------------------------- /ch15_sockets/select_inet_ex/run.sh: -------------------------------------------------------------------------------- 1 | gcc client.c -o client 2 | gcc server.c -o server 3 | 4 | echo "Starting server... " 5 | ./server & 6 | sleep 1 # if you forget this, the clients might complain about the 7 | # socket not existing. 8 | 9 | echo "netstat -tulpn | grep 9734" 10 | netstat -tulpn | grep 9734 11 | 12 | echo "Starting client..." 13 | ./client & 14 | ./client & 15 | ./client & 16 | ./client & 17 | 18 | sleep 1 19 | 20 | echo "Killing jobs..." 21 | jobs -l 22 | jobs -l | awk '{print $3}' 23 | jobs -l | awk '{print $3}' | xargs kill -9 24 | 25 | rm client 26 | rm server 27 | -------------------------------------------------------------------------------- /ch8_mysql/cd_prog_ch8/schema.sql: -------------------------------------------------------------------------------- 1 | DROP DATABASE IF EXISTS blpcd; 2 | CREATE DATABASE blpcd; 3 | USE blpcd; 4 | 5 | CREATE TABLE cd ( 6 | id INTEGER AUTO_INCREMENT NOT NULL PRIMARY KEY, 7 | title VARCHAR(70) NOT NULL, 8 | artist_id INTEGER NOT NULL, 9 | catalogue VARCHAR(30) NOT NULL, 10 | notes VARCHAR(100) 11 | ); 12 | 13 | CREATE TABLE artist ( 14 | id INTEGER AUTO_INCREMENT NOT NULL PRIMARY KEY, 15 | name VARCHAR(100) NOT NULL 16 | ); 17 | 18 | CREATE TABLE track ( 19 | track_id INTEGER NOT NULL, 20 | cd_id INTEGER NOT NULL, 21 | title VARCHAR(70), 22 | PRIMARY KEY(cd_id, track_id) 23 | ); 24 | -------------------------------------------------------------------------------- /ch4_env/hostinfo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | char computer[256]; 8 | struct utsname uts; 9 | 10 | // both of these funcs accept pointers and return codes, 0 for success 11 | if (gethostname(computer, 256) !=0 || uname(&uts) < 0) { 12 | fprintf(stderr, "Failed to get hostname or uname\n"); 13 | exit(1); 14 | } 15 | 16 | printf("Computer host name is %s\n", computer); 17 | printf("System is %s on %s hardware\n", uts.sysname, uts.machine); 18 | printf("Nodename is %s\n", uts.nodename); 19 | printf("Version is %s, %s\n", uts.release, uts.version); 20 | } 21 | -------------------------------------------------------------------------------- /ch13_pipes/pipe/pipe_exec/run_pipe_exec.sh: -------------------------------------------------------------------------------- 1 | # Start up a parent-child example of using exec and a pipe. 2 | # The messages print the pid, which is nice :) 3 | set -x 4 | gcc pipe_exec_parent.c 5 | gcc pipe_exec_child.c -o pipe_exec_child 6 | ./a.out 7 | sleep 1 # without this, it seems sometimes the child doesn't run. 8 | # my guess is, this is what happens: 9 | # bash does indeed block as long as ./a.out is running 10 | # but, as soon as the parent process exits, bash resumes. It doesn't 11 | # wait for grandchild processes to finish 12 | # as a result, it's possible to hit the rm statement before the 13 | # child process has had a chance to call execl() 14 | # putting this sleep 1 here fixes the issue 15 | rm pipe_exec_child 16 | -------------------------------------------------------------------------------- /ch13_pipes/popen/popen_write.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* Like read_ex0, this is a minimal example of popen, except this time we 7 | * popen as "w", which means the new process's stdin will come from the 8 | * FILE we create. 9 | * 10 | * In this case we pipe to an awk program that will print just the second 11 | * work ("upon") 12 | */ 13 | 14 | int main() { 15 | FILE *write_fp; 16 | char buffer[BUFSIZ + 1]; 17 | 18 | sprintf(buffer, "Once upon a time, there was...\n"); 19 | write_fp = popen("awk '{print $2}'", "w"); 20 | if (write_fp != NULL) { 21 | fwrite(buffer, sizeof(char), strlen(buffer), write_fp); 22 | pclose(write_fp); 23 | exit(EXIT_SUCCESS); 24 | } 25 | exit(EXIT_FAILURE); 26 | } 27 | -------------------------------------------------------------------------------- /ch14_sem_shmem_mqs/sem/semun.h: -------------------------------------------------------------------------------- 1 | /* This header file isn't needed in a lot of computers, but the 2 | * X/Open standar that defines this semaphore library says that the 3 | * union need not be provided in standard library headers, so you should 4 | * include a file like this when you need them. 5 | */ 6 | 7 | 8 | #if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) 9 | /* union semun is defined by including */ 10 | #else 11 | /* according to X/OPEN we have to define it ourselves */ 12 | union semun { 13 | int val; /* value for SETVAL */ 14 | struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ 15 | unsigned short int *array; /* array for GETALL, SETALL */ 16 | struct seminfo *__buf; /* buffer for IPC_INFO */ 17 | }; 18 | #endif 19 | 20 | -------------------------------------------------------------------------------- /ch3_files/file_cp_3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | int main(int argc, char *argv[]) { 6 | char c; 7 | FILE *inf, *outf; 8 | 9 | if (argc != 3) { 10 | printf("Usage: %s ", argv[0]); 11 | exit(1); 12 | } 13 | 14 | // open file in read-only mode 15 | inf = fopen(argv[1], "r"); 16 | // open file in write-only mode 17 | // note that unlike the fdes version, we don't specify create 18 | // or permissions. I'm pretty sure it's defaulting them the same as 19 | // we hand-specified in our fdes examples 20 | outf = fopen(argv[2], "w"); 21 | 22 | // note that there's no need for a block; the FILEs already buffer 23 | while((c = fgetc(inf)) != EOF) { 24 | fputc(c, outf); 25 | } 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /ch11_sigsprocs/zombie.notes: -------------------------------------------------------------------------------- 1 | See page 477 and 478 for a discussion of zombie processes. The gist is that 2 | child processes never finish terminating until the parent exits or calls wait - 3 | the reason being that if they did, then the parent coudn't call wait(). 4 | 5 | You can see this by modifying the fork demo so that the parent terminates 6 | much later than the child; if you run ps in the meantime, you'll see a 7 | process with next to it - this is the child, which finished but is 8 | waiting for the parent to either call wait() or exit. 9 | 10 | This can be a problem if you fork many times and never wait(). It's an even 11 | bigger issue if the parent crashes without exiting - the child processes are 12 | inherited by init, which will eventually clean them up but it can take a long 13 | time. These are called zombie processes. 14 | -------------------------------------------------------------------------------- /ch8_mysql/run_examples.sh: -------------------------------------------------------------------------------- 1 | echo 2 | echo "Inserting sample data ...." 3 | mysql -u trox -ptrox < children.sql 4 | 5 | echo 6 | echo "Connection example 1 ...." 7 | gcc -I/usr/include/mysql connect_ex1.c -lmysqlclient 8 | ./a.out 9 | 10 | echo 11 | echo "Connection example 2 ...." 12 | gcc -I/usr/include/mysql connect_ex2.c -lmysqlclient 13 | ./a.out 14 | 15 | echo 16 | echo "Prior to any changes, data is ...." 17 | mysql -u trox -ptrox foo << EOF 18 | select * from children; 19 | EOF 20 | 21 | echo 22 | echo "Runing example mutation .... " 23 | gcc -I/usr/include/mysql mutate.c -lmysqlclient 24 | ./a.out 25 | 26 | echo 27 | echo "After mutation, data is ...." 28 | mysql -u trox -ptrox foo << EOF 29 | select * from children; 30 | EOF 31 | 32 | echo 33 | echo "Now, we demonstrate reading data in C ...." 34 | gcc -I/usr/include/mysql read_data.c -lmysqlclient 35 | ./a.out 36 | -------------------------------------------------------------------------------- /ch8_mysql/children.sql: -------------------------------------------------------------------------------- 1 | -- first, set up a database. We'll call it foo since the book does 2 | DROP DATABASE IF EXISTS foo; 3 | CREATE DATABASE foo; 4 | USE foo; 5 | 6 | -- create a children table 7 | -- this is from the example starting on p 339 8 | 9 | CREATE TABLE children ( 10 | childno int(11) NOT NULL auto_increment, fname varchar(30), 11 | age int(11), 12 | PRIMARY KEY (childno) 13 | ); 14 | 15 | INSERT INTO children(childno, fname, age) VALUES (1,'Jenny',7); 16 | INSERT INTO children(childno, fname, age) VALUES (2,'Andrew',17); 17 | INSERT INTO children(childno, fname, age) VALUES (3,'Gavin',8); 18 | INSERT INTO children(childno, fname, age) VALUES (4,'Duncan',6); 19 | INSERT INTO children(childno, fname, age) VALUES (5,'Emma',4); 20 | INSERT INTO children(childno, fname, age) VALUES (6,'Alex',15); 21 | INSERT INTO children(childno, fname, age) VALUES (7,'Adrian',9); 22 | -------------------------------------------------------------------------------- /ch13_pipes/popen/popen_read.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* Simple example of a read popen. 7 | * popen("command", "mode") creates a FILE similar to fopen(), except that 8 | * instead of a filename, you give it a command. 9 | * 10 | * It only allows "r" or "w" as modes, and automatically sets you to piping 11 | * that processes stdout or stdin depending on whether you have "r" or "w". 12 | */ 13 | 14 | int main() { 15 | FILE *read_fp; 16 | char buffer[BUFSIZ + 1]; // bufsize is a constant set in stdio 17 | int chars_read; 18 | memset(buffer, '\0', sizeof(buffer)); 19 | read_fp = popen("uname -a", "r"); 20 | if (read_fp != NULL) { 21 | chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp); 22 | if (chars_read > 0) { 23 | printf("Output of \"uname -a\" was:-\n%s\n", buffer); 24 | } 25 | pclose(read_fp); 26 | exit(EXIT_SUCCESS); 27 | } 28 | exit(EXIT_FAILURE); 29 | } 30 | -------------------------------------------------------------------------------- /ch13_pipes/pipe/pipe_exec/pipe_exec_child.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | /* This is the consumer program that gets started via an execl() call in 8 | * the `pipe_exec_parent.c` code. 9 | * 10 | * It accepts a single command line argument which should be a string 11 | * representation of the file descriptor to use as the read end of the pipe 12 | * (the actual file descriptor is preserved by exec()). 13 | * 14 | * Once it has scanned the file descriptor, and also zeroed out the buffer, 15 | * it just reads in the data and prints a message. 16 | */ 17 | 18 | int main(int argc, char *argv[]) { 19 | int data_processed; 20 | char buffer[BUFSIZ + 1]; 21 | int file_descriptor; 22 | 23 | memset(buffer, '\0', sizeof(buffer)); 24 | sscanf(argv[1], "%d", &file_descriptor); 25 | data_processed = read(file_descriptor, buffer, BUFSIZ); 26 | 27 | printf("%d - read %d bytes: %s\n", getpid(), data_processed, buffer); 28 | exit(EXIT_SUCCESS); 29 | } 30 | -------------------------------------------------------------------------------- /ch8_mysql/connect_ex1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "mysql.h" 5 | 6 | /* Minimal example of creating a mysql connection. 7 | */ 8 | 9 | int main(int argc, char *argv[]) { 10 | MYSQL *conn_ptr; 11 | 12 | // mysql initializes the MYSQL struct *before* making a connection. Don't 13 | // pass a null pointer to mysql_real_connect! 14 | conn_ptr = mysql_init(NULL); 15 | if (!conn_ptr) { 16 | fprintf(stderr, "mysql_init failed\n"); 17 | return EXIT_FAILURE; 18 | } 19 | 20 | // args are host, username, password, db 21 | // port number (0 maps to the default 3306), unix_socket_name, 22 | // and flags. 23 | conn_ptr = mysql_real_connect(conn_ptr, "localhost", "trox", "trox", "foo", 24 | 0, NULL, 0); 25 | if (conn_ptr) { 26 | printf("Connectiion succeeded\n"); 27 | } else { 28 | printf("Connectiion failed\n"); 29 | } 30 | 31 | // cleanup 32 | mysql_close(conn_ptr); 33 | return EXIT_SUCCESS; 34 | } 35 | -------------------------------------------------------------------------------- /ch8_mysql/install_mysql.sh: -------------------------------------------------------------------------------- 1 | # install using apt. You'll get prompted for 'root's password. 2 | apt-get update 3 | apt-get -y install mysql-server 4 | apt-get -y install libmysqlclient-dev 5 | 6 | # this drops the header file in 7 | # /usr/include/mysql/mysql.h 8 | # and the client libraries in 9 | # /usr/lib/x86_64-linux-gnu/libmysqlclient* 10 | 11 | 12 | # to log in as root, do the following: 13 | ## mysql -u root -p mysql 14 | # which will lead to you being prompted for the password, then dropping 15 | # into a mysql shell. 16 | # alternatively, you can use the -p with an argument to provide the password 17 | # on the command line. This isn't secure if someone can see your history, but 18 | # convenient if you aren't concerned. If you do this, don't put a space, e.g. 19 | ## mysql -u root -proot mysql 20 | 21 | # if you want to run a script in mysql, there's no equialent of psql -f; just 22 | # use the < shell operator to set a file as stdin 23 | 24 | # the conf file that runs things winds up, on ubuntu with apt, living at 25 | ## /etc/mysql/my.cnf 26 | -------------------------------------------------------------------------------- /ch5_term/password.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | /* Simple example of turning off echoing, as is generally done when 7 | * fetching passwords from the terminal. 8 | */ 9 | #define PASSWORD_LEN 8 10 | 11 | int main() { 12 | // if you are going to change termios settings, *always* save the old ones 13 | // and reset! 14 | struct termios initialsettings, newsettings; 15 | char password[PASSWORD_LEN + 1]; 16 | 17 | tcgetattr(fileno(stdin), &initialsettings); 18 | newsettings = initialsettings; 19 | // turn off echoing by using (~ECHO) as a bitmask 20 | newsettings.c_lflag &= ~ECHO; 21 | fprintf(stdout, "Enter password: "); 22 | if (tcsetattr(fileno(stdin), TCSAFLUSH, &newsettings) != 0) { 23 | fprintf(stderr, "Could not set attributes!\n"); 24 | } else { 25 | fgets(password, PASSWORD_LEN, stdin); 26 | tcsetattr(fileno(stdin), TCSANOW, &initialsettings); 27 | fprintf(stdout, "\nYou entered \"%s\".\n", password); 28 | } 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /ch3_files/fdes_unbuffered_cp_1.c: -------------------------------------------------------------------------------- 1 | #include // has to come first b/c it can affect the others 2 | // via flags 3 | #include 4 | #include 5 | #include // I think this is only used for exit 6 | #include // not used for the example, just the usage message 7 | 8 | 9 | int main(int argc, char *argv[]) { 10 | char c; 11 | int infdes, outfdes; 12 | 13 | if (argc != 3) { 14 | printf("Usage: %s ", argv[0]); 15 | exit(1); 16 | } 17 | 18 | // open file in read-only mode 19 | infdes = open(argv[1], O_RDONLY); 20 | // open file in write-only mode, creating if it doesn't exist and 21 | // assigning the user read and write permissions if umask allows it (the 22 | // actual permissions are the pointwise min of umask and what's requested) 23 | outfdes = open(argv[2], O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); 24 | 25 | while(read(infdes, &c, 1) == 1) { 26 | write(outfdes, &c, 1); 27 | } 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /ch7_data/basic_filelock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /** 8 | * Demo of simple file locking (lock based on existance of the lock file) 9 | * 10 | * To run it, do something like 11 | * ./a.out & 12 | * ./a.out 13 | * and you will see both processes printing to stdout. 14 | */ 15 | const char *LOCK_FILE = "/tmp/LCK.test"; 16 | 17 | int main() { 18 | int fdesc; 19 | int tries = 10; 20 | while (tries-- > 0) { 21 | // the flags used here are what make it work 22 | fdesc = open(LOCK_FILE, O_RDWR | O_CREAT | O_EXCL, 0444); 23 | if (fdesc == -1) { 24 | printf("%d: - Lock already present, waiting...\n", getpid()); 25 | sleep(2); 26 | } else { 27 | printf("%d: - I have exclusive access\n", getpid()); 28 | sleep(4); 29 | printf("%d: - closing file now\n", getpid()); 30 | close(fdesc); 31 | unlink(LOCK_FILE); 32 | break; 33 | } 34 | } 35 | 36 | exit(EXIT_SUCCESS); 37 | } 38 | -------------------------------------------------------------------------------- /ch3_files/fdes_buffered_cp_2.c: -------------------------------------------------------------------------------- 1 | #include // has to come first b/c it can affect the others 2 | // via flags 3 | #include 4 | #include 5 | #include // I think this is only used for exit 6 | #include // not used for the example, just the usage message 7 | 8 | 9 | int main(int argc, char *argv[]) { 10 | char block[1024]; 11 | int infdes, outfdes; 12 | int nread; 13 | 14 | if (argc != 3) { 15 | printf("Usage: %s ", argv[0]); 16 | exit(1); 17 | } 18 | 19 | // open file in read-only mode 20 | infdes = open(argv[1], O_RDONLY); 21 | // open file in write-only mode, creating if it doesn't exist and 22 | // assigning the user read and write permissions if umask allows it (the 23 | // actual permissions are the pointwise min of umask and what's requested) 24 | outfdes = open(argv[2], O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); 25 | 26 | while((nread = read(infdes, block, 1)) > 0) { 27 | write(outfdes, block, nread); 28 | } 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /ch12_pthreads/run_all.sh: -------------------------------------------------------------------------------- 1 | # You need to either #define _REENTRANT or use a -D in order to make standard 2 | # C library calls be thread safe. It does a few things, including forcing most 3 | # macros to become functions. 4 | 5 | # on some machines you might need -I/user/include/nptl and -L/usr/lib/nptl 6 | 7 | echo 8 | echo "Running a simple thread example (just spin off and join, no syncing) ..." 9 | gcc -D_REENTRANT simple_example.c -lpthread 10 | ./a.out 11 | 12 | echo 13 | echo "Running a simple (underengineered) semaphore demo ..." 14 | gcc -D_REENTRANT semaphore_ex.c -lpthread 15 | ./a.out 16 | 17 | echo 18 | echo "Running a simple mutex demo ..." 19 | gcc -D_REENTRANT mutex_ex.c -lpthread 20 | ./a.out 21 | 22 | echo 23 | echo "Running a simple example using thread attrs ..." 24 | gcc -D_REENTRANT attrs_ex.c -lpthread 25 | ./a.out 26 | 27 | echo 28 | echo "Running a simple example using thread cancel ..." 29 | gcc -D_REENTRANT cancel_ex.c -lpthread 30 | ./a.out 31 | 32 | echo 33 | echo "Running a simple example using many threads ..." 34 | echo " -> it uses a dumb casting trick, so expect some warnings from gcc...." 35 | gcc -D_REENTRANT manythread_ex.c -lpthread 36 | ./a.out 37 | -------------------------------------------------------------------------------- /ch11_sigsprocs/basic_signal_demo.c: -------------------------------------------------------------------------------- 1 | /* In this demo, we use the initial signal() call (in main) to set our 2 | * program up to call ouch when SIGINT is called. The signal is also passed 3 | * to ouch(), which isn't needed in this example because we only bound one 4 | * signal, but in general we could have bound ouch() to many signals. 5 | * 6 | * Inside the ouch() function, we remap the signal handler back to the 7 | * default handler (you can also map it to SIG_IGN, which would ignore signals) 8 | * 9 | * To use it, try pressing ctl-c once, and you'll get a message. If you press 10 | * it again, the program will exit as usual. 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | void ouch(int sig) { 18 | printf("OUCH! - I got signal %d\n", sig); 19 | (void) signal(SIGINT, SIG_DFL); 20 | } 21 | 22 | /* The main function has to intercept the SIGINT signal generated when we type Ctrl-C . 23 | For the rest of the time, it just sits in an infinite loop, 24 | printing a message once a second. */ 25 | 26 | int main() { 27 | (void) signal(SIGINT, ouch); 28 | 29 | while(1) { 30 | printf("Hello World!\n"); 31 | sleep(1); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ch7_data/cd_prog_dir/cd_data.h: -------------------------------------------------------------------------------- 1 | /* define the dbm 'schema' */ 2 | #define CAT_LEN 30 3 | #define TITLE_LEN 70 4 | #define TYPE_LEN 30 5 | #define ARTIST_LEN 70 6 | #define TTEXT_LEN 70 7 | 8 | // the catalog 'table' datatype 9 | typedef struct { 10 | char catalog[CAT_LEN + 1]; 11 | char title[TITLE_LEN + 1]; 12 | char type[TYPE_LEN + 1]; 13 | char artist[ARTIST_LEN + 1]; 14 | } cdc_entry; 15 | 16 | // the tracks 'table' datatype 17 | typedef struct { 18 | char catalog[CAT_LEN + 1]; 19 | int track_no; 20 | char track_txt[TTEXT_LEN + 1]; 21 | } cdt_entry; 22 | 23 | /* Initialization and termination functions */ 24 | int database_initialize(const int new_database); 25 | void database_close(void); 26 | 27 | /* simple data retrieval */ 28 | cdc_entry get_cdc_entry(const char *cd_catalog_ptr); 29 | cdt_entry get_cdt_entry(const char *cd_catalog_ptr, const int track_no); 30 | 31 | /* data addition */ 32 | int add_cdc_entry(const cdc_entry entry_to_add); 33 | int add_cdt_entry(const cdt_entry entry_to_add); 34 | 35 | /* data deletion */ 36 | int del_cdc_entry(const char *cd_catalog_ptr); 37 | int del_cdt_entry(const char *cd_catalog_ptr, const int track_no); 38 | 39 | /* search */ 40 | cdc_entry search_cdc_entry(const char *cd_catalog_ptr, int *first_call_ptr); 41 | -------------------------------------------------------------------------------- /ch11_sigsprocs/basic_sigaction_demo.c: -------------------------------------------------------------------------------- 1 | /* This program is very similar to basic_signal_demo, except it uses the 2 | * newer sigaction api, which is supposedly to be preferred. 3 | * 4 | * To use the `sigaction` func, you map a signal to a handler via a `sigaction` 5 | * struct. (the name is overloaded - this is our first clear demo that C uses 6 | * distinct namespaces for functions and dtypes). 7 | * 8 | * before you call sigaction, you need to set the handler, mask and flags of 9 | * the sigaction struct. Use `sigemptyset` and flags of 0 for basic behavior; 10 | * read the docs if you want to know more (we are just demoing the api here). 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | struct sigaction old_act; 18 | 19 | void ouch(int sig) { 20 | printf("OUCH! - I got signal %d\n", sig); 21 | sigaction(SIGINT, &old_act, NULL); 22 | } 23 | 24 | int main() { 25 | struct sigaction act; 26 | 27 | act.sa_handler = ouch; 28 | sigemptyset(&act.sa_mask); 29 | act.sa_flags = 0; 30 | 31 | // the third arg, if non-NULL, is a place to save the old action. It's 32 | // ignored if null. 33 | sigaction(SIGINT, &act, &old_act); 34 | 35 | while(1) { 36 | printf("Hello World!\n"); 37 | sleep(1); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ch5_term/terminfo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* This is an example of working with terminal info. The routines here 7 | * provide some of the low-level control used by curses. 8 | * 9 | * The setupterm() call lets you set the terminal (e.g., 'xterm'), or if 10 | * you pass NULL as the first parameter it uses the value of '$TERM'. 11 | * 12 | * It creates a structure that the other functions can read, with information 13 | * that can be used by libraries like curses. 14 | * see the docs: http://linux.die.net/man/3/setupterm 15 | * ... no need to learn this well, generally you'll use higher-level 16 | * curses apis to do real work. 17 | * 18 | * NOTE: this has to be compiled with -lcurses, it isn't linked by 19 | * default (like math) 20 | */ 21 | int main() { 22 | int nrows, ncolumns; 23 | // the (int *) 0 looks kind of weird, but only because we are ignoring 24 | // error conditions here. See the docs for how the return value and that 25 | // argument together provide error handling. 26 | setupterm(NULL, fileno(stdout), (int *)0); 27 | nrows = tigetnum("lines"); 28 | ncolumns = tigetnum("cols"); 29 | printf("This terminal has %d rows and %d columns\n", nrows, ncolumns); 30 | exit(0); 31 | } 32 | -------------------------------------------------------------------------------- /ch13_pipes/high_level_notes_on_popen_pipe_fifo: -------------------------------------------------------------------------------- 1 | We discuss three 'types' of pipes: popen, pipe, and fifo 2 | 3 | popen and pipe actually use the same kind of anonymous pipe as a shell 4 | command with a | in it uses under the hood, the difference is in the view: 5 | - popen lets you boot up a command via a shell (you create the shell process, 6 | so there's extra overhead) as if opening a file, and either read or write 7 | to it. It handles mapping the pipe created to either stdin or stdout of the 8 | process you create, based on the mode. Very simple. 9 | - pipe creates two raw file descriptors in your process, which you can 10 | then use with the read and write functions. You then do magic using 11 | fork, exec, and dup/dup2 to deal with those file descriptors as needed. 12 | 13 | A fifo is also called a "named pipe" 14 | 15 | They are basically the same thing as a pipe in terms of transferring data: 16 | they provide a queue which can be read/written using file descriptors, similar 17 | to files except that non-linear ops like seek won't always work. 18 | 19 | The difference is that the operating system also maps the pipe to a location 20 | in the file system, which allows you to use open() as if the fifo were an 21 | ordinary file. 22 | 23 | You can make a fifo from the command line using mkfifo, which is the shell 24 | ersion of the C library mkfifo() funciton. 25 | -------------------------------------------------------------------------------- /ch13_pipes/cd_app_fifo/Makefile: -------------------------------------------------------------------------------- 1 | ll: server client 2 | 3 | CC=cc 4 | CFLAGS= -Wall # I got rid of -pedantic b/c it doesn't like C++ comments (//) 5 | 6 | # For debugging un-comment the next line 7 | # DFLAGS=-DDEBUG_TRACE=1 -g 8 | 9 | 10 | # Where, and which version, of dbm are we using. 11 | # This assumes gdbm is pre-installed in a standard place, but we are 12 | # going to use the gdbm compatability routines, that make it emulate ndbm. 13 | # We do this because ndbm is the 'most standard' of the dbm versions. 14 | # Depending on your distribution, these may need changing. 15 | 16 | DBM_INC_PATH=/usr/include/gdbm 17 | DBM_LIB_PATH=/usr/lib 18 | DBM_LIB_FILE=-lgdbm_compat -lgdbm 19 | # On some distributions you may need to change the above line to include 20 | # the compatibility library, as shown below. 21 | # DBM_LIB_FILE=-lgdbm 22 | 23 | .c.o: 24 | $(CC) $(CFLAGS) -I$(DBM_INC_PATH) $(DFLAGS) -c $< 25 | 26 | app_ui.o: app_ui.c cd_data.h 27 | cd_dbm.o: cd_dbm.c cd_data.h 28 | client_f.o: clientif.c cd_data.h cliserv.h 29 | pipe_imp.o: pipe_imp.c cd_data.h cliserv.h 30 | server.o: server.c cd_data.h cliserv.h 31 | 32 | 33 | client: app_ui.o clientif.o pipe_imp.o 34 | $(CC) -o client $(DFLAGS) app_ui.o clientif.o pipe_imp.o 35 | 36 | server: server.o cd_dbm.o pipe_imp.o 37 | $(CC) -o server -L$(DBM_LIB_PATH) $(DFLAGS) server.o cd_dbm.o pipe_imp.o $(DBM_LIB_FILE) 38 | 39 | clean: 40 | rm -f server client *.o *~ 41 | -------------------------------------------------------------------------------- /ch14_sem_shmem_mqs/mq/producer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #define MAX_TEXT 512 10 | 11 | /* producer that sends data into the message queue, to be read by 12 | * the consumer.c program. 13 | * 14 | * Note that we don't call msgctl with IPC_RMID here; the queue is only 15 | * cleared from the consumer side. 16 | */ 17 | 18 | struct my_msg_st { 19 | long int my_msg_type; 20 | char some_text[MAX_TEXT]; 21 | }; 22 | 23 | int main() { 24 | int running = 1; 25 | struct my_msg_st some_data; 26 | int msgid; 27 | char buffer[BUFSIZ]; 28 | 29 | msgid = msgget((key_t)1234, 0666 | IPC_CREAT); 30 | 31 | if (msgid == -1) { 32 | fprintf(stderr, "msgget failed with error: %d\n", errno); 33 | exit(EXIT_FAILURE); 34 | } 35 | 36 | while(running) { 37 | printf("Enter some text: "); 38 | fgets(buffer, BUFSIZ, stdin); 39 | some_data.my_msg_type = 1; 40 | strcpy(some_data.some_text, buffer); 41 | 42 | if (msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0) == -1) { 43 | fprintf(stderr, "msgsnd failed\n"); 44 | exit(EXIT_FAILURE); 45 | } 46 | if (strncmp(buffer, "end", 3) == 0) { 47 | running = 0; 48 | } 49 | } 50 | 51 | exit(EXIT_SUCCESS); 52 | } 53 | 54 | -------------------------------------------------------------------------------- /ch14_sem_shmem_mqs/cd_app_mq/Makefile: -------------------------------------------------------------------------------- 1 | ll: server client 2 | 3 | CC=cc 4 | CFLAGS= -Wall # I got rid of -pedantic b/c it doesn't like C++ comments (//) 5 | 6 | # For debugging un-comment the next line 7 | # DFLAGS=-DDEBUG_TRACE=1 -g 8 | 9 | 10 | # Where, and which version, of dbm are we using. 11 | # This assumes gdbm is pre-installed in a standard place, but we are 12 | # going to use the gdbm compatability routines, that make it emulate ndbm. 13 | # We do this because ndbm is the 'most standard' of the dbm versions. 14 | # Depending on your distribution, these may need changing. 15 | 16 | DBM_INC_PATH=/usr/include/gdbm 17 | DBM_LIB_PATH=/usr/lib 18 | DBM_LIB_FILE=-lgdbm_compat -lgdbm 19 | # On some distributions you may need to change the above line to include 20 | # the compatibility library, as shown below. 21 | # DBM_LIB_FILE=-lgdbm 22 | 23 | .c.o: 24 | $(CC) $(CFLAGS) -I$(DBM_INC_PATH) $(DFLAGS) -c $< 25 | 26 | app_ui.o: app_ui.c cd_data.h 27 | cd_dbm.o: cd_dbm.c cd_data.h 28 | client_f.o: clientif.c cd_data.h cliserv.h 29 | mqueue_imp.o: mqueue_imp.c cd_data.h cliserv.h 30 | server.o: server.c cd_data.h cliserv.h 31 | 32 | 33 | client: app_ui.o clientif.o mqueue_imp.o 34 | $(CC) -o client $(DFLAGS) app_ui.o clientif.o mqueue_imp.o 35 | 36 | server: server.o cd_dbm.o mqueue_imp.o 37 | $(CC) -o server -L$(DBM_LIB_PATH) $(DFLAGS) server.o cd_dbm.o mqueue_imp.o $(DBM_LIB_FILE) 38 | 39 | clean: 40 | rm -f server client *.o *~ 41 | -------------------------------------------------------------------------------- /ch15_sockets/forking_inet_ex/client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* Simple example of a client program, communicating via inet. 11 | * 12 | * This client is identical to the one from simple_inet_ex; only the 13 | * server has changed. 14 | */ 15 | 16 | int main() { 17 | int sockfd; 18 | int len; 19 | struct sockaddr_in address; 20 | int result; 21 | char ch = 'A'; 22 | 23 | // create the file descriptor. We need to specify the domain and the 24 | // type (STREAM or DGRAM), but not the address. 25 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 26 | 27 | // create the address and connect 28 | address.sin_family = AF_INET; 29 | address.sin_addr.s_addr = inet_addr("127.0.0.1"); 30 | address.sin_port = htons(9734); 31 | len = sizeof(address); 32 | // note that the cast hasn't changed.. in fact this line is identical 33 | // to the unix_af variant, as is everything below. 34 | result = connect(sockfd, (struct sockaddr *)&address, len); 35 | if(result == -1) { 36 | perror("oops: client1"); 37 | exit(1); 38 | } 39 | 40 | write(sockfd, &ch, 1); 41 | read(sockfd, &ch, 1); 42 | printf("char from server = %c\n", ch); 43 | close(sockfd); 44 | exit(0); 45 | } 46 | 47 | -------------------------------------------------------------------------------- /ch15_sockets/select_inet_ex/client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* Simple example of a client program, communicating via inet. 11 | * 12 | * This client is identical to the one from simple_inet_ex; only the 13 | * server has changed. 14 | */ 15 | 16 | int main() { 17 | int sockfd; 18 | int len; 19 | struct sockaddr_in address; 20 | int result; 21 | char ch = 'A'; 22 | 23 | // create the file descriptor. We need to specify the domain and the 24 | // type (STREAM or DGRAM), but not the address. 25 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 26 | 27 | // create the address and connect 28 | address.sin_family = AF_INET; 29 | address.sin_addr.s_addr = inet_addr("127.0.0.1"); 30 | address.sin_port = htons(9734); 31 | len = sizeof(address); 32 | // note that the cast hasn't changed.. in fact this line is identical 33 | // to the unix_af variant, as is everything below. 34 | result = connect(sockfd, (struct sockaddr *)&address, len); 35 | if(result == -1) { 36 | perror("oops: client1"); 37 | exit(1); 38 | } 39 | 40 | write(sockfd, &ch, 1); 41 | read(sockfd, &ch, 1); 42 | printf("char from server = %c\n", ch); 43 | close(sockfd); 44 | exit(0); 45 | } 46 | 47 | -------------------------------------------------------------------------------- /ch6_curses/subwindows.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* In curses, windows can have subwindows. Here's a simple demo program. 6 | */ 7 | int main() { 8 | WINDOW *sub_window; 9 | int x_loop, y_loop; 10 | int counter; 11 | char a_letter = '0'; 12 | 13 | initscr(); 14 | 15 | for(y_loop = 0; y_loop < LINES - 1; y_loop++) { 16 | for(x_loop = 0; x_loop < COLS - 1; x_loop++) { 17 | mvwaddch(stdscr, y_loop, x_loop, a_letter); 18 | a_letter++; 19 | if(a_letter > '9') a_letter = '0'; 20 | } 21 | } 22 | 23 | sub_window = subwin(stdscr, 12, 22, 10, 10); 24 | scrollok(sub_window, 1); // turn on 'scrolling' in the subwindow 25 | touchwin(stdscr); 26 | refresh(); 27 | sleep(1); 28 | 29 | werase(sub_window); // erase contents, that is, make it blank 30 | mvwprintw(sub_window, 2, 0, "%s", "This window will now scroll\n\n"); 31 | wrefresh(sub_window); 32 | sleep(1); 33 | 34 | /* print some stuff to see the scrolling effect */ 35 | for (counter = 1; counter < 10; counter++) { 36 | wprintw(sub_window, "%s", "The text is both wrapping and scrolling\n"); 37 | wrefresh(sub_window); 38 | sleep(1); 39 | } 40 | 41 | delwin(sub_window); 42 | touchwin(stdscr); 43 | refresh(); 44 | sleep(1); 45 | 46 | endwin(); 47 | exit(EXIT_SUCCESS); 48 | } 49 | -------------------------------------------------------------------------------- /ch8_mysql/connect_ex2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "mysql.h" 5 | 6 | /* Minimal example of failing to create a conection and printing errors 7 | */ 8 | 9 | int main(int argc, char *argv[]) { 10 | MYSQL my_conn; 11 | 12 | // our previous example passed NULL to mysql_init and used the returned 13 | // MYSQL* pointer. Here we only use the return value for its truthiness 14 | if (!mysql_init(&my_conn)) { 15 | fprintf(stderr, "mysql_init failed\n"); 16 | return EXIT_FAILURE; 17 | } 18 | 19 | // similar to mysql_init, this time we ignore the return value, except 20 | // to measure its truthiness for error detection 21 | if (mysql_real_connect( 22 | &my_conn, "localhost", "trox", "trox", "notadb", 0, NULL, 0)) { 23 | printf("Connectiion succeeded\n"); 24 | mysql_close(&my_conn); 25 | } else { 26 | fprintf(stderr, "Connectiion failed\n"); 27 | // mysql_errno tells us if there's a mysql error code in the global 28 | // namespace. mysql_error should only be called if there's an errno; it 29 | // translates the errno into a human-readable string. 30 | if (mysql_errno(&my_conn)) { 31 | fprintf(stderr, "Connection error: %d %s\n", 32 | mysql_errno(&my_conn), mysql_error(&my_conn)); 33 | } 34 | } 35 | 36 | // cleanup 37 | return EXIT_SUCCESS; 38 | } 39 | -------------------------------------------------------------------------------- /ch4_env/env_environ.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* this array of c-strings is guaranteed to be null-terminated 6 | * each entry is a string of the form "VAR=value" */ 7 | extern char **environ; 8 | 9 | int main(int argc, char *argv[]) { 10 | 11 | char *var = "HOME"; 12 | char *newvalue = "my_new_HOME_value"; 13 | char **env; 14 | 15 | printf("The value of %s is %s\n", var, getenv(var)); 16 | 17 | printf("\nThe whole env is.....\n"); 18 | env = environ; 19 | while (*env) { 20 | printf("\t%s\n", *env); 21 | env++; 22 | } 23 | 24 | // note that in general strlen is not safe. In this case we've 25 | // hard-coded both strings so we can guarantee \0-termination. 26 | char *assignment = malloc(strlen(var) + strlen(newvalue) + 2); 27 | if (!assignment) { 28 | fprintf(stderr, "out of memory"); 29 | } 30 | strcpy(assignment, var); 31 | strcat(assignment, "="); 32 | strcat(assignment, newvalue); 33 | printf("Changing the value of %s to %s\n", var, newvalue); 34 | int rcode = putenv(assignment); 35 | if (rcode != 0) { 36 | fprintf(stderr, "putenv failed"); 37 | free(assignment); 38 | exit(1); 39 | } 40 | 41 | printf("\nNow the whole env is.....\n"); 42 | env = environ; 43 | while (*env) { 44 | printf("\t%s\n", *env); 45 | env++; 46 | } 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /ch13_pipes/pipe/pipe_in_one_process.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* In contrast to popen(), which accepts a command and mode much like 7 | * fopen() does, and invokes a shell, pipe() provides low-level shell-free 8 | * pipes using raw file descriptors. 9 | * 10 | * In this example we demo the api in just one process (which is obviously 11 | * pointless except as a demo). The pipe() command takes an array 12 | * int fds[2] 13 | * and it sets fd[0] to be the file descriptor of the read end of a pipe, 14 | * while fd[1] is the write end of the pipe (this seems backward :( ) 15 | * ... it returns 0 if pipe opened properly, and -1 (plus sets errno) o/w. 16 | * 17 | * Here we open the pipe and talk with both ends from one process using the 18 | * low level read and write functions. 19 | */ 20 | 21 | 22 | int main() { 23 | int data_processed; 24 | int file_pipes[2]; 25 | const char some_data[] = "123"; 26 | char buffer[BUFSIZ + 1]; 27 | 28 | memset(buffer, '\0', sizeof(buffer)); 29 | 30 | if (pipe(file_pipes) == 0) { 31 | data_processed = write(file_pipes[1], some_data, strlen(some_data)); 32 | printf("Wrote %d bytes\n", data_processed); 33 | data_processed = read(file_pipes[0], buffer, BUFSIZ); 34 | printf("Read %d bytes: %s\n", data_processed, buffer); 35 | exit(EXIT_SUCCESS); 36 | } 37 | exit(EXIT_FAILURE); 38 | } 39 | -------------------------------------------------------------------------------- /ch4_env/userinfo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main() { 8 | uid_t uid; gid_t gid; 9 | char* whoami; 10 | struct passwd *pw; // it's not actually a password, it's a struct for an 11 | // entry the /etc/passwd file. 12 | 13 | // get the calling user's uid / gid 14 | uid = getuid(); 15 | gid = getgid(); 16 | 17 | // getlogin() is unsafe and deprecated. Does not work on my box, 18 | // it returns "(null)" as a string(!) 19 | whoami = getlogin(); 20 | printf("User's getlogin doesn't work: %s\n", whoami); 21 | printf("But getuid and getgid do... uid: %d gid:%d\n", uid, gid); 22 | 23 | // you can get the passwd struct for a user by uid, or by name 24 | pw = getpwuid(uid); 25 | printf("is pw null? %p\n", pw); 26 | printf("\nGot passwd entry for uid %d\n", uid); 27 | printf("\tname=%s, uid=%d, gid=%d, home=%s, shell=%s\n", 28 | pw->pw_name, pw->pw_uid, pw->pw_gid, pw->pw_dir, pw->pw_shell); 29 | 30 | // technically I think I'm leaking memory here: should be freeing the 31 | // old pw stuff first, but this is just an api demo... 32 | whoami = pw->pw_name; 33 | pw = getpwnam(whoami); 34 | printf("\nGot passwd entry for username %s\n", whoami); 35 | printf("\tname=%s, uid=%d, gid=%d, home=%s, shell=%s\n", 36 | pw->pw_name, pw->pw_uid, pw->pw_gid, pw->pw_dir, pw->pw_shell); 37 | 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /ch4_env/getopt.c: -------------------------------------------------------------------------------- 1 | // page 141 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | /* Suggested example call is ./a.out -i -lr 'hi there' -f fred.c -q */ 8 | 9 | int main(int argc, char *argv[]) { 10 | int opt; 11 | // the leading colon could be left off for raw functionality. The benefit 12 | // is that we can detect missing args as distinct from unknown options - if 13 | // you leave off the leading :, then both situations would cause the case 14 | // to match '?', so you can't distinguish. 15 | while ((opt = getopt(argc, argv, ":if:lr")) != -1) { 16 | switch (opt) { 17 | case 'i': 18 | case 'l': 19 | case 'r': 20 | printf("option: %c\n", opt); 21 | break; 22 | case 'f': 23 | printf("filename: %s\n", optarg); 24 | break; 25 | case ':': 26 | printf("option '%c' needs a value\n", optopt); 27 | break; 28 | case '?': 29 | printf("Unknown option: %c\n", optopt); 30 | break; 31 | } 32 | 33 | } 34 | // optind has the index of the last argv parsed by optarg 35 | // ... 36 | // this works even if the arg is before some options, because linux's 37 | // getopt rearranges for you. That's why `rm file -rf` works on 38 | // linux but not mac. 39 | for(; optind < argc; optind++) { 40 | printf("argument: %s\n", argv[optind]); 41 | } 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /ch8_mysql/cd_prog_ch8/app_mysql.h: -------------------------------------------------------------------------------- 1 | // a struct to represent a cd. Note it corresponds almost exactly to the sql 2 | // the only exception is that the artist_name, which has its own sql table, 3 | // is placed in the struct. 4 | struct current_cd_st { 5 | int artist_id; 6 | int cd_id; 7 | char artist_name[100]; 8 | char title[100]; 9 | char catalogue[100]; 10 | }; 11 | 12 | // a struct to hold track information. We allow 19 tracks at most, because the 13 | // code would be a lot more complex if this were dynamic. 14 | // (19, not 20, because an entry with '\0' in the 1st char is used to 15 | // indicate the end of the data) 16 | struct current_tracks_st { 17 | int cd_id; 18 | char track[20][100]; 19 | }; 20 | 21 | 22 | // a struct to hold the ids from a find_cd command. We don't allow more than 23 | // 10 -- again, making this dynamic would lead to much more complex code. 24 | #define MAX_CD_RESULT 10 25 | struct cd_search_st { 26 | int cd_id[MAX_CD_RESULT]; 27 | }; 28 | 29 | // database handling 30 | int database_start(char *name, char *password); 31 | void database_end(); 32 | 33 | // adding a cd 34 | int add_cd(char *artist, char *title, char *catalogue, int *cd_id); 35 | int add_tracks(struct current_tracks_st *tracks); 36 | 37 | // finding and retrieving a cd 38 | int find_cds(char *search_str, struct cd_search_st *results); 39 | int get_cd(int cd_id, struct current_cd_st *dest); 40 | int get_cd_tracks(int cd_id, struct current_tracks_st *dest); 41 | 42 | // delete a cd 43 | int delete_cd(int cd_id); 44 | -------------------------------------------------------------------------------- /ch11_sigsprocs/exec_fd_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* As promised, here's a demo of using file descriptors with exec, and the 6 | * fact that exec preserves them. We want a program that will convert a 7 | * file's contents to uppercase and write to stdout. 8 | * 9 | * We wrote a simple program upper.c (compile it with `gcc upper.c -o upper`), 10 | * but it only deals with stdin / stdout. 11 | * 12 | * What to do? Well, here we write a wrapper: in this program we reopen 13 | * file descriptor 0 using `freopen`, using a file input from the command 14 | * line. Then we call exec to turn ourselves into upper. But now when upper 15 | * reads from stdin, it's actually reading from the file opened with fd 0, 16 | * which makes it do what we want! 17 | * 18 | * Obviously this is just a fun demo, but it shows how things work nicely. 19 | */ 20 | 21 | int main(int argc, char *argv[]) 22 | { 23 | char *filename; 24 | 25 | if(argc != 2) { 26 | fprintf(stderr, "usage: useupper file\n"); 27 | exit(1); 28 | } 29 | 30 | // reopen stdin on the file given 31 | filename = argv[1]; 32 | if(!freopen(filename, "r", stdin)) { 33 | fprintf(stderr, "could not redirect stdin to file %s\n", filename); 34 | exit(2); 35 | } 36 | 37 | // replace this program with upper 38 | execl("./upper", "upper", NULL); 39 | 40 | // don't forget to print a message and exit abnormally if exec fails. 41 | perror("could not exec ./upper"); 42 | exit(3); 43 | } 44 | -------------------------------------------------------------------------------- /ch6_curses/pad_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* In curses, a `pad` is a logical window that is allowed to be larger than 7 | * the screen (normal windows may not be). 8 | * 9 | * Here we do a simple demo of a pad. */ 10 | 11 | int main() { 12 | WINDOW *pad; 13 | int x, y; 14 | int pad_lines, pad_cols; 15 | char disp_char; 16 | 17 | initscr(); 18 | pad_lines = LINES + 50; 19 | pad_cols = COLS + 50; 20 | 21 | // pads are initialized using a different func, but they are still WINDOWs 22 | pad = newpad(pad_lines, pad_cols); 23 | 24 | // fill up the pad with alphabetic data 25 | disp_char= 'a'; 26 | for (x = 0; x < pad_lines; x++) { 27 | for (y = 0; y < pad_cols; y++) { 28 | mvwaddch(pad, x, y, disp_char); 29 | disp_char = (disp_char == 'z') ? 'a' : disp_char + 1; 30 | } 31 | } 32 | 33 | // the inputs here are: 34 | // - the (line, col) coord *within* the pad which will be at the top-left 35 | // of what we will actually display 36 | // - the (line, col) coords of the top-left corner inside the actual 37 | // screen where we want to display 38 | // - the (line, col) coords of the bottom-left corner inside the actual 39 | // screen where we want to display 40 | prefresh(pad, 5, 7, 2, 2, 9, 9); 41 | sleep(1); 42 | prefresh(pad, LINES + 5, COLS + 7, 5, 5, 21, 19); 43 | sleep(1); 44 | 45 | delwin(pad); // they are deleted like any other window 46 | endwin(); 47 | exit(EXIT_SUCCESS); 48 | } 49 | -------------------------------------------------------------------------------- /ch4_env/getopt_long.c: -------------------------------------------------------------------------------- 1 | // page 143 ... the inputs are a bit strange, see p 143 144 for discussion 2 | #include 3 | #include 4 | #include 5 | 6 | #define _GNU_SOURCE 7 | #include 8 | 9 | 10 | /* Suggested example call is ./a.out -i --list 'hi there' -f fred.c -q */ 11 | 12 | int main(int argc, char *argv[]) { 13 | int opt; 14 | 15 | struct option longopts[] = { 16 | {"initialize", 0, NULL, 'i'}, 17 | {"file", 1, NULL, 'f'}, 18 | {"list", 0, NULL, 'l'}, 19 | {"restart", 0, NULL, 'r'}, 20 | {0, 0, 0, 0} 21 | }; 22 | 23 | while ((opt = getopt_long(argc, argv, ":if:lr", longopts, NULL)) != -1) { 24 | switch (opt) { 25 | case 'i': 26 | case 'l': 27 | case 'r': 28 | printf("option: %c\n", opt); 29 | break; 30 | case 'f': 31 | printf("filename: %s\n", optarg); 32 | break; 33 | case ':': 34 | printf("option '%c' needs a value\n", optopt); 35 | break; 36 | case '?': 37 | printf("Unknown option: %c\n", optopt); 38 | break; 39 | } 40 | 41 | } 42 | // optind has the index of the last argv parsed by optarg 43 | // ... 44 | // this works even if the arg is before some options, because linux's 45 | // getopt rearranges for you. That's why `rm file -rf` works on 46 | // linux but not mac. 47 | for(; optind < argc; optind++) { 48 | printf("argument: %s\n", argv[optind]); 49 | } 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /ch11_sigsprocs/alarm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* In this program, we use the same signal api demod in basic_signal.c 7 | * in combination with fork() to set an alarm, where the child (which gets 8 | * 0 from fork(), remember) interrupts the parent with a SIGALARM and then 9 | * exits, and the parent catches the alarm in a handler which sets a global 10 | * var. 11 | * 12 | * The kill(pid, signal) function is used to send signals from one process 13 | * to another. 14 | * 15 | * The pause() function causes a program to stop running until it sees the 16 | * next signal. So when the parent hits that, it freezes until the child sends 17 | * the interrupt. 18 | * 19 | * This program, of course, would be better done with threads. But it's a 20 | * nice little demo of the apis. 21 | */ 22 | 23 | static int alarm_fired = 0; 24 | 25 | void ding(int sig) 26 | { 27 | alarm_fired = 1; 28 | } 29 | 30 | int main() { 31 | pid_t pid; 32 | 33 | printf("alarm application starting\n"); 34 | 35 | pid = fork(); 36 | switch(pid) { 37 | case -1: 38 | /* Failure */ 39 | perror("fork failed"); 40 | exit(1); 41 | case 0: 42 | /* child : waits 3 seconds, then sends alarm to parent */ 43 | sleep(5); 44 | kill(getppid(), SIGALRM); 45 | exit(0); 46 | } 47 | 48 | // if we get here, we are in the parent 49 | printf("waiting for alarm to go off\n"); 50 | (void) signal(SIGALRM, ding); 51 | 52 | pause(); 53 | if (alarm_fired) 54 | printf("Ding!\n"); 55 | 56 | printf("done\n"); 57 | exit(0); 58 | } 59 | -------------------------------------------------------------------------------- /ch13_pipes/pipe/a_note_on_closing_pipes: -------------------------------------------------------------------------------- 1 | Most of our pipe examples have ignored what happens when you boot up a 2 | child that loops and continuously reads from a pipe. What happens if there's 3 | data? What if there's no data? What if the write end is closed? 4 | 5 | Here's what happens: 6 | - if there's data, it will behave as we've seen: it reads at most the 7 | number of bytes you asked for, and returns the number of bytes read. If 8 | more stuff is getting written, you'll just read it in your next try. 9 | - if there's no data but the pipe is open, the read call will block until 10 | there's at least a byte of data. 11 | - if the write end of the pipe is closed (see below for notes!), then read 12 | will not block, but will return 0. This is your indication that the pipe 13 | is closed, it's like an EOF 14 | - on an error, the read call returns -1. This also happens if you try to use 15 | an invalid file descriptor (which would happen if you tried to close the 16 | *read* end of the pipe and then read again). 17 | 18 | -> note: if you used a fork to deal with the pipe, there's actually a write 19 | end of the file descriptor open in *both* processes. The read ends won't 20 | start returning 0 on read() until *both* of the write fd's are closed. So, 21 | if in your read client you want to count on this behavior, be sure to 22 | close the write fd as soon as you've forked. That way you know when the 23 | write server closed it's fd and/or exited. 24 | 25 | If you forget this last point, you are likely to wind up deadlocked. Try 26 | to remember this note when you wind up debugging a deadlock on a fork + pipe 27 | program 28 | -------------------------------------------------------------------------------- /ch6_curses/colors.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* Simple demo of curses color capabilities. An important thing to be 7 | * aware of with curses colors is that curses works only with color pairs, 8 | * of a fg and bg color. */ 9 | 10 | int main() { 11 | int i; 12 | initscr(); 13 | 14 | // first check that the terminal supports colors 15 | if (!has_colors()) { 16 | endwin(); 17 | fprintf(stderr, "Error - no color support on this terminal.\n"); 18 | exit(EXIT_FAILURE); 19 | } 20 | 21 | // next, set up the colors (and check error code) 22 | if (start_color() != OK) { 23 | endwin(); 24 | fprintf(stderr, "Error - failed to initialize colors.\n"); 25 | exit(EXIT_FAILURE); 26 | } 27 | 28 | clear(); 29 | mvprintw(5, 5, "There are %d COLORS, and %d COLOR_PAIRS available", 30 | COLORS, COLOR_PAIRS); 31 | refresh(); 32 | 33 | init_pair(1, COLOR_RED, COLOR_BLACK); 34 | init_pair(2, COLOR_RED, COLOR_GREEN); 35 | init_pair(3, COLOR_GREEN, COLOR_RED); 36 | init_pair(4, COLOR_YELLOW, COLOR_BLUE); 37 | init_pair(5, COLOR_BLACK, COLOR_WHITE); 38 | init_pair(6, COLOR_MAGENTA, COLOR_BLUE); 39 | init_pair(7, COLOR_CYAN, COLOR_WHITE); 40 | 41 | for (i = 1; i <= 7; i++) { 42 | attroff(A_BOLD); 43 | attrset(COLOR_PAIR(i)); 44 | mvprintw(5 + i, 5, "Color pair %d", i); 45 | attrset(COLOR_PAIR(i) | A_BOLD); 46 | mvprintw(5 + i, 25, "Bold color pair %d", i); 47 | refresh(); 48 | sleep(1); 49 | } 50 | 51 | endwin(); 52 | exit(EXIT_SUCCESS); 53 | } 54 | -------------------------------------------------------------------------------- /ch13_pipes/fifo/fifo_rw/fifo_reader_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define FIFO_NAME "/tmp/my_fifo" 11 | #define BUFFER_SIZE PIPE_BUF 12 | 13 | 14 | /* This is the fifo reader demo program. 15 | * compare to fifo_writer_demo.c 16 | * 17 | * The writere writes 10 meggabytes of junk to the fifo. 18 | * Here we just read until a read() returns 0 (which happens 19 | * when the writer exits or closes the fifo; in this case 20 | * we close explicitly in the writer function - remember that 21 | * if there's no data but the writer didn't close, we would block 22 | * on read rather than returning 0). Once read returns 0, we are 23 | * finished so we close the pipe and exit. 24 | */ 25 | int main() { 26 | int pipe_fd; 27 | int res; 28 | int open_mode = O_RDONLY; 29 | char buffer[BUFFER_SIZE + 1]; 30 | int bytes_read = 0; 31 | 32 | memset(buffer, '\0', sizeof(buffer)); 33 | 34 | printf("Process %d opening FIFO O_RDONLY\n", getpid()); 35 | pipe_fd = open(FIFO_NAME, open_mode); 36 | printf("Process %d result %d\n", getpid(), pipe_fd); 37 | 38 | if (pipe_fd != -1) { 39 | do { 40 | res = read(pipe_fd, buffer, BUFFER_SIZE); 41 | bytes_read += res; 42 | } while (res > 0); 43 | (void)close(pipe_fd); 44 | } 45 | else { 46 | fprintf(stderr, "Failed to open fifo!\n"); 47 | exit(EXIT_FAILURE); 48 | } 49 | 50 | printf("Process %d finished, %d bytes read\n", getpid(), bytes_read); 51 | exit(EXIT_SUCCESS); 52 | } 53 | 54 | -------------------------------------------------------------------------------- /ch11_sigsprocs/exec_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* simple demo of exec: this process replaces itself a ps aux call. The 6 | * "Done." print never gets printed, and this process doesn't show up in 7 | * the ps aux list. 8 | * 9 | * The difference between this and a system call is that the system call 10 | * goes through a shell, and creates a *new* process, whereas using exec 11 | * you (a) avoid the shell, and (b) reuse this pid. 12 | * 13 | * The execlp is one of several exec functions; the p means it includes 14 | * the search path when looking for the file. The first argument is the 15 | * name of the binary to execute. The second argument is kind of confusing, 16 | * but because you aren't using a shell, you get to *choose* the value of 17 | * arg[0], the name that the new process will see for itself. 18 | * 19 | * The exec functions that don't have a v in their name take vararg inputs; 20 | * the ones that do take a char * const array[] as their second input. Some 21 | * of them also accept a final input which is an array of environment 22 | * variablse. The details are not worth discussing here, see the docs. 23 | * 24 | * Additionally, you can use environ to set environment variables prior to 25 | * an exec call. 26 | * 27 | * A call to exec(), importantly, preserves file descriptors. We'll see a 28 | * fun hack that uses this in a later example. 29 | */ 30 | 31 | int main() 32 | { 33 | printf("Running ps with execlp\n"); 34 | execlp("ps", "ps", "ax", NULL); // the authors use 0, not NULL, but gcc 35 | // gives a 'missing sentinel' warning. grr 36 | printf("Done.\n"); 37 | exit(0); 38 | } 39 | -------------------------------------------------------------------------------- /ch15_sockets/simple_inet_ex/server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* Simple example of a server over an inet scocket. 11 | * 12 | * I discussed all of the meaningful changes already in the client. Note 13 | * that up to changing the unlink(), and modifying the address code, everything 14 | * else is identical to the unix_af variant - the socket semantics are 15 | * the same. 16 | * 17 | * Using a SOCK_STREAM type socket over inet means using TCP for transmission, 18 | * which gives a reliable two-way byte stream. 19 | */ 20 | 21 | int main() { 22 | int server_sockfd, client_sockfd; 23 | int server_len, client_len; 24 | struct sockaddr_in server_address; 25 | struct sockaddr_in client_address; 26 | 27 | server_sockfd = socket(AF_INET, SOCK_STREAM, 0); 28 | 29 | server_address.sin_family = AF_INET; 30 | server_address.sin_addr.s_addr = inet_addr("127.0.0.1"); 31 | server_address.sin_port = htons(9734); 32 | 33 | server_len = sizeof(server_address); 34 | bind(server_sockfd, (struct sockaddr *)&server_address, server_len); 35 | 36 | client_len = sizeof(client_address); 37 | char ch; 38 | 39 | listen(server_sockfd, 5); 40 | while(1) { 41 | printf("server waiting\n"); 42 | 43 | client_sockfd = accept(server_sockfd, 44 | (struct sockaddr *)&client_address, 45 | &client_len); 46 | 47 | read(client_sockfd, &ch, 1); 48 | ch++; 49 | write(client_sockfd, &ch, 1); 50 | 51 | close(client_sockfd); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ch13_pipes/pipe/pipe_plus_fork.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* Here we make a simple demo of combining fork() with pipe() to actually 7 | * communicate between processes. The content of the example is identical 8 | * to our single-process pipe demo 9 | * 10 | * First we make a pipe, then we call fork() and in the parent we write 11 | * to the pipe, in the child we read from it. 12 | * 13 | * It's still not a super-interesting example yet, but when we go one further 14 | * and use exec(), we get minimal-overhead pipes with no shell invocation. 15 | * See later examples... 16 | */ 17 | 18 | int main() { 19 | int data_processed; 20 | int file_pipes[2]; 21 | const char some_data[] = "123"; 22 | char buffer[BUFSIZ + 1]; 23 | pid_t fork_result; 24 | 25 | memset(buffer, '\0', sizeof(buffer)); 26 | 27 | if (pipe(file_pipes) == 0) { 28 | fork_result = fork(); 29 | 30 | // handle errors 31 | if (fork_result == -1) { 32 | fprintf(stderr, "Fork failure"); 33 | exit(EXIT_FAILURE); 34 | } 35 | 36 | // in the child: read from the 0th (read side of the pipe) descriptor 37 | if (fork_result == 0) { 38 | data_processed = read(file_pipes[0], buffer, BUFSIZ); 39 | printf("Read %d bytes: %s\n", data_processed, buffer); 40 | exit(EXIT_SUCCESS); 41 | } 42 | 43 | // in the parent: write to the 1st (write side of the pipe) descriptor 44 | else { 45 | data_processed = write(file_pipes[1], some_data, 46 | strlen(some_data)); 47 | printf("Wrote %d bytes\n", data_processed); 48 | } 49 | } 50 | exit(EXIT_SUCCESS); 51 | } 52 | 53 | -------------------------------------------------------------------------------- /ch6_curses/keypad.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* Simple demo of a keypad program. 6 | */ 7 | #define LOCAL_ESCAPE_KEY 27 // 27 is the normal escape key 8 | 9 | 10 | int main() { 11 | int key; 12 | initscr(); 13 | crmode(); // this is a synonym for nocbreak(); - normal line buffering 14 | // mode. But I think it was actually a typo for the authors 15 | // to have this... what you really want is cbreak() so that 16 | // individual characters may be read. 17 | cbreak(); 18 | keypad(stdscr, TRUE); // the true enables curses to automatically translate 19 | // function keys, such as arrow keys, into logical 20 | // values such as KEY_LEFT. 21 | noecho(); 22 | clear(); 23 | mvprintw(5, 5, "Key pad demonstration. Press 'q' to quit."); 24 | move(7, 5); 25 | refresh(); 26 | key = getch(); 27 | 28 | while (key != ERR && key != 'q') { 29 | move(7, 5); 30 | clrtoeol(); // carriage return to eol 31 | if ((key >= 'A' && key <= 'Z') || 32 | (key >= 'a' && key <= 'z')) { 33 | printw("Key was '%c'", (char) key); 34 | } else { 35 | switch (key) { 36 | case LOCAL_ESCAPE_KEY: printw("%s", "Escape key"); break; 37 | case KEY_END: printw("%s", "End key key"); break; 38 | case KEY_BEG: printw("%s", "Beginning key"); break; 39 | case KEY_RIGHT: printw("%s", "Right key"); break; 40 | case KEY_LEFT: printw("%s", "Left key"); break; 41 | case KEY_UP: printw("%s", "Up key"); break; 42 | case KEY_DOWN: printw("%s", "Down key"); break; 43 | } 44 | } 45 | refresh(); 46 | key = getch(); 47 | 48 | } 49 | 50 | endwin(); 51 | exit(EXIT_SUCCESS); 52 | } 53 | -------------------------------------------------------------------------------- /ch13_pipes/pipe/pipe_exec/pipe_exec_parent.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* this is the parent of an example that uses fork and exec to communicate 7 | * over a pipe using two different programs 8 | * 9 | * The parent program forks, then in the parent we write to the pipe and 10 | * exit. The child calls execl to change the program to the pipe_exec_child, 11 | * which will in turn read the data. 12 | */ 13 | 14 | int main() { 15 | int data_processed; 16 | int file_pipes[2]; 17 | const char some_data[] = "123"; 18 | char buffer[BUFSIZ + 1]; 19 | pid_t fork_result; 20 | 21 | memset(buffer, '\0', sizeof(buffer)); 22 | 23 | if (pipe(file_pipes) == 0) { 24 | fork_result = fork(); 25 | 26 | // handle failure 27 | if (fork_result == (pid_t)-1) { 28 | fprintf(stderr, "Fork failure"); 29 | exit(EXIT_FAILURE); 30 | } 31 | 32 | // in child, print the file descriptor number to a string because 33 | // when we call exec the file descriptors are preserved, but we're 34 | // going to need to know which one to use. To find out, we pass it as 35 | // a command line option. 36 | // 37 | // Then call execl. If execl fails, exit as a failure. 38 | if (fork_result == 0) { 39 | sprintf(buffer, "%d", file_pipes[0]); 40 | execl("./pipe_exec_child", "pipe_exec_child", 41 | buffer, (char *)0); 42 | exit(EXIT_FAILURE); 43 | } 44 | 45 | // in the parent, write some stuff to the pipe and print status. Then 46 | // exit. 47 | else { 48 | data_processed = write(file_pipes[1], some_data, 49 | strlen(some_data)); 50 | printf("%d - wrote %d bytes\n", getpid(), data_processed); 51 | } 52 | } 53 | exit(EXIT_SUCCESS); 54 | } 55 | 56 | -------------------------------------------------------------------------------- /ch13_pipes/fifo/fifo_serverclient/client.c: -------------------------------------------------------------------------------- 1 | #include "protocol.h" 2 | #include 3 | 4 | /* the client side of our client-server program. See inside the 5 | * program for notes on how it works. */ 6 | 7 | int main() { 8 | int server_fifo_fd, client_fifo_fd; 9 | struct data_to_pass_st my_data; 10 | int times_to_send; 11 | char client_fifo[256]; 12 | 13 | // open the server fifo (we write to this). 14 | // here we are assuming the server already started 15 | server_fifo_fd = open(SERVER_FIFO_NAME, O_WRONLY); 16 | if (server_fifo_fd == -1) { 17 | fprintf(stderr, "Sorry, no server\n"); 18 | exit(EXIT_FAILURE); 19 | } 20 | 21 | // create the client fifo. The path is determined by our pid. 22 | my_data.client_pid = getpid(); 23 | sprintf(client_fifo, CLIENT_FIFO_NAME, my_data.client_pid); 24 | if (mkfifo(client_fifo, 0777) == -1) { 25 | fprintf(stderr, "Sorry, can't make %s\n", client_fifo); 26 | exit(EXIT_FAILURE); 27 | } 28 | 29 | // send a hello message to the server 5 times. 30 | // each time, open up the client fifo, read the server reply, 31 | // and close the client fifo 32 | for (times_to_send = 0; times_to_send < 5; times_to_send++) { 33 | sprintf(my_data.some_data, "Hello from %d", my_data.client_pid); 34 | printf("%d sent %s, ", my_data.client_pid, my_data.some_data); 35 | write(server_fifo_fd, &my_data, sizeof(my_data)); 36 | client_fifo_fd = open(client_fifo, O_RDONLY); 37 | if (client_fifo_fd != -1) { 38 | if (read(client_fifo_fd, &my_data, sizeof(my_data)) > 0) { 39 | printf("received: %s\n", my_data.some_data); 40 | } 41 | close(client_fifo_fd); 42 | } 43 | } 44 | 45 | // close the server fifo, and unlink the client fifo b/c we're done 46 | close(server_fifo_fd); 47 | unlink(client_fifo); 48 | exit(EXIT_SUCCESS); 49 | } 50 | 51 | -------------------------------------------------------------------------------- /ch8_mysql/cd_prog_ch8/app_testdriver.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "app_mysql.h" 6 | 7 | int main() { 8 | struct current_cd_st cd; 9 | struct cd_search_st cd_res; 10 | struct current_tracks_st ct; 11 | int cd_id; 12 | int res, i; 13 | 14 | database_start("trox", "trox"); 15 | 16 | // add a new cd. We get the cd_id back from the data code. 17 | res = add_cd("Mahler", "Symphony No 1", "4596102", &cd_id); 18 | printf("Result of adding a cd was %d, cd_id is %d\n", res, cd_id); 19 | 20 | // add tracks for the new cd. Note how we need to zero out bytes first. 21 | memset(&ct, 0, sizeof(ct)); 22 | ct.cd_id = cd_id; 23 | strcpy(ct.track[0], "Lansam Schleppend"); 24 | strcpy(ct.track[1], "Kraftig bewegt"); 25 | strcpy(ct.track[2], "Feierlich und gemesen"); 26 | strcpy(ct.track[3], "Sturmisch bewegt"); 27 | add_tracks(&ct); 28 | 29 | // search for the cd we just added. There's a struct to hold search data, 30 | // which could include many cds. It returns the ids. 31 | res = find_cds("Symphony", &cd_res); 32 | printf("Found %d cds, first has ID %d\n", res, cd_res.cd_id[0]); 33 | 34 | // to get full data on any of the results, look it up by id 35 | res = get_cd(cd_res.cd_id[0], &cd); 36 | printf("get_cd returned %d\n", res); 37 | 38 | // ... you can also look up the tracks once you have the id 39 | // note that we need to zero out the bytes again 40 | memset(&ct, 0, sizeof(ct)); 41 | res = get_cd_tracks(cd_res.cd_id[0], &ct); // returns num tracks in `res` 42 | printf("get_cd_tracks returned %d\n", res); 43 | for (i = 0; i < res; i++) { 44 | printf("\ttrack %d is %s\n", i, ct.track[i]); 45 | } 46 | 47 | // delete the cd we just added 48 | res = delete_cd(cd_res.cd_id[0]); 49 | printf("Delete_cd returned %d\n", res); 50 | 51 | // cleanup 52 | database_end(); 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /ch13_pipes/fifo/fifo_rw/fifo_writer_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define FIFO_NAME "/tmp/my_fifo" 11 | #define BUFFER_SIZE PIPE_BUF 12 | #define TEN_MEG (1024 * 1024 * 10) 13 | 14 | /* This is the fifo writer demo program. 15 | * compare to fifo_reader_demo.c 16 | * 17 | * It writes 10 megabytes of data to the pipe, in chuncks 18 | * of size BUFFER_SIZE == PIPE_BUF, and prints a message 19 | * when done if it finishes normally. 20 | */ 21 | 22 | int main() { 23 | int pipe_fd; 24 | int res; 25 | int open_mode = O_WRONLY; 26 | int bytes_sent = 0; 27 | char buffer[BUFFER_SIZE + 1]; 28 | 29 | // make the fifo if needed. 30 | if (access(FIFO_NAME, F_OK) == -1) { 31 | res = mkfifo(FIFO_NAME, 0777); 32 | if (res != 0) { 33 | fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME); 34 | exit(EXIT_FAILURE); 35 | } 36 | } 37 | 38 | // open the fifo 39 | printf("Process %d opening FIFO O_WRONLY\n", getpid()); 40 | pipe_fd = open(FIFO_NAME, open_mode); 41 | printf("Process %d result %d\n", getpid(), pipe_fd); 42 | 43 | // assuming no error, loop and write 10 mb to fifo, in chuncks 44 | // of BUFFER_SIZE. 45 | if (pipe_fd != -1) { 46 | while(bytes_sent < TEN_MEG) { 47 | res = write(pipe_fd, buffer, BUFFER_SIZE); 48 | if (res == -1) { 49 | fprintf(stderr, "Write error on pipe\n"); 50 | exit(EXIT_FAILURE); 51 | } 52 | bytes_sent += res; 53 | } 54 | (void)close(pipe_fd); 55 | } else { 56 | fprintf(stderr, "Failed to open fifo!\n"); 57 | exit(EXIT_FAILURE); 58 | } 59 | 60 | // all done, print a msg and quit. This closes the write 61 | // side of the fifo. 62 | printf("Process %d finished\n", getpid()); 63 | exit(EXIT_SUCCESS); 64 | } 65 | -------------------------------------------------------------------------------- /ch6_curses/curses_pwdemo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /* A simple password / login demo using curses, to show the how to read 8 | * keyboard input */ 9 | 10 | #define PW_LEN 256 11 | #define NAME_LEN 256 12 | 13 | int main() { 14 | // the space in front serves a purpose; it isn't ever printed, but allows 15 | // a clean while loop. See the code at line 47. 16 | char name[NAME_LEN]; 17 | char password[PW_LEN]; 18 | const char *real_password = "xyzzy"; 19 | 20 | int i = 0; 21 | initscr(); 22 | 23 | move(5, 10); 24 | printw("%s", "Please login:"); 25 | move(7, 10); 26 | printw("%s", "User name:"); 27 | getnstr(name, NAME_LEN); // get a string with length-checking 28 | // normally no referesh is needed above because get functions automatically 29 | // refresh. In some old versions of curses this is not the case. 30 | // 31 | // note that printw leaves the curser at the end of the printed string 32 | 33 | move(8, 10); 34 | printw("%s", "Password:"); 35 | refresh(); 36 | /* prevent the password from being echoed to the screen */ 37 | cbreak(); // by default curses processes input line-by-line, like the 38 | // terminal. Use this to enable character-by-character. You can 39 | // unset it by calling nocbreak() 40 | noecho(); // similarly, stop input from being echoed; can unset with echo() 41 | memset(password, '\0', sizeof(password)); // set all password contents to \0 42 | while (i < PW_LEN) { 43 | password[i] = getch(); // getch gets 1 char at a time 44 | if (password[i] == '\n') break; 45 | move(8, 20+i); 46 | addch('*'); 47 | refresh(); 48 | i++; 49 | } 50 | 51 | echo(); 52 | nocbreak(); 53 | 54 | move(11, 10); 55 | if (strncmp(real_password, password, strlen(real_password)) == 0) { 56 | printw("Correct"); 57 | } else { 58 | printw("Incorrect"); 59 | } 60 | printw(" password."); 61 | 62 | refresh(); 63 | sleep(2); 64 | 65 | endwin(); 66 | exit(EXIT_SUCCESS); 67 | } 68 | -------------------------------------------------------------------------------- /ch15_sockets/simple_inet_ex/client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* Simple example of a client program, communicating via inet. 11 | * 12 | * Most of this example is lifted straight from the unix variant. Only the 13 | * handling of the address differs. Note that we use a completely different 14 | * struct for the address, but we do still cast to the generic 15 | * sockaddr type when calling connect(). 16 | * 17 | * Also note that the header files changed. 18 | * 19 | * Note one other thing: we have to call the htons function 20 | * (read host to network short) port to convert the port from the host's 21 | * byte ordering (little- or big-endian) to whichever ordering the network 22 | * is using. The operating system handles this for us, be we have to call 23 | * it's function. 24 | * 25 | * The inet_addr() function is a handy utility that converts from human 26 | * readable sting ips to integers. 27 | */ 28 | 29 | int main() { 30 | int sockfd; 31 | int len; 32 | struct sockaddr_in address; 33 | int result; 34 | char ch = 'A'; 35 | 36 | // create the file descriptor. We need to specify the domain and the 37 | // type (STREAM or DGRAM), but not the address. 38 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 39 | 40 | // create the address and connect 41 | address.sin_family = AF_INET; 42 | address.sin_addr.s_addr = inet_addr("127.0.0.1"); 43 | address.sin_port = htons(9734); 44 | len = sizeof(address); 45 | // note that the cast hasn't changed.. in fact this line is identical 46 | // to the unix_af variant, as is everything below. 47 | result = connect(sockfd, (struct sockaddr *)&address, len); 48 | if(result == -1) { 49 | perror("oops: client1"); 50 | exit(1); 51 | } 52 | 53 | write(sockfd, &ch, 1); 54 | read(sockfd, &ch, 1); 55 | printf("char from server = %c\n", ch); 56 | close(sockfd); 57 | exit(0); 58 | } 59 | 60 | -------------------------------------------------------------------------------- /ch7_data/mem_ex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | int *some_memory, *more_memory; 6 | size_t SIZE1 = 50, SIZE2 = 100; 7 | int i; 8 | 9 | // malloc assigns memory, does not initialize 10 | // that said, often you'll get all zeros anyway, if the ram you are 11 | // given hasn't been used before 12 | some_memory = (int *) malloc(SIZE1 * sizeof(int)); 13 | if (!some_memory) { 14 | fprintf(stderr, "Memory error."); 15 | free(some_memory); 16 | exit(1); 17 | } 18 | printf("After using malloc, some_memory isi %p, with contents:", 19 | some_memory); 20 | for (i = 0; i < SIZE1; i++) { 21 | printf(" %d", some_memory[i]); 22 | } 23 | printf("\n"); 24 | 25 | // calloc has a slightly different interface, and zero-initializes. 26 | some_memory = (int *) calloc(sizeof(int), SIZE1); 27 | if (!some_memory) { 28 | fprintf(stderr, "Memory error."); 29 | free(some_memory); 30 | exit(1); 31 | } 32 | printf("After using calloc, some_memory isi %p, with contents:", 33 | some_memory); 34 | for (i = 0; i < SIZE1; i++) { 35 | printf(" %d", some_memory[i]); 36 | } 37 | printf("\n"); 38 | 39 | 40 | // realloc tries to give you more space. If it can't, it gives a new 41 | // address and mem copies your data. 42 | // Note that we use a different pointer for the memory - it doesn't 43 | // matter here, but in general realloc can return NULL on memory issues, 44 | // and so you want to keep your old pointer around just in case - 45 | // especially if you have a long-running, robust program. 46 | more_memory = (int *) realloc(some_memory, sizeof(int) * SIZE2); 47 | if (!more_memory) { 48 | fprintf(stderr, "Memory error."); 49 | free(more_memory); 50 | exit(1); 51 | } 52 | printf("After using realloc, more_memory isi %p, with contents:", 53 | more_memory); 54 | for (i = 0; i < SIZE2; i++) { 55 | printf(" %d", more_memory[i]); 56 | } 57 | printf("\n"); 58 | exit(0); 59 | } 60 | 61 | -------------------------------------------------------------------------------- /ch15_sockets/forking_inet_ex/server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* Example of a server over an inet socket which forks itself to handle 11 | * each client connection. 12 | * 13 | * A more efficient approach would be to spawn a thread that deals with each 14 | * connection, which is what most web frameworks do. Forking is simpler if 15 | * the service is stateless and overhead isn't a concern, so that's what we 16 | * do in this example. 17 | * 18 | * In a later example, we will see how using the `select` utility to listen 19 | * on many file descriptors, it's also possible for a single-threaded 20 | * server to handle simultaneous connections. 21 | */ 22 | 23 | int main() { 24 | int server_sockfd, client_sockfd; 25 | int server_len, client_len; 26 | struct sockaddr_in server_address; 27 | struct sockaddr_in client_address; 28 | 29 | server_sockfd = socket(AF_INET, SOCK_STREAM, 0); 30 | 31 | server_address.sin_family = AF_INET; 32 | server_address.sin_addr.s_addr = inet_addr("127.0.0.1"); 33 | server_address.sin_port = htons(9734); 34 | 35 | server_len = sizeof(server_address); 36 | bind(server_sockfd, (struct sockaddr *)&server_address, server_len); 37 | 38 | client_len = sizeof(client_address); 39 | char ch; 40 | 41 | listen(server_sockfd, 5); 42 | while(1) { 43 | printf("server waiting\n"); 44 | 45 | client_sockfd = accept(server_sockfd, 46 | (struct sockaddr *)&client_address, 47 | &client_len); 48 | 49 | // after accepting, fork and deal with the new connection in the 50 | // child. Don't forget that both the child *and* the main process 51 | // need to close the new client_sockfd! 52 | if (fork() == 0) { 53 | read(client_sockfd, &ch, 1); 54 | ch++; 55 | write(client_sockfd, &ch, 1); 56 | close(client_sockfd); 57 | } else { 58 | close(client_sockfd); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ch13_pipes/popen/popen_bufread.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* This is a slight extension of popen_read, where we do two things: 7 | * 1. we demonstrate that popen starts a shell by doing ps aux, which 8 | * will show a child *and* a grandchild of this process, because popen 9 | * makes a shell and then it is fork / exec'ed to create the ps. This 10 | * means doing a ton of popens can cause overhead. 11 | * 2. We show the api for reading a buffer a bit at a time. The previous 12 | * examples only read one buffer worth of input because the programs 13 | * were small, but here we make no such assumption, and we keep 14 | * freading till we are done. 15 | * 16 | * ... nothing is really new here, since the fread api we are using is 17 | * the same as it would be for files (there are differences, I think, 18 | * e.g. I'm pretty sure you can't do arbitrary seeks, but I'm guessing 19 | * the operating system handles those errors; the api allows it) 20 | */ 21 | 22 | int main() 23 | { 24 | FILE *read_fp; 25 | char buffer[BUFSIZ + 1]; 26 | int chars_read; 27 | 28 | memset(buffer, '\0', sizeof(buffer)); 29 | read_fp = popen("ps aux", "r"); 30 | if (read_fp != NULL) { 31 | chars_read = fread(buffer, sizeof(char), BUFSIZ/10, read_fp); 32 | while (chars_read > 0) { 33 | // The authors used `chars_read - 1` here, but that's an error 34 | // and overwrites the last byte read in. How do I know? I tried 35 | // changing the command to "echo -n 'some_stuff'", and saw that 36 | // the last f was removed. 37 | // 38 | // I'm guessing the authors tested using echo without the -n, and 39 | // the newline deleted canceled out with the newline in their 40 | // printf call, which is how they messed it up. 41 | buffer[chars_read] = '\0'; 42 | printf("Reading %d:-\n %s\n", BUFSIZ/10, buffer); 43 | chars_read = fread(buffer, sizeof(char), BUFSIZ/10, read_fp); 44 | } 45 | pclose(read_fp); 46 | exit(EXIT_SUCCESS); 47 | } 48 | exit(EXIT_FAILURE); 49 | } 50 | -------------------------------------------------------------------------------- /ch4_env/tempfiles.c: -------------------------------------------------------------------------------- 1 | /* page 150-155. The discussion of strftime / strptime on page 2 | * 154-156 might prove especially helpful */ 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | int main() { 9 | // L_tmpnam is defined by your system libraries 10 | char tmpname[L_tmpnam]; 11 | char *filename; 12 | FILE *tmpfp; 13 | 14 | // the beginning linux programming book demos using tmpnam and tmpfile. 15 | // However, the utility of tmpfile is limited, because it only gives you 16 | // a FILE*, so you can't recover the path. 17 | // And tmpnam is considered unsafe; in OSX the compiler warns that it is 18 | // deprecated, and in linux it explicitly says it is unsafe. 19 | // 20 | // on more modern unix flavors, you can use 21 | // char * mktemp(char * template) 22 | // to get a valid name for a temp file, where the `template` arg is a 23 | // string with six trailing X characters, which will be replaced. 24 | // 25 | // You can get a low-level file descriptor instead: 26 | // int * mkstemp(char * template), 27 | // but in general this can be an issue because you can't recover the path. 28 | // 29 | // The preferred approach seems to be mkdtemp, which can instantly make 30 | // a directory and also return the string. 31 | // char * mkdtemp(char * template) 32 | // 33 | // This function *can't* be given a read-only c-string, which is why I 34 | // do all this copy nonsense: it modifies the template in-place, so if 35 | // you pass a read-only array, you get an error 36 | // on osx it's a bus error 37 | // on linux it's a seg fault 38 | // 39 | // Also, it doesn't automatically put the temp file in "/tmp"; if you 40 | // want that, you have to do it explicitly in your template. 41 | char *template_rom = "/tmp/my-temp-dir-XXXXXX"; 42 | char *template_heap = (char *) malloc(100); 43 | if (!template_heap) { 44 | printf("Memory error"); 45 | exit(1); 46 | } 47 | 48 | strcpy(template_heap, template_rom); 49 | 50 | filename = mkdtemp(template_heap); 51 | printf("Generated a temp dir name with `mkdtemp`: \"%s\"\n", filename); 52 | 53 | exit(0); 54 | } 55 | -------------------------------------------------------------------------------- /ch13_pipes/cd_app_fifo/cd_data.h: -------------------------------------------------------------------------------- 1 | /* the cds table 2 | * 3 | * this is pretty much unchanged since ch7. 4 | * the authors choose to have separate 5 | * #define's in the cat and track tables. I 6 | * find that annoying, and changed it when I 7 | * implemented ch 7, but i'm not going to change 8 | * it here. 9 | */ 10 | #define CAT_CAT_LEN 30 11 | #define CAT_TITLE_LEN 70 12 | #define CAT_TYPE_LEN 30 13 | #define CAT_ARTIST_LEN 70 14 | 15 | typedef struct { 16 | char catalog[CAT_CAT_LEN + 1]; 17 | char title[CAT_TITLE_LEN + 1]; 18 | char type[CAT_TYPE_LEN + 1]; 19 | char artist[CAT_ARTIST_LEN + 1]; 20 | } cdc_entry; 21 | 22 | /* The tracks table 23 | * 24 | * again, pretty much the same as ch 7 */ 25 | 26 | #define TRACK_CAT_LEN CAT_CAT_LEN 27 | #define TRACK_TTEXT_LEN 70 28 | 29 | typedef struct { 30 | char catalog[TRACK_CAT_LEN + 1]; 31 | int track_no; 32 | char track_txt[TRACK_TTEXT_LEN + 1]; 33 | } cdt_entry; 34 | 35 | /* Now that we have some data structures, we can define some access routines 36 | * that we'll need. Functions with cdc_ are for catalog entries; functions 37 | * with cdt_ are for track entries. Notice that some of the functions return 38 | * data structures. We can indicate the failure of these functions by forcing 39 | * the contents of the structure to be empty. 40 | * 41 | * These functions are mostly the same as in chapter 7. 42 | * For fun, we could have used the SQL versions from chapter 8 - the 43 | * interface could be unchanged! 44 | */ 45 | 46 | /* Initialization and termination functions */ 47 | int database_initialize(const int new_database); 48 | void database_close(void); 49 | 50 | /* two for simple data retrieval */ 51 | cdc_entry get_cdc_entry(const char *cd_catalog_ptr); 52 | cdt_entry get_cdt_entry(const char *cd_catalog_ptr, const int track_no); 53 | 54 | /* two for data addition */ 55 | int add_cdc_entry(const cdc_entry entry_to_add); 56 | int add_cdt_entry(const cdt_entry entry_to_add); 57 | 58 | /* two for data deletion */ 59 | int del_cdc_entry(const char *cd_catalog_ptr); 60 | int del_cdt_entry(const char *cd_catalog_ptr, const int track_no); 61 | 62 | /* one search function */ 63 | cdc_entry search_cdc_entry(const char *cd_catalog_ptr, int *first_call_ptr); 64 | 65 | -------------------------------------------------------------------------------- /ch14_sem_shmem_mqs/cd_app_mq/cd_data.h: -------------------------------------------------------------------------------- 1 | /* the cds table 2 | * 3 | * this is pretty much unchanged since ch7. 4 | * the authors choose to have separate 5 | * #define's in the cat and track tables. I 6 | * find that annoying, and changed it when I 7 | * implemented ch 7, but i'm not going to change 8 | * it here. 9 | */ 10 | #define CAT_CAT_LEN 30 11 | #define CAT_TITLE_LEN 70 12 | #define CAT_TYPE_LEN 30 13 | #define CAT_ARTIST_LEN 70 14 | 15 | typedef struct { 16 | char catalog[CAT_CAT_LEN + 1]; 17 | char title[CAT_TITLE_LEN + 1]; 18 | char type[CAT_TYPE_LEN + 1]; 19 | char artist[CAT_ARTIST_LEN + 1]; 20 | } cdc_entry; 21 | 22 | /* The tracks table 23 | * 24 | * again, pretty much the same as ch 7 */ 25 | 26 | #define TRACK_CAT_LEN CAT_CAT_LEN 27 | #define TRACK_TTEXT_LEN 70 28 | 29 | typedef struct { 30 | char catalog[TRACK_CAT_LEN + 1]; 31 | int track_no; 32 | char track_txt[TRACK_TTEXT_LEN + 1]; 33 | } cdt_entry; 34 | 35 | /* Now that we have some data structures, we can define some access routines 36 | * that we'll need. Functions with cdc_ are for catalog entries; functions 37 | * with cdt_ are for track entries. Notice that some of the functions return 38 | * data structures. We can indicate the failure of these functions by forcing 39 | * the contents of the structure to be empty. 40 | * 41 | * These functions are mostly the same as in chapter 7. 42 | * For fun, we could have used the SQL versions from chapter 8 - the 43 | * interface could be unchanged! 44 | */ 45 | 46 | /* Initialization and termination functions */ 47 | int database_initialize(const int new_database); 48 | void database_close(void); 49 | 50 | /* two for simple data retrieval */ 51 | cdc_entry get_cdc_entry(const char *cd_catalog_ptr); 52 | cdt_entry get_cdt_entry(const char *cd_catalog_ptr, const int track_no); 53 | 54 | /* two for data addition */ 55 | int add_cdc_entry(const cdc_entry entry_to_add); 56 | int add_cdt_entry(const cdt_entry entry_to_add); 57 | 58 | /* two for data deletion */ 59 | int del_cdc_entry(const char *cd_catalog_ptr); 60 | int del_cdt_entry(const char *cd_catalog_ptr, const int track_no); 61 | 62 | /* one search function */ 63 | cdc_entry search_cdc_entry(const char *cd_catalog_ptr, int *first_call_ptr); 64 | 65 | -------------------------------------------------------------------------------- /ch3_files/dir_scan_4.c: -------------------------------------------------------------------------------- 1 | #include // Include this first 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | void printdir(char *dirname, int depth) { 10 | DIR *dir; 11 | struct dirent *entry; 12 | struct stat statbuf; 13 | 14 | if ((dir = opendir(dirname)) == NULL) { 15 | fprintf(stderr, "Cannot open directory: %s\n", dirname); 16 | return; 17 | } 18 | 19 | chdir(dirname); // this is how you change working directories in C 20 | // I'm not sure whether it's needed... I'm guessing that 21 | // without this the way we are calling lstat, and maybe 22 | // also the comparisons with '.' and '..', would fail. 23 | 24 | while((entry = readdir(dir)) != NULL) { // readdir iterates through 25 | lstat(entry->d_name, &statbuf); 26 | if (S_ISDIR(statbuf.st_mode)) { 27 | /* found a directory. But we should skip '.' and '..' */ 28 | if (strcmp(".", entry->d_name) == 0 || 29 | strcmp("..", entry->d_name) == 0) { continue; } 30 | 31 | /* print at this indent level, then recurse at new indent level */ 32 | printf("%*s%s\n", depth, "", entry->d_name); 33 | printdir(entry->d_name, depth+4); 34 | } 35 | else { 36 | printf("%*s%s\n", depth, "", entry->d_name); 37 | } 38 | } 39 | 40 | chdir(".."); // undo the prev chdir... it doesn't work at the top level, 41 | // but we don't care there anyway (although it might cause 42 | // an error if we try to scan '/') 43 | 44 | closedir(dir); // don't forget to clean up! 45 | // the prev file copy example skipped this b/c everything 46 | // was in main, but here we are redursing, so we should make 47 | // sure the number of open dirs is bounded by the depth of 48 | // the scan. 49 | } 50 | 51 | int main(int argc, char *argv[]) { 52 | 53 | if (argc != 2) { 54 | printf("Usage: %s \n", argv[0]); 55 | exit(1); 56 | } 57 | 58 | char *dirname = argv[1]; 59 | printdir(dirname, 0); 60 | printf("[done]\n"); 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /ch4_env/syslog.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | /* Syslog demo. 8 | * This program outputs nothing. On the ubuntu machine I tested with, 9 | * you can see its output by running 10 | * tail /var/log/syslog 11 | * On a lot of systems, you would instead do 12 | * tail /var/log/messages 13 | * 14 | * This is also the first demo of getpid(), which is self-explanatory. There's 15 | * also a getppid which gets the parent's pid. 16 | */ 17 | 18 | int main() { 19 | int logmask; 20 | // SET UP LOGGING PARAMS 21 | // you can do syslog without this, but it modifies stuff: your logs will 22 | // say they came from the "logmask" program, and will be logged with a 23 | // pid. LOG_CONS says to log to console if there is an error accessing 24 | // the sys log. 25 | // The LOG_USER says to do generic user messages. See 26 | // http://www.gsp.com/cgi-bin/man.cgi?section=3&topic=openlog 27 | openlog("logmask", LOG_PID|LOG_CONS, LOG_USER); 28 | 29 | // you can also get the "user-style" logging if you put 30 | // LOG_LEVEL|LOG_USER directly in the first arg to syslog. But it's 31 | // nicer to call openlog first, so that you can customize the prefixes 32 | // and it's easier to disentagle your program from others. 33 | 34 | // SEND SOME SIMPLE LOG MESSAGES 35 | // note that syslog takes printf-style formatting and varargs. 36 | syslog(LOG_INFO, "the pid should also be prefixed, it is %d", getpid()); 37 | syslog(LOG_DEBUG, "This debug message will show up b/c default level"); 38 | 39 | // RESET LOGGING LEVEL 40 | // (DEBUG is lower than NOTICE) Amazingly, the docs don't say what is 41 | // returned from this function; at any rate one calls it for side-effects. 42 | // the LOG_UPTO macro is named that way because the system uses small 43 | // numbers for high priority (much like nice). 44 | logmask = setlogmask(LOG_UPTO(LOG_NOTICE)); 45 | syslog(LOG_DEBUG, "This message won't appear"); 46 | 47 | // You can use the %m code to print the message encoded in errno 48 | FILE* f = fopen("file_does_not_exist.txt", "r"); 49 | if (!f) { 50 | syslog(LOG_ERR, "This message should appear, an error: %m"); 51 | } 52 | 53 | return 0; 54 | } 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /ch15_sockets/daytime_examples/tcp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* This program demonstrates writing a client for a built-in networking 9 | * service, the "daytime" service that tells us what time a computer thinks 10 | * it is. 11 | * 12 | * In the process, we demonstrate using `struct hostent` and the 13 | * gethostbyname function to look up information from /etc/hosts, and 14 | * similarly the `struct servent` and the getservbyname to look up 15 | * services from /etc/services. 16 | * 17 | * In this demo, we use tcp as we do for nearly all of our other examples in 18 | * this chapter. The other example uses udp, which makes use of SOCK_DGRAM 19 | * instead of SOCK_STREAM. 20 | */ 21 | 22 | int main(int argc, char *argv[]) { 23 | char *host; 24 | int sockfd; 25 | int len, result; 26 | struct sockaddr_in address; 27 | struct hostent *hostinfo; 28 | struct servent *servinfo; 29 | char buffer[128]; 30 | 31 | // the hosst is either localhost or anything specified in arg 1 32 | host = (argc == 1) ? "localhost" : argv[1]; 33 | 34 | // look up the host info 35 | hostinfo = gethostbyname(host); 36 | if(!hostinfo) { 37 | fprintf(stderr, "no host: %s\n", host); 38 | exit(1); 39 | } 40 | 41 | // look up the service info. Note that we specify the tcp version! 42 | servinfo = getservbyname("daytime", "tcp"); 43 | if(!servinfo) { 44 | fprintf(stderr,"no daytime service\n"); 45 | exit(1); 46 | } 47 | printf("daytime port is %d\n", ntohs(servinfo -> s_port)); 48 | 49 | // construct the address 50 | address.sin_family = AF_INET; 51 | address.sin_port = servinfo -> s_port; 52 | address.sin_addr = *(struct in_addr *)*hostinfo -> h_addr_list; 53 | len = sizeof(address); 54 | 55 | // create the socket and then connect 56 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 57 | result = connect(sockfd, (struct sockaddr *)&address, len); 58 | if(result == -1) { 59 | perror("oops: getdate"); 60 | exit(1); 61 | } 62 | 63 | // get the info! 64 | result = read(sockfd, buffer, sizeof(buffer)); 65 | buffer[result] = '\0'; 66 | printf("read %d bytes: %s", result, buffer); 67 | 68 | close(sockfd); 69 | exit(0); 70 | } 71 | -------------------------------------------------------------------------------- /ch5_term/dev_tty.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* This is like the basic menu, except instead of crashing when the 6 | * terminal is non-interactive, it directly uses /dev/tty, which 7 | * allows us to display information and get commands interactively while 8 | * still directing stdout to a file. 9 | * 10 | * Suggested use is ./a.out > junk.txt 11 | * (and then, say, a d q) 12 | */ 13 | 14 | char *menu[] = { 15 | "a - add new record", 16 | "d - delete record", 17 | "q - quit", 18 | NULL, 19 | }; 20 | 21 | int getchoice(char * greet, char * choices[], FILE * input, FILE * output); 22 | 23 | int main() { 24 | 25 | FILE * input, * output; 26 | 27 | if (!isatty(fileno(stdout))) { 28 | fprintf(stderr, "You are not at a terminal. That's okay."); 29 | } 30 | 31 | input = fopen("/dev/tty", "r"); 32 | output = fopen("/dev/tty", "w"); 33 | if (!input || !output) { 34 | fprintf(stderr, "Unable to open /dev/tty\n"); 35 | exit(1); 36 | } 37 | 38 | int choice = 0; 39 | do { 40 | choice = getchoice("Please select an action", menu, input, output); 41 | printf("You have chosen: %c\n", choice); 42 | } while (choice != 'q'); 43 | 44 | return 0; 45 | } 46 | 47 | /* Display a menu with a heading line that says "Choice: ", and 48 | * wait for the user to enter a valid choice. 49 | * 50 | * input and output are FILE* here. 51 | */ 52 | int getchoice(char * greet, char * choices[], FILE * input, FILE * output) { 53 | int selected; 54 | int chosen = 0; // bool 55 | char **option; 56 | 57 | do { 58 | fprintf(output, "Choice: %s\n", greet); 59 | option = choices; 60 | while (*option) { 61 | fprintf(output, "%s\n", *option); 62 | option++; 63 | } 64 | do { 65 | selected = fgetc(input); // note that fgetc isn't named in 66 | // parallel to getchar. C libraries have 67 | // kind of sucky naming :( 68 | } while (selected == '\n'); 69 | 70 | option = choices; 71 | while (*option) { 72 | if (selected == *option[0]) { 73 | chosen = 1; // true 74 | break; 75 | } 76 | option++; 77 | } 78 | if (!chosen) { 79 | fprintf(output, "Unrecognized choice, select again.\n"); 80 | } 81 | } while (!chosen); 82 | 83 | return selected; 84 | } 85 | -------------------------------------------------------------------------------- /ch14_sem_shmem_mqs/shm/producer.c: -------------------------------------------------------------------------------- 1 | /* The second program is the producer and allows us to enter data for consumers. 2 | It's very similar to shm1.c and looks like this. */ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "common.h" 11 | 12 | /* The consumer_creator.c program sets up shared memory and reads data, setting 13 | * a byte in the data so that this program can write more. 14 | * 15 | * In this program, we just loop over stdin and then write out to that shared 16 | * memory whenever it is free. 17 | * 18 | * The consumer_creator.c program is in charge of deleting the shared memory 19 | * by id; this program only calls smdt, which detaches and frees local 20 | * resources. 21 | */ 22 | 23 | int main() 24 | { 25 | int running = 1; 26 | void *shared_memory = (void *)0; 27 | struct shared_use_st *shared_stuff; 28 | char buffer[BUFSIZ]; 29 | int shmid; 30 | 31 | // we actually create the shared memory in both processes, because 32 | // using the IPC_CREAT flag twice doesn't cause issues and we get rid of 33 | // a race condition this way. But only the consumer is responsible for 34 | // deleting - notice we call shmdt but we *dont* call smctl with 35 | // IPC_RMID at the end of the function, the way we do in consumer_creator.c 36 | shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT); 37 | 38 | if (shmid == -1) { 39 | fprintf(stderr, "shmget failed\n"); 40 | exit(EXIT_FAILURE); 41 | } 42 | 43 | shared_memory = shmat(shmid, (void *)0, 0); 44 | if (shared_memory == (void *)-1) { 45 | fprintf(stderr, "shmat failed\n"); 46 | exit(EXIT_FAILURE); 47 | } 48 | 49 | printf("Memory attached at %p\n", shared_memory); 50 | 51 | shared_stuff = (struct shared_use_st *)shared_memory; 52 | while(running) { 53 | while(shared_stuff->written_by_you == 1) { 54 | sleep(1); 55 | printf("waiting for consumer...\n"); 56 | } 57 | printf("Enter some text: "); 58 | fgets(buffer, BUFSIZ, stdin); 59 | 60 | strncpy(shared_stuff->some_text, buffer, TEXT_SZ); 61 | shared_stuff->written_by_you = 1; 62 | 63 | if (strncmp(buffer, "end", 3) == 0) { 64 | running = 0; 65 | } 66 | } 67 | 68 | if (shmdt(shared_memory) == -1) { 69 | fprintf(stderr, "shmdt failed\n"); 70 | exit(EXIT_FAILURE); 71 | } 72 | exit(EXIT_SUCCESS); 73 | } 74 | -------------------------------------------------------------------------------- /ch14_sem_shmem_mqs/mq/consumer.c: -------------------------------------------------------------------------------- 1 | /* Here's the receiver program. */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | /* Simple consumer that receives messages of type 12 | * my_msg_st. over a message queue. 13 | * 14 | * Note that unlike with shared memory and semaphores, we don't need a 15 | * msgctl call to initialize the global resource - the message queue is ready 16 | * to use as soon as we've called msgget, neither the consumer nor producer 17 | * programs initialize it. 18 | * 19 | * Note that all the messages we send involve a struct that starts with a long. 20 | * This is a part of the messaging api: that long is used as a key in the 21 | * msgrcv function, which allows a single queue to be used for different 22 | * purposes and/or by different clients. 23 | */ 24 | 25 | struct my_msg_st { 26 | long int my_msg_type; 27 | char some_text[BUFSIZ]; 28 | }; 29 | 30 | int main() 31 | { 32 | int running = 1; 33 | int msgid; 34 | struct my_msg_st some_data; 35 | long int msg_to_receive = 0; 36 | 37 | // set up access to the message queue 38 | msgid = msgget((key_t)1234, 0666 | IPC_CREAT); 39 | if (msgid == -1) { 40 | fprintf(stderr, "msgget failed with error: %d\n", errno); 41 | exit(EXIT_FAILURE); 42 | } 43 | 44 | // loop over reading a `my_msg_st` struct from the queue. 45 | // 46 | // Note that unlike the shared memory example, we are doing i/o type 47 | // ops and have to copy data here, just like with files and pipes. So 48 | // although it's much simpler and more robust than shared memory, this 49 | // isn't as efficient in some settings. (and of course unlike shared 50 | // memory, we can't do random access here) 51 | // 52 | // whenever we see end, we exit. 53 | while(running) { 54 | if (msgrcv(msgid, (void *)&some_data, BUFSIZ, 55 | msg_to_receive, 0) == -1) { 56 | fprintf(stderr, "msgrcv failed with error: %d\n", errno); 57 | exit(EXIT_FAILURE); 58 | } 59 | printf("You wrote: %s", some_data.some_text); 60 | if (strncmp(some_data.some_text, "end", 3) == 0) { 61 | running = 0; 62 | } 63 | } 64 | 65 | // delete the message queue. This is important not to forget. 66 | if (msgctl(msgid, IPC_RMID, 0) == -1) { 67 | fprintf(stderr, "msgctl(IPC_RMID) failed\n"); 68 | exit(EXIT_FAILURE); 69 | } 70 | 71 | exit(EXIT_SUCCESS); 72 | } 73 | 74 | -------------------------------------------------------------------------------- /ch15_sockets/daytime_examples/udp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* This program does the same thing as tcp.c, but using the SOCK_DGRAM 9 | * type of socket, which in the inet domain means using udp. 10 | * 11 | * Note the changes: 12 | * 1. we specify udp in the getservbyname. the host is the same. 13 | * 2. when we call socket(), we specify SOCK_DGRAM 14 | * 3. there's no connect() step. We send and recieve messges with the 15 | * actual server socket (not a deticated two-way socket as with tcp), 16 | * using the `sendto` and `recvfrom` functions. 17 | */ 18 | 19 | int main(int argc, char *argv[]) { 20 | char *host; 21 | int sockfd; 22 | int len, result; 23 | struct sockaddr_in address; 24 | struct hostent *hostinfo; 25 | struct servent *servinfo; 26 | char buffer[128]; 27 | 28 | // the hosst is either localhost or anything specified in arg 1 29 | host = (argc == 1) ? "localhost" : argv[1]; 30 | 31 | // look up the host info 32 | hostinfo = gethostbyname(host); 33 | if(!hostinfo) { 34 | fprintf(stderr, "no host: %s\n", host); 35 | exit(1); 36 | } 37 | 38 | // look up the service info. Note that we specify the udp version! 39 | servinfo = getservbyname("daytime", "udp"); 40 | if(!servinfo) { 41 | fprintf(stderr,"no daytime service\n"); 42 | exit(1); 43 | } 44 | printf("daytime port is %d\n", ntohs(servinfo -> s_port)); 45 | 46 | // construct the address. Note that (other than s_port) it is the same. 47 | address.sin_family = AF_INET; 48 | address.sin_port = servinfo -> s_port; 49 | address.sin_addr = *(struct in_addr *)*hostinfo -> h_addr_list; 50 | len = sizeof(address); 51 | 52 | // make the socket, then 53 | // get the information. Note there's no connect, hence to 'ask' for the 54 | // data we have to just send a byte. 55 | sockfd = socket(AF_INET, SOCK_DGRAM, 0); 56 | result = sendto(sockfd, buffer, 1, 0, (struct sockaddr *)&address, len); 57 | result = recvfrom(sockfd, buffer, sizeof(buffer), 0, 58 | (struct sockaddr *)&address, &len); 59 | // the `result` is the number of bytes received, so null out the next 60 | // one (apparently udp doesn't do it for us, which makes sense b/c for 61 | // binary data we wouldn't care) and then print. 62 | buffer[result] = '\0'; 63 | printf("read %d bytes: %s", result, buffer); 64 | 65 | close(sockfd); 66 | exit(0); 67 | } 68 | -------------------------------------------------------------------------------- /ch5_term/basic_menu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* Basic menu. It has some messy newline handling because by default the tty 6 | * doesn't send us data until a newline is entered, and a bit of messy 7 | * code because getchar() picks up newlines. 8 | * ... we fix this in a later program by modifying how tty works 9 | * 10 | * It also detects if we are piping output and crashes. 11 | * ... we show a workaround in a later program. 12 | */ 13 | 14 | // note the null-termination, a common C idiom. 15 | char *menu[] = { 16 | "a - add new record", 17 | "d - delete record", 18 | "q - quit", 19 | NULL, 20 | }; 21 | 22 | int getchoice(char * greet, char * choices[]); 23 | 24 | int main() { 25 | 26 | // this bit of logic forces the app to quit if we are piping to 27 | // files. A later version will have a real workaround. 28 | if (!isatty(fileno(stdout))) { 29 | fprintf(stderr, "You are not a terminal!"); 30 | exit(1); 31 | } 32 | 33 | // this bit of logic actually drives the menu 34 | int choice = 0; 35 | do { 36 | choice = getchoice("Please select an action", menu); 37 | printf("You have chosen: %c\n", choice); 38 | } while (choice != 'q'); 39 | 40 | return 0; 41 | } 42 | 43 | /* Display a menu with a heading line that says "Choice: ", and 44 | * wait for the user to enter a valid choice. 45 | */ 46 | int getchoice(char * greet, char * choices[]) { 47 | int selected; 48 | int chosen = 0; // bool 49 | char **option; 50 | 51 | do { 52 | printf("Choice: %s\n", greet); 53 | // note the use of the null termination here 54 | option = choices; 55 | while (*option) { 56 | printf("%s\n", *option); 57 | option++; 58 | } 59 | 60 | // this is messy, but without it we will pick up the newline that 61 | // ended the previous menu choice in getchar() 62 | do { 63 | selected = getchar(); 64 | } while (selected == '\n'); 65 | 66 | option = choices; 67 | while (*option) { 68 | if (selected == *option[0]) { 69 | chosen = 1; // true 70 | break; 71 | } 72 | option++; 73 | } 74 | if (!chosen) { 75 | // Notice that we get one of these after every successful choice, 76 | // because getchar() picks up our newlines. 77 | printf("Unrecognized choice, select again.\n"); 78 | } 79 | } while (!chosen); 80 | 81 | return selected; 82 | } 83 | -------------------------------------------------------------------------------- /ch6_curses/macbeth.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /* This is a simple deterministic demo of curses writing, using an 8 | * animation of some lines from Macbeth. 9 | * 10 | * Compared to the term code from chapter 5, this is very clean indeed :) 11 | */ 12 | 13 | int main() { 14 | // the space in front serves a purpose; it isn't ever printed, but allows 15 | // a clean while loop. See the code at line 47. 16 | const char witch_one[] = " First Witch "; 17 | const char witch_two[] = " Second Witch "; 18 | const char *scan_ptr; 19 | 20 | // initscr() always needs to be called at the start of a curses program. 21 | // It returns a WINDOW *, but the output isn't usually used. 22 | initscr(); 23 | 24 | /* make a title, then wait 1 second */ 25 | move(5, 10); 26 | attron(A_STANDOUT); // turn on bold for the next print 27 | printw("%s", "Macbeth"); // printw has similar semantics to printf 28 | attroff(A_STANDOUT); 29 | refresh(); // nothing gets writen until you call this. 30 | sleep(1); 31 | 32 | /* make a boldfaced bit for the sound effects */ 33 | move(8, 10); 34 | attron(A_BOLD); 35 | printw("%s", "Thunder and Ligning"); 36 | attroff(A_BOLD); 37 | 38 | /* put the text on the screen, then wait 1 sec */ 39 | move(10, 10); 40 | printw("%s", "When shall we three meet again?"); 41 | move(11, 23); 42 | printw("%s", "In thunder, ligtning, or in rain?"); 43 | move(13, 10); 44 | printw("%s", "When the hurlyburly's done,"); 45 | move(14, 23); 46 | printw("%s", "When the battle's lost and won"); 47 | refresh(); 48 | sleep(1); 49 | 50 | /* insert actor identifications one char at a time in reverse order */ 51 | // (insch = insert character, also remember postfix '--' is done after use) 52 | attron(A_DIM); 53 | scan_ptr = witch_one + strlen(witch_one) - 1; 54 | while (scan_ptr != witch_one) { 55 | move(10, 10); 56 | insch(*scan_ptr--); 57 | usleep(50000); // usleep is in microseconds 58 | refresh(); // dont't forget this if you want to see results! 59 | } 60 | scan_ptr = witch_two + strlen(witch_one) - 1; 61 | while (scan_ptr != witch_two) { 62 | move(13, 10); 63 | insch(*scan_ptr--); 64 | usleep(50000); 65 | refresh(); 66 | } 67 | 68 | 69 | attroff(A_DIM); 70 | 71 | /* move the cursor to the bottom-right corner and tidy up */ 72 | move(LINES - 1, COLS - 1); 73 | refresh(); 74 | sleep(1); 75 | 76 | // always call this at the end of a curses program 77 | endwin(); 78 | exit(EXIT_SUCCESS); 79 | } 80 | -------------------------------------------------------------------------------- /ch3_files/mmap_ex_5.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | typedef struct { 8 | int integer; 9 | char string[24]; 10 | } Record; 11 | 12 | #define NRECORDS 10 13 | 14 | /* write records to data_file */ 15 | void create_records(char * data_file) { 16 | FILE *file; 17 | int i; 18 | Record record; 19 | file = fopen(data_file, "w+"); 20 | for (i=0; i < NRECORDS; i++) { 21 | record.integer = i; 22 | sprintf(record.string, "record %d", i); // sprintf is *not* bounds safe 23 | fwrite(&record, sizeof(Record), 1, file); 24 | } 25 | fclose(file); 26 | } 27 | 28 | /* read records from data_file */ 29 | void print_records(char * data_file) { 30 | FILE *file; 31 | int i; 32 | Record record; 33 | file = fopen(data_file, "r"); 34 | printf("-----------\n"); 35 | for (i=0; i < NRECORDS; i++) { 36 | fread(&record, sizeof(Record), 1, file); 37 | printf("Record{%d, \"%s\"}\n", record.integer, record.string); 38 | } 39 | fclose(file); 40 | } 41 | 42 | 43 | 44 | 45 | int main(int argc, char *argv[]) { 46 | 47 | if (argc != 2) { 48 | printf("Usage: %s \n", argv[0]); 49 | } 50 | 51 | // declarations 52 | Record record, *mapped; 53 | char *data_file = argv[1]; 54 | int i, fdes; 55 | FILE *file; 56 | 57 | // write a bunch of records to the file 58 | create_records(data_file); 59 | 60 | print_records(data_file); 61 | 62 | // change record 4 by hand. This is what you would do without mmap 63 | file = fopen(data_file, "r+"); 64 | fseek(file, 4 * sizeof(Record), SEEK_SET); 65 | fread(&record, sizeof(Record), 1, file); 66 | record.integer = 14; 67 | sprintf(record.string, "changed to %d", record.integer); 68 | fseek(file, 4 * sizeof(Record), SEEK_SET); 69 | fwrite(&record, sizeof(Record), 1, file); 70 | fclose(file); 71 | 72 | print_records(data_file); 73 | 74 | // now, do a similar update but use mmap. Note it uses a raw 75 | // file descriptor, not a FILE 76 | fdes = open(data_file, O_RDWR); 77 | mapped = (Record *)mmap(0, NRECORDS * sizeof(Record), 78 | PROT_READ|PROT_WRITE, MAP_SHARED, fdes, 0); 79 | mapped[4].integer = 24; 80 | sprintf(mapped[4].string, "changed to %d", mapped[4].integer); 81 | msync((void *)mapped, NRECORDS * sizeof(Record), MS_ASYNC); 82 | munmap((void *)mapped, NRECORDS * sizeof(Record)); 83 | close(fdes); 84 | 85 | print_records(data_file); 86 | 87 | // all done! 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /ch12_pthreads/cancel_ex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void *thread_function(void *arg); 7 | 8 | /* Simple demo of cancelling a thread. 9 | * 10 | * by setting cancelstate to PTHREAD_CANCEL_ENABLE you allow cancels 11 | * in the thread (if you use DISABLE, it ignores them) 12 | * by setting canceltype to PTHREAD_CANCEL_DEFERRED you make it so the 13 | * cancel doesn't terminate the thread until the next call to a mutex 14 | * operation or a sleep or some other blocking point. The exact set of 15 | * functions that act as cancellation points is implementation-dependent; 16 | * we use sleep here but it isn't guaranteed, so if you rely on this, 17 | * you should call pthread_testcancel() in the thread 18 | * If you use PTHREAD_CANCEL_ASYNCHRONOUS, cancels happen immediately instead 19 | * 20 | * We actually don't need to setcancelstate or setcanceltype here, because 21 | * PTHREAD_CANCEL_ENABLE and PTHREAD_CANCEL_DEFERRED are the defaults. But 22 | * we do it explicilty to demo the api. 23 | */ 24 | 25 | int main() { 26 | int res; 27 | pthread_t a_thread; 28 | void *thread_result; 29 | 30 | // make the thread 31 | res = pthread_create(&a_thread, NULL, thread_function, NULL); 32 | if (res != 0) { 33 | perror("Thread creation failed"); 34 | exit(EXIT_FAILURE); 35 | } 36 | 37 | // give it some time to run, then cancel it 38 | sleep(3); 39 | printf("Canceling thread...\n"); 40 | res = pthread_cancel(a_thread); 41 | if (res != 0) { 42 | perror("Thread cancelation failed"); 43 | exit(EXIT_FAILURE); 44 | } 45 | 46 | // join the thread, which will happen as soon as it picks up the cancel 47 | printf("Waiting for thread to finish...\n"); 48 | res = pthread_join(a_thread, &thread_result); 49 | if (res != 0) { 50 | perror("Thread join failed"); 51 | exit(EXIT_FAILURE); 52 | } 53 | 54 | exit(EXIT_SUCCESS); 55 | } 56 | 57 | void *thread_function(void *arg) { 58 | int i, res, j; 59 | /* 60 | res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); 61 | if (res != 0) { 62 | perror("Thread pthread_setcancelstate failed"); 63 | exit(EXIT_FAILURE); 64 | } 65 | res = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); 66 | if (res != 0) { 67 | perror("Thread pthread_setcanceltype failed"); 68 | exit(EXIT_FAILURE); 69 | } 70 | */ 71 | printf("thread_function is running\n"); 72 | for(i = 0; i < 10; i++) { 73 | printf("Thread is still running (%d)...\n", i); 74 | sleep(1); 75 | } 76 | pthread_exit(0); 77 | } 78 | 79 | -------------------------------------------------------------------------------- /ch15_sockets/unix_af/client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* Simple example of a client program, communicating via unix 10 | * domain socket. 11 | * 12 | * The interface between a unix socket and an inet socket is identical, the 13 | * only difference is in the address. By creating a sockaddr_un struct for 14 | * the address and setting AF_UNIX in the sun_family, we specify that the 15 | * socket is a unix domain socket 16 | * (why do we need the sun_family if the struct is already specific? Because 17 | * we pass the address to functions that take a more general sockaddr, 18 | * which is bigger, so the first few bytes need to specify what kind it 19 | * is... this is basically C-style inheritance) 20 | * 21 | * When we create the sockfd with the call to socket, we likewise specify 22 | * that the domain is an AF_UNIX socket. We also specify that it's a stream, 23 | * which over inet means tcp: a two way file-like reliable byte stream. We 24 | * interact with the file descriptor just as we would with a named pipe. 25 | * 26 | * In order to actually hook up our socket file descriptor, we call connect, 27 | * which give it the address. On the server side, this shows up in a queue 28 | * under the named socket the server is listening to (and once the server 29 | * accepts, a new unnamed socket will be created to handle this connection... 30 | * the named socket is *only* used to detect new ones. 31 | * 32 | * We close the socket file descriptor when we are finished, just as we would 33 | * any other file descriptor. 34 | */ 35 | 36 | int main() { 37 | int sockfd; 38 | int len; 39 | struct sockaddr_un address; 40 | int result; 41 | char ch = 'A'; 42 | 43 | // create the file descriptor. We need to specify the domain and the 44 | // type (STREAM or DGRAM), but not the address. 45 | sockfd = socket(AF_UNIX, SOCK_STREAM, 0); 46 | 47 | // create the address and connect 48 | address.sun_family = AF_UNIX; 49 | strcpy(address.sun_path, "/tmp/server.sock"); 50 | len = sizeof(address); 51 | // note that we cast our address to a general sockaddr *, and we 52 | // tell connect() it's size. 53 | result = connect(sockfd, (struct sockaddr *)&address, len); 54 | if(result == -1) { 55 | perror("oops: client1"); 56 | exit(1); 57 | } 58 | 59 | // at this point, we can treat it like any other file descriptor. 60 | write(sockfd, &ch, 1); 61 | read(sockfd, &ch, 1); 62 | printf("char from server = %c\n", ch); 63 | close(sockfd); 64 | exit(0); 65 | } 66 | 67 | -------------------------------------------------------------------------------- /ch11_sigsprocs/fork_wait_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | /* A simple fork demonstration. 9 | * 10 | * Exec takes your single process and replaces it with a new one. Fork is 11 | * the opposite in a sense: it preserves your original process and also creates 12 | * an almost exact copy of it. The only way to see the difference between the 13 | * two processes is to look at the pid variable, which will be 0 in the new 14 | * process and will be the pid of the newly started process in the parent. 15 | * -> this convention seems backward in some ways, but it makes sense because 16 | * the child can already use getpid or getppid to get it's and it's 17 | * parent's pid's, so it doesn't need anything from fork(), whereas the 18 | * parent process needs the pid from fork() in order to find it's child. 19 | * 20 | * Fork is similar to threading in some ways, but it's different in that (a) 21 | * there's more overhead, and (b) there's not a shared address space - the 22 | * forked process has its own copies of everything. 23 | * 24 | * the wait() function causes a process to wait until any of its child 25 | * processes die before resuming. It returns the pid of the child that died 26 | * (in this case there's only one child, so it's redundant, but in general 27 | * you might have many children), and if the stat_val int pointer is not 28 | * null, it writes status information which can be interpreted by 29 | * functions such as WIFEXITED. 30 | * 31 | * There's also a waitpid() function you can use to wait for only one 32 | * particular child process to finish. 33 | */ 34 | int main() 35 | { 36 | pid_t pid; 37 | char *message; 38 | int n; 39 | int exit_code; 40 | 41 | printf("fork program starting\n"); 42 | pid = fork(); 43 | switch(pid) 44 | { 45 | case -1: 46 | exit(1); 47 | case 0: 48 | message = "This is the child"; 49 | n = 5; 50 | exit_code = 37; 51 | break; 52 | default: 53 | message = "This is the parent"; 54 | n = 3; 55 | exit_code = 0; 56 | break; 57 | } 58 | 59 | for(; n > 0; n--) { 60 | puts(message); 61 | sleep(1); 62 | } 63 | 64 | if(pid) { 65 | int stat_val; 66 | pid_t child_pid; 67 | 68 | child_pid = wait(&stat_val); 69 | 70 | printf("Child has finished: PID = %d\n", child_pid); 71 | if(WIFEXITED(stat_val)) 72 | printf("Child exited with code %d\n", WEXITSTATUS(stat_val)); 73 | else 74 | printf("Child terminated abnormally\n"); 75 | } 76 | exit (exit_code); 77 | } 78 | -------------------------------------------------------------------------------- /ch14_sem_shmem_mqs/cd_app_mq/cliserv.h: -------------------------------------------------------------------------------- 1 | /* Required #include header files. */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | #define ERR_TEXT_LEN 80 13 | 14 | /* We implement the commands as enumerated types, rather than #defines. This 15 | * is a good way of allowing the compiler to do more type checking and also 16 | * helps in debugging the application, as many debuggers are able to show the 17 | * name of enumerated constants, but not the name defined by a #define 18 | * directive. The first typedef gives the type of request being sent to the 19 | * server, the second the server response to the client. */ 20 | typedef enum { 21 | s_create_new_database = 0, 22 | s_get_cdc_entry, 23 | s_get_cdt_entry, 24 | s_add_cdc_entry, 25 | s_add_cdt_entry, 26 | s_del_cdc_entry, 27 | s_del_cdt_entry, 28 | s_find_cdc_entry 29 | } client_request_e; 30 | 31 | /* Server responses are enumerated */ 32 | typedef enum { 33 | r_success = 0, 34 | r_failure, 35 | r_find_no_more 36 | } server_response_e; 37 | 38 | /* Next, we declare a structure that will form the message passed in both 39 | * directions between the two processes. 40 | * 41 | * Since we don't actually need to return both a cdc_entry and cdt_entry in the 42 | * same response, we could have combined them in a union. For simplicity, we 43 | * keep them separate. This also makes the code easier to maintain. */ 44 | typedef struct { 45 | pid_t client_pid; 46 | client_request_e request; 47 | server_response_e response; 48 | cdc_entry cdc_entry_data; 49 | cdt_entry cdt_entry_data; 50 | char error_text[ERR_TEXT_LEN + 1]; 51 | } message_db_t; 52 | 53 | /* Finally, we get to the mqueue interface functions that perform data transfer 54 | * implementedg in mqueue_imp.c. These divide into server- and client-side 55 | * functions, in the first and second blocks respectively. 56 | * 57 | * Note that we don't have functions for specific database command - these 58 | * are all encoded in side of the cient_request and server_response withing 59 | * the message_db_t struct. */ 60 | int server_starting(void); 61 | void server_ending(void); 62 | int read_request_from_client(message_db_t *rec_ptr); 63 | int start_resp_to_client(const message_db_t mess_to_send); 64 | int send_resp_to_client(const message_db_t mess_to_send); 65 | void end_resp_to_client(void); 66 | 67 | int client_starting(void); 68 | void client_ending(void); 69 | int send_mess_to_server(message_db_t mess_to_send); 70 | int start_resp_from_server(void); 71 | int read_resp_from_server(message_db_t *rec_ptr); 72 | void end_resp_from_server(void); 73 | 74 | 75 | -------------------------------------------------------------------------------- /ch4_env/resources.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* This program demos getting priority and resource data. 10 | * NOTE: it won't compile unless you add the -lm flag on many boxes, 11 | * including my ubuntu machine. math.h is added automatically, 12 | * but the linker doesn't auto-link libm. 13 | */ 14 | 15 | void work(); 16 | 17 | int main() { 18 | 19 | // get this process's priority. The higher the number, the lower the 20 | // priority (it's basically the `nice` value, see the demo notes). 21 | // the input PRIO_PROCESS says to get the priority for a process (by pid) 22 | // which is usually what we want. 23 | int priority = getpriority(PRIO_PROCESS, getpid()); 24 | printf("Current priority = %d\n\n", priority); 25 | 26 | // get resource usage date for this process, after doing some work. 27 | // ru_utime is the user type, usec is microseconds 28 | struct rusage r_usage; 29 | work(); 30 | getrusage(RUSAGE_SELF, &r_usage); 31 | printf("CPU usage: User = %ld.%06ld, System = %ld.%06ld\n\n", 32 | r_usage.ru_utime.tv_sec, r_usage.ru_utime.tv_usec, 33 | r_usage.ru_stime.tv_sec, r_usage.ru_stime.tv_usec); 34 | 35 | // get the current resource limit for file sizes. 36 | // the soft limit is "an advisory limit that shouldn't be exceeded, 37 | // doing so can cause library functions to return errors" 38 | // the hard limit is the point at which the operating system might 39 | // send a terminate signal to your process. 40 | struct rlimit r_limit; 41 | getrlimit(RLIMIT_FSIZE, &r_limit); 42 | printf("Current FSIZE limit: soft = %ld, hard = %ld\n\n", 43 | r_limit.rlim_cur, r_limit.rlim_max); 44 | 45 | // set a tight limit on the file size this process can create, then 46 | // call work() again and generate an error 47 | r_limit.rlim_cur = 2048; 48 | r_limit.rlim_max = 4096; 49 | printf("Setting a 2K file size limit\n"); 50 | setrlimit(RLIMIT_FSIZE, &r_limit); 51 | printf("Limit set.\n"); 52 | work(); 53 | 54 | return 0; 55 | } 56 | 57 | /* does a bunch of io (write to a temp file) and cpu (stupid math) to 58 | * generate usage data */ 59 | void work() { 60 | FILE * f; 61 | int i; 62 | double x = 4.5; 63 | 64 | f = tmpfile(); // we ought to be doing error checking and closing the 65 | // file; we don't b/c we are just demoing apis here. 66 | for (i = 0; i < 10000; i++) { 67 | fprintf(f, "Do some output\n"); 68 | if (ferror(f)) { 69 | fprintf(stderr, "Error writing to temporary file.\n"); 70 | exit(1); 71 | } 72 | } 73 | for (i = 0; i < 1000000; i++) { 74 | x = log(x * x + 3.21); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /ch13_pipes/pipe/pipe_dup_ex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* The functions dup and dup2 let you copy file descriptors. 7 | * 8 | * you can read the docs in more detail if you need them, but in this case 9 | * what we are doing is: 10 | * - making a pipe, which creates two file descriptors (all of them > 2, 11 | * b/c stdout, stdin, and stderr are taken) 12 | * - calling fork 13 | * - in the parent, we write pretty much as before 14 | * - in he child, we use dup() to remap the file descriptor and then 15 | * close the ones opened by pipe() first, then we call exec 16 | * 17 | * -> this leads to the child process (which has file descriptor 0 18 | * free, and dup() always maps to the smallest free file descriptor) 19 | * getting the read end of the pipe as stdin. 20 | * 21 | * This means we can use ordinary programs! 22 | * We could just do this with popen much more easily, of course, but 23 | * that creates an extra shell as we saw, so in a situation where we 24 | * need low overhead it can be an issue. 25 | */ 26 | 27 | 28 | int main() { 29 | int data_processed; 30 | int file_pipes[2]; 31 | const char some_data[] = "123 456 789"; 32 | pid_t fork_result; 33 | 34 | if (pipe(file_pipes) == 0) { 35 | fork_result = fork(); 36 | 37 | // handle errors 38 | if (fork_result == (pid_t)-1) { 39 | fprintf(stderr, "Fork failure"); 40 | exit(EXIT_FAILURE); 41 | } 42 | 43 | // child process: 44 | // - call dup, which due to the fork - which leads to 45 | // a process lacking stdin - causes the file descriptor to be 46 | // mapped to 0, which is stdin 47 | // - close both of the file descriptors opened via the pipe 48 | // - then pass control off using execlp: at this point, the read 49 | // end of the pipe is stdin, so we don't need a custom process 50 | // the way we did in the pipe_exec_child.c program! 51 | if (fork_result == (pid_t)0) { 52 | close(0); 53 | dup(file_pipes[0]); 54 | close(file_pipes[0]); 55 | close(file_pipes[1]); 56 | 57 | execlp("awk", "{print $2}", NULL); 58 | exit(EXIT_FAILURE); 59 | } 60 | 61 | // parent process: just write some data to the write end of the pipe. 62 | // close both ends (we've been sloppy about this till 63 | // now) of the pipe, also. 64 | else { 65 | close(file_pipes[0]); 66 | data_processed = write(file_pipes[1], some_data, 67 | strlen(some_data)); 68 | close(file_pipes[1]); 69 | printf("%d - wrote %d bytes\n", (int)getpid(), data_processed); 70 | } 71 | } 72 | exit(EXIT_SUCCESS); 73 | } 74 | 75 | -------------------------------------------------------------------------------- /ch13_pipes/fifo/fifo_serverclient/server.c: -------------------------------------------------------------------------------- 1 | #include "protocol.h" 2 | #include 3 | 4 | /* the server side of our client-server program. See inside the 5 | * program for notes on how it works. */ 6 | 7 | int main() { 8 | int server_fifo_fd, client_fifo_fd; 9 | struct data_to_pass_st my_data; 10 | int read_res; 11 | char client_fifo_path[256]; 12 | char *tmp_char_ptr; 13 | 14 | // make the server fifo (clients write, server reads), then open it 15 | mkfifo(SERVER_FIFO_NAME, 0777); 16 | server_fifo_fd = open(SERVER_FIFO_NAME, O_RDONLY); 17 | if (server_fifo_fd == -1) { 18 | fprintf(stderr, "Server fifo failure\n"); 19 | exit(EXIT_FAILURE); 20 | } 21 | 22 | sleep(10); /* lets clients queue for demo purposes */ 23 | 24 | // repeatedly 25 | // - reads in chunks of bytes into a 26 | // ` data_to_pass_st` struct 27 | // - looks inside those bytes and, using a pointer to the 28 | // message part of the struct, convert the message to 29 | // uppercase 30 | // - looks inside those bytes and reads the client's pid 31 | // - uses the pid to set the client_fifo_path, which is a 32 | // string template for the location of a fifo to send 33 | // data back to the client. It's assumed that the client 34 | // would make it's own fifo, so we don't call mkfifo() 35 | // - open the client fifo for writing, using the path that 36 | // we just filled out 37 | // - write the data back over the client fifo 38 | // - close the client fifo fd (we have many clients, and we are 39 | // opening new fifo fds all the time, so we can't leave it open 40 | // or we might run out of fds. 41 | do { 42 | // read bytes into the struct 43 | read_res = read(server_fifo_fd, &my_data, sizeof(my_data)); 44 | if (read_res > 0) { 45 | 46 | // convert the data to uppercase (in-place) in the struct 47 | // we just read in 48 | tmp_char_ptr = my_data.some_data; 49 | while (*tmp_char_ptr) { 50 | *tmp_char_ptr = toupper(*tmp_char_ptr); 51 | tmp_char_ptr++; 52 | } 53 | 54 | // get the client fifo path from our template and the pid, 55 | // and open the fifo 56 | sprintf(client_fifo_path, CLIENT_FIFO_NAME, my_data.client_pid); 57 | client_fifo_fd = open(client_fifo_path, O_WRONLY); 58 | 59 | // write the struct (which now has an uppercased 60 | // message) back to the fifo 61 | if (client_fifo_fd != -1) { 62 | write(client_fifo_fd, &my_data, sizeof(my_data)); 63 | close(client_fifo_fd); 64 | } 65 | } 66 | } while (read_res > 0); 67 | 68 | // when we are done, close the fifo and also unlink it 69 | close(server_fifo_fd); 70 | unlink(SERVER_FIFO_NAME); 71 | exit(EXIT_SUCCESS); 72 | } 73 | 74 | -------------------------------------------------------------------------------- /ch15_sockets/unix_af/server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* Simple example of a server over a unix domain socket. 10 | * 11 | * Most of my discussion is in client.c or in the in-code comments. 12 | */ 13 | 14 | int main() 15 | { 16 | int server_sockfd, client_sockfd; 17 | int server_len, client_len; 18 | struct sockaddr_un server_address; 19 | struct sockaddr_un client_address; 20 | 21 | // open the server socket - this is only used to create connections, 22 | // the actual data transfer happens on a different socket, as we see 23 | // in the while loop 24 | server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0); 25 | 26 | // create the address of the socket 27 | server_address.sun_family = AF_UNIX; 28 | strcpy(server_address.sun_path, "/tmp/server.sock"); 29 | server_len = sizeof(server_address); 30 | 31 | // unlink any existing socket first, then bind to the address 32 | // ... this is important. If you forget to do it then it will start 33 | // generating a ton of empty connections (it will loop as fast as 34 | // it can, printing "server waiting...", which means that the 35 | // accept() isn't blocking). I'm not sure what's going on under 36 | // the hood here, but clearly it's important that the bind operation 37 | // creates a brand-new file 38 | unlink("/tmp/server.sock"); 39 | bind(server_sockfd, (struct sockaddr *)&server_address, server_len); 40 | 41 | // prep some vars to help us listen for connections 42 | client_len = sizeof(client_address); 43 | char ch; 44 | 45 | // start listening. Every connection created in accept() - which will 46 | // block waiting for connections - creates its own file descriptor. 47 | // 48 | // the connect() call in a client corresponds to a return of accept() 49 | // on the server side. 50 | listen(server_sockfd, 5); 51 | while(1) { 52 | printf("server waiting\n"); 53 | 54 | // note that we pass the client address to accept(), which will 55 | // set it and thereby let us use the client address in handling the 56 | // connection if desired. Note that we cast to sockaddr * and say 57 | // the size, much like the connect() function used client-side. 58 | client_sockfd = accept(server_sockfd, 59 | (struct sockaddr *)&client_address, 60 | &client_len); 61 | 62 | // at this point we read and write from the client like we would a 63 | // file or fifo. 64 | read(client_sockfd, &ch, 1); 65 | ch++; 66 | write(client_sockfd, &ch, 1); 67 | 68 | // don't forget to close it! if you forget this in a real server, you 69 | // will run out of file descriptors 70 | close(client_sockfd); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /ch4_env/times.c: -------------------------------------------------------------------------------- 1 | /* page 150-155. The discussion of strftime / strptime on page 2 | * 154-156 might prove especially helpful */ 3 | #define _GNU_SOURCE // this is needed for strptime to work on linux 4 | // (mac was fine without it). Without this, the compiler 5 | // warns about an int to pointer issue, and the code 6 | // segfaults. 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | void print_tm(struct tm * tm_time) { 14 | printf("\tdate: %04d-%02d-%02d\n", 1900+tm_time->tm_year, tm_time->tm_mon+1, 15 | tm_time->tm_mday); 16 | printf("\ttime: %02d:%02d:%02d\n", tm_time->tm_hour, tm_time->tm_min, 17 | tm_time->tm_sec); 18 | } 19 | 20 | int main() { 21 | int i; 22 | time_t int_time; 23 | struct tm *tm_time; 24 | 25 | // print off raw times: you can see, time is just a timestamp in seconds. 26 | for (i = 1; i <= 2; i++) { 27 | // the time function is kind of weird. It returns, but it also modifies 28 | // its input, *unless* the input is null (as is `(time_t *) 0`) 29 | // here we demo both approaches. 30 | int_time = time((time_t *) 0); 31 | printf("The time is %ld\n", int_time); 32 | sleep(1); 33 | time(&int_time); 34 | printf("The time is %ld\n", int_time); 35 | sleep(1); 36 | } 37 | 38 | // gmtime can help us print a useful time. It outputs a struct of 39 | // type `*tm_ptr`. It takes an addres to time_t, which is sort of strange. 40 | // 41 | // also, the output has years as a count since 1900. A holdout from when 42 | // memory was more precious, I think. 43 | printf("\nRaw time is %ld\n", int_time); 44 | 45 | tm_time = gmtime(&int_time); 46 | printf("gmtime gives:\n"); 47 | print_tm(tm_time); 48 | 49 | tm_time = localtime(&int_time); 50 | printf("localtime gives:\n"); 51 | print_tm(tm_time); 52 | 53 | // ctime provides more convenient times if you just want human-readable 54 | // strings to start with. 55 | printf("\nctime gives: %s\n", ctime(&int_time)); 56 | 57 | // we can also work with the `tm` struct using 58 | // strftime = string format time, for converting time to string 59 | // strptime = ? string put to time ? for converting string to time 60 | 61 | // converting from string... 62 | char buf[256]; 63 | char *remaining; 64 | strcpy(buf, "Thu 26 July 2007, 17:53 and then more text after"); 65 | printf("Converting buf = \"%s\" to time...", buf); 66 | remaining = strptime(buf, "%a %d %b %Y, %R", tm_time); 67 | printf("the copied tm is:\n"); 68 | print_tm(tm_time); 69 | printf("The remaining (unconsumed) string is \"%s\"\n", remaining); 70 | 71 | // converting back to string... 72 | printf("strftime gives:\n"); 73 | strftime(buf, 256, "%A %d %B %I:%S %p", tm_time); 74 | printf("\t\"%s\"\n", buf); 75 | 76 | exit(0); 77 | } 78 | -------------------------------------------------------------------------------- /ch14_sem_shmem_mqs/shm/writer.c: -------------------------------------------------------------------------------- 1 | /* The second program is the producer and allows us to enter data for consumers. 2 | It's very similar to shm1.c and looks like this. */ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "common.h" 11 | 12 | /* The consumer_creator.c program sets up shared memory and reads data, setting 13 | * a byte in the data so that this program can write more. 14 | * 15 | * In this program, we just loop over stdin and then write out to that shared 16 | * memory whenever it is free. 17 | * 18 | * The consumer_creator.c program is in charge of deleting the shared memory 19 | * by id; this program only calls smdt, which detaches and frees local 20 | * resources. 21 | */ 22 | 23 | int main() 24 | { 25 | int running = 1; 26 | void *shared_memory = (void *)0; 27 | struct shared_use_st *shared_stuff; 28 | char buffer[BUFSIZ]; 29 | int shmid; 30 | 31 | // we actually create the shared memory in both processes, because 32 | // using the IPC_CREAT flag twice doesn't cause issues and we get rid of 33 | // a race condition this way. But only the consumer is responsible for 34 | // deleting - notice we call shmdt but we *dont* call smctl with 35 | // IPC_RMID at the end of the function, the way we do in consumer_creator.c 36 | shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT); 37 | 38 | if (shmid == -1) { 39 | fprintf(stderr, "shmget failed\n"); 40 | exit(EXIT_FAILURE); 41 | } 42 | 43 | shared_memory = shmat(shmid, (void *)0, 0); 44 | if (shared_memory == (void *)-1) { 45 | fprintf(stderr, "shmat failed\n"); 46 | exit(EXIT_FAILURE); 47 | } 48 | 49 | printf("Memory attached at %p\n", shared_memory); 50 | 51 | // in the loop, we only let the user enter text when `written_by_you` 52 | // is 0, which means the consumer_creator is ready for more input. 53 | // 54 | // When this is the case, we then write the user input into the 55 | // `shared_stuff` struct's `some_text`, and we also set `shared_stuff`s 56 | // `written_by_you` back to 1. Then the consumer_creator can read it. 57 | // 58 | // when the user types end, we exit. 59 | shared_stuff = (struct shared_use_st *)shared_memory; 60 | while(running) { 61 | while(shared_stuff->written_by_you == 1) { 62 | sleep(1); 63 | printf("waiting for consumer...\n"); 64 | } 65 | printf("Enter some text: "); 66 | fgets(buffer, BUFSIZ, stdin); 67 | 68 | strncpy(shared_stuff->some_text, buffer, TEXT_SZ); 69 | shared_stuff->written_by_you = 1; 70 | 71 | if (strncmp(buffer, "end", 3) == 0) { 72 | running = 0; 73 | } 74 | } 75 | 76 | if (shmdt(shared_memory) == -1) { 77 | fprintf(stderr, "shmdt failed\n"); 78 | exit(EXIT_FAILURE); 79 | } 80 | exit(EXIT_SUCCESS); 81 | } 82 | -------------------------------------------------------------------------------- /ch12_pthreads/simple_example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | /* Basic example of threads, without using any tools (semaphores, mutexes, etc) 8 | * to ease interaction; interaction is via a global var. 9 | * 10 | * Demonstrates the basic calls to the pthread functions. Note that the 11 | * sleep is necessary: if two threads mutate the same global var (run_now) 12 | * at the same time, the results are undefined. The sleeps are our hackish 13 | * way of preventing this. 14 | */ 15 | void *thread_function(void *arg); 16 | int run_now = 1; 17 | char message[] = "Hello World"; 18 | 19 | int main() { 20 | int res; 21 | pthread_t a_thread; 22 | void *thread_result; 23 | int print_count1 = 0; 24 | 25 | // create a thread. To do this, you pass a (likely null) pthread_t, a 26 | // flag variable (which is NULL unless you want special behavior), a 27 | // function, and an argument to the function. 28 | // 29 | // The function always has to take a single void * argument, and return a 30 | // void *; it doesn't actually return anythong though, instead it calls 31 | // pthread_exit (which is maybe a macro?) 32 | res = pthread_create(&a_thread, NULL, thread_function, (void *)message); 33 | if (res != 0) { 34 | perror("Thread creation failed"); 35 | exit(EXIT_FAILURE); 36 | } 37 | 38 | // print 1 out, 5 times, to indicate that we are in the first thread 39 | // inside this and the thread function, we are passing control back and 40 | // forth via the global run_now variable. We'll see better ways to handle 41 | // control later. 42 | while(print_count1++ < 5) { 43 | if (run_now == 1) { 44 | printf("1"); 45 | run_now = 2; 46 | } else { 47 | sleep(1); 48 | } 49 | } 50 | 51 | // join the thread. 52 | // ... note that thread_result comes from the call to pthread_exit inside 53 | // the thread. Also note that we pass not the pointer, but the 54 | // address of the pointer. The pthread_join will make it point to 55 | // data created in the thread. 56 | printf("\nIn main, waiting for thread to finish...\n"); 57 | res = pthread_join(a_thread, &thread_result); 58 | if (res != 0) { 59 | perror("Thread join failed"); 60 | exit(EXIT_FAILURE); 61 | } 62 | printf("Thread joined, thread_result (cast to str) is %s\n", 63 | (char *) thread_result); 64 | exit(EXIT_SUCCESS); 65 | } 66 | 67 | /* print 2 out, 5 times, to indicate that we are in the second thread */ 68 | void *thread_function(void *arg) { 69 | int print_count2 = 0; 70 | while(print_count2++ < 5) { 71 | if (run_now == 2) { 72 | printf("2"); 73 | run_now = 1; 74 | } else { 75 | sleep(1); 76 | } 77 | } 78 | printf("\nInside thread, message (arg) was %s\n", (char *) arg); 79 | sleep(3); 80 | pthread_exit((void *) "This is what my thread 'returns' via pthread_exit"); 81 | } 82 | -------------------------------------------------------------------------------- /ch14_sem_shmem_mqs/README: -------------------------------------------------------------------------------- 1 | This chapter covers three IPC tools that are collectively often called 2 | "System V IPC" (the Advanced Unix Programming book I have also calls them 3 | by this name). 4 | 5 | All three of these identify resources which are global to a computer using 6 | a key_t, which is an integer type defined in headers. The book doesn't say, 7 | but my guess is that in practice you might place the key as text in a config 8 | file if you needed computers to use these tools; these example programs just 9 | hard-code them. 10 | 11 | Naming Conventions 12 | ------------------ 13 | All three of these facilities have the same semantics for setting up and 14 | removing the process vs global resources. 15 | XXget always creates a local object pointing to a global resource which 16 | might or might not exist. 17 | XXctl is always used for global control. You use this to make sure the 18 | global resource exists and / or is set as desired. For example, in our 19 | simple sempahore api wrapper, we use semctl to create and set the global 20 | semaphore to 1. 21 | 22 | The other functions are specific: 23 | semop 24 | shmat, shmdt 25 | msgsnd msgrcv 26 | 27 | 28 | Semaphores 29 | ---------- 30 | 31 | The semaphores we use in this chapter are very similar in principle to the 32 | ones used in Chapter 12. The library is completely different, and is focused 33 | on IPC rather than inter-thread communication. The api is a lot more 34 | complicated, but in the example we show how to wrap it in simpler functions 35 | so that semaphores may be used in a binary way as locks, just like we did 36 | in Chapter 12. 37 | 38 | Shared Memory 39 | ------------- 40 | 41 | Typically IPC is via some form of i/o: files provide a simple option; 42 | sockets are common in applications that can be networked (like MYSQL); pipes 43 | are physically in ram, but the os makes them look like files. 44 | 45 | But the System V shared memory allows shared random-access memory. It's not 46 | guaranteed to be the same phycial memory - the os can choose to mirror the 47 | allocated memory - but on many computers it in fact is the same physical 48 | memory. 49 | 50 | The shared memory library provides no methods for synchroniziong access to 51 | shared memory, it's up to the programmer to use some other method (very likely 52 | the semaphores just discussed, but possibly pipes, file locks, or message 53 | queues) to communicate information about who should look at what when. 54 | 55 | Messages 56 | -------- 57 | 58 | System V message queues provide a slightly easier alternative to most of the 59 | same functionality as using fifos. The main advantage is it releives you of 60 | all the file-descriptor nonsense, allowing you to use an api much closer to 61 | the logical domain of your problem: send and recieve chunks of bytes. 62 | 63 | A disadvantage is that the arbitrary key_t int used to identify global 64 | system V resources is much less transparent than the file-like approach to 65 | identifying a fifo / named pipe. (Of course there are anonymous pipes, but they 66 | aren't global, so there's less concern about your code being leaky). 67 | -------------------------------------------------------------------------------- /ch13_pipes/cd_app_fifo/cliserv.h: -------------------------------------------------------------------------------- 1 | /* Required #include header files. */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | /* We then define the named pipes. We use one pipe for the server and one pipe 12 | * for each client. Since there may be multiple clients, the client 13 | * incorporates a process ID into the name to ensure that its pipe is unique. 14 | */ 15 | 16 | #define SERVER_PIPE "/tmp/server_pipe" 17 | #define CLIENT_PIPE "/tmp/client_%d_pipe" 18 | 19 | #define ERR_TEXT_LEN 80 20 | 21 | /* We implement the commands as enumerated types, rather than #defines. This 22 | * is a good way of allowing the compiler to do more type checking and also 23 | * helps in debugging the application, as many debuggers are able to show the 24 | * name of enumerated constants, but not the name defined by a #define 25 | * directive. The first typedef gives the type of request being sent to the 26 | * server, the second the server response to the client. */ 27 | typedef enum { 28 | s_create_new_database = 0, 29 | s_get_cdc_entry, 30 | s_get_cdt_entry, 31 | s_add_cdc_entry, 32 | s_add_cdt_entry, 33 | s_del_cdc_entry, 34 | s_del_cdt_entry, 35 | s_find_cdc_entry 36 | } client_request_e; 37 | 38 | /* Server responses are enumerated */ 39 | typedef enum { 40 | r_success = 0, 41 | r_failure, 42 | r_find_no_more 43 | } server_response_e; 44 | 45 | /* Next, we declare a structure that will form the message passed in both 46 | * directions between the two processes. 47 | * 48 | * Since we don't actually need to return both a cdc_entry and cdt_entry in the 49 | * same response, we could have combined them in a union. For simplicity, we 50 | * keep them separate. This also makes the code easier to maintain. */ 51 | typedef struct { 52 | pid_t client_pid; 53 | client_request_e request; 54 | server_response_e response; 55 | cdc_entry cdc_entry_data; 56 | cdt_entry cdt_entry_data; 57 | char error_text[ERR_TEXT_LEN + 1]; 58 | } message_db_t; 59 | 60 | /* Finally, we get to the pipe interface functions that perform data transfer 61 | * implementedg in pipe_imp.c. These divide into server- and client-side 62 | * functions, in the first and second blocks respectively. 63 | * 64 | * Note that we don't have functions for specific database command - these 65 | * are all encoded in side of the cient_request and server_response withing 66 | * the message_db_t struct. */ 67 | int server_starting(void); 68 | void server_ending(void); 69 | int read_request_from_client(message_db_t *rec_ptr); 70 | int start_resp_to_client(const message_db_t mess_to_send); 71 | int send_resp_to_client(const message_db_t mess_to_send); 72 | void end_resp_to_client(void); 73 | 74 | int client_starting(void); 75 | void client_ending(void); 76 | int send_mess_to_server(message_db_t mess_to_send); 77 | int start_resp_from_server(void); 78 | int read_resp_from_server(message_db_t *rec_ptr); 79 | void end_resp_from_server(void); 80 | 81 | 82 | -------------------------------------------------------------------------------- /ch12_pthreads/manythread_ex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define NUM_THREADS 6 8 | 9 | /* Demonstration of starting many threads. 10 | * 11 | * We also show here that the void * argument to a thread function doesn't 12 | * actually have to be a pointer... in an initial version the authors pass 13 | * an int via pointer, but this actually leads to race conditions because we 14 | * are incrementing the lots_of_threads var in the main thread. In this 15 | * version, instead we pass the int by value but cast to void *, and then cast 16 | * back to int. 17 | * -> I'm pretty sure this hack only works because int is no larger than 18 | * void *, so when you cast int to void * you potentially add some bits, 19 | * and then after passing by value (all passes are by value in C!) when 20 | * you cast back to int you just discard those bits. If you tried to 21 | * pass a big struct this way it would presumably fail. But, ints are 22 | * very likely the most common use case for spnning up many threads like 23 | * this. 24 | * -> the compiler warns on the casts... they are probably bad practice. 25 | * but I'm leaving them in the demo because they are cool examples of 26 | * what's possible, and they don't interfere with showing the api. 27 | * 28 | * Because we spin up all the threads almost at once, the order in which 29 | * they announce themselves is close to random. In a test run, for instance, 30 | * the first one to say hi was thread 3. 31 | */ 32 | 33 | void *thread_function(void *arg); 34 | 35 | int main() { 36 | 37 | int res; 38 | pthread_t a_thread[NUM_THREADS]; 39 | void *thread_result; 40 | int lots_of_threads; 41 | 42 | 43 | for(lots_of_threads = 0; lots_of_threads < NUM_THREADS; lots_of_threads++) { 44 | // note that we don't actually pass a pointer here, but we cast 45 | // to a pointer... this is pretty strange! We are kind of sidestepping 46 | // the operating system's thread api. 47 | res = pthread_create(&(a_thread[lots_of_threads]), NULL, 48 | thread_function, (void *)lots_of_threads); 49 | if (res != 0) { 50 | perror("Thread creation failed"); 51 | exit(EXIT_FAILURE); 52 | } 53 | } 54 | 55 | printf("Waiting for threads to finish...\n"); 56 | for(lots_of_threads = NUM_THREADS - 1; lots_of_threads >= 0; lots_of_threads--) { 57 | res = pthread_join(a_thread[lots_of_threads], &thread_result); 58 | if (res == 0) { 59 | printf("Picked up a thread\n"); 60 | } else { 61 | perror("pthread_join failed"); 62 | } 63 | } 64 | 65 | printf("All done\n"); 66 | exit(EXIT_SUCCESS); 67 | } 68 | 69 | void *thread_function(void *arg) { 70 | int my_number = (int)arg; 71 | int rand_num; 72 | 73 | printf("thread_function is running. Argument was %d\n", my_number); 74 | rand_num=1+(int)(9.0*rand()/(RAND_MAX+1.0)); 75 | sleep(rand_num); 76 | printf("Bye from %d\n", my_number); 77 | 78 | pthread_exit(NULL); 79 | } 80 | -------------------------------------------------------------------------------- /ch8_mysql/mutate.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "mysql.h" 5 | 6 | /* Minimal example of failing to create a conection and printing errors 7 | */ 8 | 9 | int main(int argc, char *argv[]) { 10 | 11 | MYSQL my_conn; 12 | MYSQL_RES *res_ptr; 13 | MYSQL_ROW sqlrow; 14 | int res; 15 | 16 | if (!mysql_init(&my_conn)) { 17 | fprintf(stderr, "mysql_init failed\n"); 18 | return EXIT_FAILURE; 19 | } 20 | 21 | if (mysql_real_connect( 22 | &my_conn, "localhost", "trox", "trox", "foo", 0, NULL, 0)) { 23 | printf("Connection success\n"); 24 | 25 | /* update Jenny to be 8 and not 7 */ 26 | res = mysql_query(&my_conn, 27 | "UPDATE children SET age = 8 WHERE fname = 'Jenny'"); 28 | if (!res) { // return of zero means success 29 | printf("Updated %lu rows\n", 30 | (unsigned long) mysql_affected_rows(&my_conn)); 31 | } else { 32 | fprintf(stderr, "UPDATE error %d: %s\n", 33 | mysql_errno(&my_conn), mysql_error(&my_conn)); 34 | } 35 | 36 | /* Add a new entry, Flora */ 37 | res = mysql_query( 38 | &my_conn, 39 | "INSERT INTO children(fname, age) VALUES('Flora', 10)" 40 | ); 41 | if (!res) { // return of zero means success 42 | printf("Inserted %lu rows\n", 43 | (unsigned long) mysql_affected_rows(&my_conn)); 44 | } else { 45 | fprintf(stderr, "INSERT error %d: %s\n", 46 | mysql_errno(&my_conn), mysql_error(&my_conn)); 47 | } 48 | 49 | /* See what the auto-incremented childno was for Flora */ 50 | res = mysql_query(&my_conn, "SELECT LAST_INSERT_ID()"); 51 | if (!res) { // return of zero means success 52 | // get the result, in a single batch 53 | res_ptr = mysql_store_result(&my_conn); 54 | if (res_ptr) { 55 | // count the number of rows. This works for a result that 56 | // was obtained via mysql_store_result, but not if you get 57 | // it from mysql_use_result (see next example)... the same 58 | // goes for the mysql_row_seek function, which you will rarely 59 | // use but lets you seek rows in batched result sets. 60 | printf("Selecting for last insert id gave %lu rows\n", 61 | (unsigned long) mysql_num_rows(res_ptr)); 62 | // there should only be one row, but this is the typical 63 | // pattern for iterating over a result. 64 | while ((sqlrow = mysql_fetch_row(res_ptr))) { 65 | printf("We inserted childno %s\n", sqlrow[0]); 66 | } 67 | // use this method to release allocated resources 68 | mysql_free_result(res_ptr); 69 | } 70 | } else { 71 | fprintf(stderr, "SELECT error %d: %s\n", 72 | mysql_errno(&my_conn), mysql_error(&my_conn)); 73 | } 74 | } else { 75 | fprintf(stderr, "Connection failed %d: %s\n", 76 | mysql_errno(&my_conn), mysql_error(&my_conn)); 77 | } 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /ch5_term/read_immediately.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* This is very similar to the menu which reads from /dev/tty, but we add 7 | * termios controls to make characters read without requiring the user 8 | * to input a newline, and also without echoing (so some of the code looks 9 | * like password.c). 10 | */ 11 | 12 | char *menu[] = { 13 | "a - add new record", 14 | "d - delete record", 15 | "q - quit", 16 | NULL, 17 | }; 18 | 19 | int getchoice(char * greet, char * choices[], FILE * input, FILE * output); 20 | 21 | int main() { 22 | 23 | FILE * input, * output; 24 | struct termios initialsettings, newsettings; 25 | tcgetattr(fileno(stdin), &initialsettings); 26 | newsettings = initialsettings; 27 | newsettings.c_lflag &= ~ICANON; // process input immediately 28 | newsettings.c_lflag &= ~ISIG; // treat control signals as orinary 29 | // input (so, e.g., ^C won't interrupt) 30 | newsettings.c_lflag &= ~ECHO; // as with the password program, no echo 31 | 32 | 33 | if (!isatty(fileno(stdout))) { 34 | fprintf(stderr, "You are not at a terminal. That's okay."); 35 | } 36 | 37 | input = fopen("/dev/tty", "r"); 38 | output = fopen("/dev/tty", "w"); 39 | if (!input || !output) { 40 | fprintf(stderr, "Unable to open /dev/tty\n"); 41 | exit(1); 42 | } 43 | if (tcsetattr(fileno(input), TCSANOW, &newsettings) != 0) { 44 | fprintf(stderr, "Could not set tc attributes!\n"); 45 | } 46 | 47 | int choice = 0; 48 | do { 49 | choice = getchoice("Please select an action", menu, input, output); 50 | printf("You have chosen: %c\n", choice); 51 | } while (choice != 'q'); 52 | 53 | // always reset tc attributes! 54 | tcsetattr(fileno(input), TCSANOW, &initialsettings); 55 | return 0; 56 | } 57 | 58 | /* Display a menu with a heading line that says "Choice: ", and 59 | * wait for the user to enter a valid choice. 60 | * 61 | * input and output are FILE* here. 62 | */ 63 | int getchoice(char * greet, char * choices[], FILE * input, FILE * output) { 64 | int selected; 65 | int chosen = 0; // bool 66 | char **option; 67 | 68 | do { 69 | fprintf(output, "Choice: %s\n", greet); 70 | option = choices; 71 | while (*option) { 72 | fprintf(output, "%s\n", *option); 73 | option++; 74 | } 75 | do { 76 | selected = fgetc(input); 77 | // note that we added carriage returns to the exceptions. Since we 78 | // don't require a newline anymore this isn't critical, but the reason 79 | // we do it is we've turned off the terminal's auto-conversion of 80 | // \r to \n, which means depending on hardware we might see 'enter' 81 | // presses as '\r' rather than '\n'. 82 | } while (selected == '\n' || selected == '\r'); 83 | 84 | option = choices; 85 | while (*option) { 86 | if (selected == *option[0]) { 87 | chosen = 1; // true 88 | break; 89 | } 90 | option++; 91 | } 92 | if (!chosen) { 93 | fprintf(output, "Unrecognized choice, select again.\n"); 94 | } 95 | } while (!chosen); 96 | 97 | return selected; 98 | } 99 | -------------------------------------------------------------------------------- /ch13_pipes/fifo/fifo_open_demo.c: -------------------------------------------------------------------------------- 1 | // Let's start with the header files, a #define and the check that the correct number 2 | // of command-line arguments have been supplied. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | /* This demo shows using various flags when opening named pipes. 13 | * 14 | * Pipes can only be opened in RDONLY or WRONLY mode. 15 | * The other option which can be bitwise-or'ed when opening is O_NONBLOCK. 16 | * 17 | * If you set O_NONBLOCK (assuming you start at 0), then open calls 18 | * will execute and the program will continue on its way until the first 19 | * read... 20 | * If you don't set O_NONBLOCK, then the open call will block until a matching 21 | * open happens: a writer will block on open until someone also opens in 22 | * read mode, and a reader will block on open until someone also opens in 23 | * write mode. 24 | * 25 | * This program demos just the opening logic by letting you pass the flags 26 | * in as strings from the command line. To try it out, try opening with 27 | * combinations of O_RDONLY, O_WRONLY, O_NONBLOCK in two different terminals, 28 | * and see when a process waits at open or just runs to completion. 29 | * ... interestingly, you can have multiple readers and writers, it won't 30 | * raise errors. Not sure what the behavior would be when you started 31 | * passing data around. Probably multiple writers is simple, it's just 32 | * a byte queue, whereas multiple readers might cause issues unless 33 | * you read in well-controlled meaningful chunk sizes. 34 | * 35 | * We demo actually reading/writing data later. 36 | */ 37 | 38 | #define FIFO_NAME "/tmp/my_fifo" 39 | 40 | int main(int argc, char *argv[]) { 41 | int res; 42 | int open_mode = 0; 43 | int i; 44 | 45 | // check the arg count 46 | if (argc < 2) { 47 | fprintf(stderr, "Usage: %s \n", *argv); 49 | exit(EXIT_FAILURE); 50 | } 51 | 52 | // parse the args and set flags based on them 53 | for(i = 1; i < argc; i++) { 54 | if (strncmp(*++argv, "O_RDONLY", 8) == 0) 55 | open_mode |= O_RDONLY; 56 | if (strncmp(*argv, "O_WRONLY", 8) == 0) 57 | open_mode |= O_WRONLY; 58 | if (strncmp(*argv, "O_NONBLOCK", 10) == 0) 59 | open_mode |= O_NONBLOCK; 60 | } 61 | 62 | // open the fifo if it doesn't exist. The mkfifo() function works mostly 63 | // like the command-line version, except you also pass the mode to it. 64 | if (access(FIFO_NAME, F_OK) == -1) { 65 | res = mkfifo(FIFO_NAME, 0777); 66 | if (res != 0) { 67 | fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME); 68 | exit(EXIT_FAILURE); 69 | } 70 | } 71 | 72 | // open the fifo with the flags we set 73 | printf("Process %d opening FIFO\n", getpid()); 74 | res = open(FIFO_NAME, open_mode); 75 | // say when we finished opening: if O_NONBLOCK isn't set this will wait 76 | // until there's a matching process on the other end of the pipe 77 | printf("Process %d result %d\n", getpid(), res); 78 | sleep(5); 79 | if (res != -1) close(res); 80 | printf("Process %d finished\n", getpid()); 81 | exit(EXIT_SUCCESS); 82 | } 83 | -------------------------------------------------------------------------------- /ch14_sem_shmem_mqs/shm/consumer_creator.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "common.h" 8 | 9 | /* This program creates a chunk of shared memory, of size determined by the 10 | * `shared_use_st` struct defined in "common.h". 11 | * 12 | * At the end of the program, in which it loops over reading the shared 13 | * memory, which is set in the producer.c program based on user input, it 14 | * deletes the shared memory in a shmctl call. */ 15 | 16 | int main() 17 | { 18 | int running = 1; 19 | void *shared_memory = (void *)0; 20 | struct shared_use_st *shared_stuff; 21 | int shmid; 22 | 23 | srand((unsigned int)getpid()); 24 | 25 | // create shared mem block. Handle errors 26 | shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT); 27 | 28 | if (shmid == -1) { 29 | fprintf(stderr, "shmget failed\n"); 30 | exit(EXIT_FAILURE); 31 | } 32 | 33 | // attach to the chared memory (the call to shmget does *not* do this! 34 | // you need to call shmat - and you should generally pass it a null pointer 35 | // as the second arg - in order to get access to the shared memory inside 36 | // the memory space of this process. 37 | shared_memory = shmat(shmid, (void *)0, 0); 38 | if (shared_memory == (void *)-1) { 39 | fprintf(stderr, "shmat failed\n"); 40 | exit(EXIT_FAILURE); 41 | } 42 | printf("Memory attached at %p\n", shared_memory); 43 | 44 | // loop with some sleeping, and assign the shared memory to the 45 | // shared_use_st struct `shared_stuff` in this process. (Note we are not 46 | // reading out the data to `shared_stuff` - that's what we would do with 47 | // files or pipes, but here we just directly assign the pointer!) 48 | // 49 | // Whenever it's byte `written_by_you` indicates that we should, we print 50 | // out its `some_text` C-string to stdout. 51 | // 52 | // we then sleep for a bit before setting `written_by_you` back to 0. 53 | // we are using this byte to communicate when the other process is allowed 54 | // to write. 55 | // 56 | // The behavior if they read while we are writing is undefined, but that's 57 | // okay... they'll just poll again if they see 1, or charge ahead if they 58 | // see 0, and neither causes a problem in this simple example. In more 59 | // complex cases we might need to use semaphores to control access. 60 | shared_stuff = (struct shared_use_st *)shared_memory; 61 | shared_stuff->written_by_you = 0; 62 | while(running) { 63 | if (shared_stuff->written_by_you) { 64 | printf("You wrote: %s", shared_stuff->some_text); 65 | sleep( rand() % 4 ); /* make the other process wait for us ! */ 66 | shared_stuff->written_by_you = 0; 67 | if (strncmp(shared_stuff->some_text, "end", 3) == 0) { 68 | running = 0; 69 | } 70 | } 71 | } 72 | 73 | // detach - this frees process-local resources 74 | if (shmdt(shared_memory) == -1) { 75 | fprintf(stderr, "shmdt failed\n"); 76 | exit(EXIT_FAILURE); 77 | } 78 | 79 | // as with semaphores, it's very important to delete the global shared 80 | // memory resource. Note that shmdt doesn't do that - it detatches, which 81 | // frees the process-specific resources, but doesn't delete the global 82 | // resource. 83 | if (shmctl(shmid, IPC_RMID, 0) == -1) { 84 | fprintf(stderr, "shmctl(IPC_RMID) failed\n"); 85 | exit(EXIT_FAILURE); 86 | } 87 | exit(EXIT_SUCCESS); 88 | } 89 | 90 | 91 | -------------------------------------------------------------------------------- /ch8_mysql/read_data.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "mysql.h" 5 | 6 | 7 | /* display a single row from a mysql result. Assumes the connection is 8 | * still fresh so that calling mysql_field_count works properly. */ 9 | void display_row(MYSQL *conn, MYSQL_ROW *row) { 10 | unsigned int field_count; 11 | unsigned int i; 12 | field_count = mysql_field_count(conn); 13 | i = 0; 14 | printf("\t"); 15 | for (i = 0; i < field_count; i++) { 16 | if ((*row)[i]) printf("%s ", (*row)[i]); 17 | else printf("null "); 18 | } 19 | printf("\n"); 20 | } 21 | 22 | 23 | /* display a one-line header for a query result */ 24 | void display_header(MYSQL_RES *res) { 25 | MYSQL_FIELD *field_ptr; 26 | printf(" Column details: \n\t"); 27 | while ((field_ptr = mysql_fetch_field(res)) != NULL) { 28 | printf("%s: ", field_ptr->name); 29 | // the type logic is kind of convoluted and inconsistent... 30 | if (IS_NUM(field_ptr->type)) { 31 | printf("Numeric, "); 32 | } else { 33 | switch (field_ptr->type) { 34 | case FIELD_TYPE_VAR_STRING: 35 | printf("VARCHAR, "); break; 36 | case FIELD_TYPE_LONG: 37 | printf("LONG, "); break; 38 | default: 39 | printf("unknown_type_oops, "); 40 | } 41 | } 42 | printf("maxwidth %ld", field_ptr->length); 43 | if (field_ptr->flags & AUTO_INCREMENT_FLAG) printf(", autoincr; "); 44 | else printf("; "); 45 | } 46 | printf("|\n"); 47 | } 48 | 49 | 50 | /* execute a query, and display it with a header line */ 51 | void execute_query_and_show(MYSQL *conn, char *query) { 52 | MYSQL_RES *res_ptr; 53 | MYSQL_ROW sqlrow; 54 | int res; 55 | 56 | // Do the query... 57 | // Use mysql_use_result, which fetches rows one at a time, rather than 58 | // mysql_store_result. You iterate over the rows in the same way. 59 | res = mysql_query(conn, query); 60 | if (res) { 61 | fprintf(stderr, "SELECT error %d: %s\n", 62 | mysql_errno(conn), mysql_error(conn)); 63 | } else { 64 | res_ptr = mysql_use_result(conn); 65 | if (!res_ptr) { 66 | fprintf(stderr, "retrieve error %d: %s\n", 67 | mysql_errno(conn), mysql_error(conn)); 68 | } else { 69 | display_header(res_ptr); 70 | while ((sqlrow = mysql_fetch_row(res_ptr))) { 71 | display_row(conn, &sqlrow); 72 | } 73 | } 74 | } 75 | } 76 | 77 | 78 | int main(int argc, char *argv[]) { 79 | MYSQL my_conn; 80 | int res; 81 | 82 | if (!mysql_init(&my_conn)) { 83 | fprintf(stderr, "mysql_init failed\n"); 84 | return EXIT_FAILURE; 85 | } 86 | 87 | if (mysql_real_connect( 88 | &my_conn, "localhost", "trox", "trox", "foo", 0, NULL, 0)) { 89 | printf("Connection success\n"); 90 | 91 | printf("All children:\n"); 92 | execute_query_and_show( 93 | &my_conn, 94 | "SELECT childno, fname, age FROM children" 95 | ); 96 | 97 | printf("Children 10 and up:\n"); 98 | execute_query_and_show( 99 | &my_conn, 100 | "SELECT childno, fname, age FROM children WHERE age >= 10" 101 | ); 102 | 103 | 104 | 105 | 106 | } else { 107 | fprintf(stderr, "Connection failed %d: %s\n", 108 | mysql_errno(&my_conn), mysql_error(&my_conn)); 109 | } 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /ch5_term/kbhit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* Create a tiny library that emulates MS DOS's kbhit (keyboard hit) function, 9 | * which lets you busy-loop waiting for keyboard input. This is not commonly 10 | * done in unix, so there's no built-in tool, but this micro-library can help 11 | * you port programs; more importantly it's a nice example of playing with 12 | * the terminal settings. 13 | * 14 | * The key thing here is that 15 | * 1. in the init_keyboard settings, we set c_cc so that we unblock waiting 16 | * for input on just 1 character with 0 delay, and set no ECHO, no 17 | * ICANON, no ISIG. 18 | * 2. inside kbhit() we change it to unblock on 0 characters, before calling 19 | * read [we call read on fd 0, which is stdin]. This allows read to 20 | * return without reading anything, if no keys are hit. 21 | * 3. We save the actual key hit, if any, in the global peek_character, which 22 | * can be accessed via readch() 23 | * 4. We provide a close_keyboard() method, which any program using this 24 | * mini-library should call, to reset the term settings. 25 | * 26 | * remember, static variables are file-visible only. I think data is actually 27 | * static by default so this is redundant, but it's not bad to be explicit. 28 | */ 29 | 30 | static int peek_character; 31 | static struct termios initial_settings, new_settings; 32 | 33 | /* Set up terminal settings: no ECHO, no ICANON, no ISIG, minimum 34 | * characters for read at 1, no delay for read. Save old settings. */ 35 | void init_keyboard() { 36 | tcgetattr(0, &initial_settings); 37 | new_settings = initial_settings; 38 | new_settings.c_lflag &= ~ICANON; 39 | new_settings.c_lflag &= ~ECHO; 40 | new_settings.c_lflag &= ~ISIG; 41 | new_settings.c_cc[VMIN] = 1; 42 | new_settings.c_cc[VTIME] = 0; 43 | tcsetattr(0, TCSANOW, &new_settings); 44 | } 45 | 46 | /* Reset to original settings. */ 47 | void close_keyboard() { 48 | tcsetattr(0, TCSANOW, &initial_settings); 49 | } 50 | 51 | /* return 1 if any keyboard character has been entered since the character 52 | * obtained by the most recent call to reachch(), otherwise return 0. */ 53 | int kbhit() { 54 | char ch; 55 | int nread; 56 | if (peek_character != -1) { 57 | return 1; 58 | } 59 | 60 | // set so that read() won't block at all. Remember, read returns the number 61 | // of bytes read, so it will return 0 if there was nothing to read. When 62 | // we finish reading, reset so it blocks for a single character. 63 | new_settings.c_cc[VMIN] = 0; 64 | tcsetattr(0, TCSANOW, &new_settings); 65 | nread = read(0, &ch, 1); 66 | new_settings.c_cc[VMIN] = 1; 67 | tcsetattr(0, TCSANOW, &new_settings); 68 | 69 | if (nread == 1) { 70 | peek_character = ch; 71 | return 1; 72 | } 73 | return 0; 74 | } 75 | 76 | 77 | 78 | /* Read a character. If we've already peeked at a character, then 79 | * return the peeked character. If not, read one from stdin. This call 80 | * will in fact block if no character was peeked. */ 81 | int readch() { 82 | char ch; 83 | if (peek_character != -1) { 84 | ch = peek_character; 85 | peek_character = -1; 86 | return ch; 87 | } 88 | read(0, &ch, 1); 89 | return ch; 90 | } 91 | 92 | /********** sample driver program *****/ 93 | 94 | int main() { 95 | int ch = 0; 96 | init_keyboard(); 97 | 98 | while (ch != 'q') { 99 | printf("looping\n"); 100 | sleep(1); 101 | if (kbhit()) { 102 | ch = readch(); 103 | printf("You hit '%c'\n", ch); 104 | } 105 | } 106 | close_keyboard(); 107 | return 0; 108 | } 109 | -------------------------------------------------------------------------------- /ch6_curses/multi_window_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* Simple program to demonstrate how windows work in the curses 6 | * environment. The program is just an animation of 1 second frames 7 | * that you can watch and read along with. */ 8 | 9 | int main() { 10 | WINDOW *new_window; 11 | WINDOW *popup_window; 12 | int x_loop, y_loop; 13 | char a_letter = 'a'; 14 | 15 | initscr(); 16 | 17 | move(5, 5); 18 | printw("%s", "Testing multiple windows"); 19 | refresh(); 20 | 21 | sleep(1); 22 | 23 | /* add the alphabet cycled across the whole screen */ 24 | for(y_loop = 0; y_loop < LINES - 1; y_loop++) { 25 | for(x_loop = 0; x_loop < COLS - 1; x_loop++) { 26 | // mvaddch is mv plus addch, adds a character at a location 27 | // mvwaddch is the same thing but takes a WINDOW; in this case 28 | // we are passing it stdscr, which is the same as mvaddch. 29 | mvwaddch(stdscr, y_loop, x_loop, a_letter); 30 | a_letter++; 31 | if(a_letter > 'z') a_letter = 'a'; 32 | } 33 | } 34 | refresh(); 35 | sleep(1); 36 | 37 | // newwin(nlines, ncols, top_lineidx, left_colidx) 38 | new_window = newwin(10, 20, 5, 5); 39 | // mvwprintw, like mvwaddch, is a move plus a printw, on a given WINDOW 40 | mvwprintw(new_window, 2, 2, "%s", "Hello world"); 41 | mvwprintw(new_window, 5, 2, "%s", "Note that very long lines will wrap!"); 42 | wrefresh(new_window); 43 | sleep(1); 44 | 45 | 46 | /* Change the background to be 0-9 cycled. When we refresh, the window 47 | * becomes obscured.... */ 48 | a_letter = '0'; 49 | for(y_loop = 0; y_loop < LINES - 1; y_loop++) { 50 | for(x_loop = 0; x_loop < COLS - 1; x_loop++) { 51 | mvwaddch(stdscr, y_loop, x_loop, a_letter); 52 | a_letter++; 53 | if(a_letter > '9') a_letter = '0'; 54 | } 55 | } 56 | refresh(); 57 | sleep(1); 58 | 59 | /* ...and re-refreshing the new window doesn't help because we haven't 60 | * changed the new window... */ 61 | wrefresh(new_window); 62 | sleep(1); 63 | 64 | /* ...but if we 'touch' the new window, we can make it jump to the top 65 | * again by refreshing */ 66 | touchwin(new_window); 67 | wrefresh(new_window); 68 | sleep(1); 69 | 70 | /* now let's make a popup window, which is just another window with a 71 | * box around it */ 72 | popup_window = newwin(10, 20, 8, 8); 73 | box(popup_window, '|', '-'); // the border is drawn *inside* the window 74 | // boundaries. 75 | mvwprintw(popup_window, 5, 2, "%s", "pop up window!"); 76 | wrefresh(popup_window); 77 | sleep(1); 78 | 79 | /* by touching and refreshing, we can bring new_window back to the fore. 80 | * note that where the new window has blanks, nothing is overwritten */ 81 | touchwin(new_window); 82 | wrefresh(new_window); 83 | sleep(1); 84 | 85 | /* clear the contents of new_window - make it blank again. Note that this 86 | * overwrites all remaining traces of popup_window inside the boundaries */ 87 | wclear(new_window); 88 | wrefresh(new_window); 89 | sleep(1); 90 | 91 | /* now delete new window - always remember to delete windows other than 92 | * stdscr, never delete stdscr except via endwin(). 93 | * Note that this has no effect on the display. */ 94 | delwin(new_window); 95 | sleep(1); 96 | 97 | /* bring popup window back to the fore */ 98 | touchwin(popup_window); 99 | wrefresh(popup_window); 100 | sleep(1); 101 | 102 | /* delete popup_window. Touch stdscr to bring it back to front. */ 103 | delwin(popup_window); 104 | touchwin(stdscr); 105 | refresh(); 106 | sleep(1); 107 | 108 | endwin(); 109 | exit(EXIT_SUCCESS); 110 | } 111 | -------------------------------------------------------------------------------- /ch7_data/fnctl_ex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | const char *TEST_FILE = "/tmp/test_lock"; 7 | 8 | /* File locking example 9 | * 10 | * The book authors present several more examples so that you can run tests 11 | * in several processes and see how stuff works. But given that I'm unlikely 12 | * to need a lot of file locking, I'm just doing this and writing some 13 | * detailed-ish notes on how it works. Feel free to reference the book for 14 | * the other two examples, which cover 15 | * - using F_GETLK when a program is holding a lock 16 | * - using F_SETLK in two processes (competing locks) 17 | */ 18 | 19 | int main() { 20 | int fdesc; 21 | int i_byte; 22 | char *byte = "A"; // I think this is just the authors being lazy... we 23 | // never use the second '\0' byte lol. Could have 24 | // just had char byte = 'A' and then used &byte in 25 | // the `write` calls. 26 | struct flock region_1; 27 | struct flock region_2; 28 | int res; 29 | 30 | // open file 31 | fdesc = open(TEST_FILE, O_RDWR | O_CREAT, 0666); 32 | if (!fdesc) { 33 | fprintf(stderr, "Unable to open %s for read/write\n", TEST_FILE); 34 | exit(EXIT_FAILURE); 35 | } 36 | 37 | // write some data to the file 38 | for (i_byte = 0; i_byte < 100; i_byte++) { 39 | write(fdesc, byte, 1); 40 | }; 41 | 42 | // set up a shared (read) lock on bytes 10 to 30. Any number of processes 43 | // may use shared locks, but requests for exclusive (write) locks on the 44 | // same block will fail if read locks are open. 45 | // 46 | // The parameter l_type can bye F_RDLCK, F_WRLCK (to get a shared or 47 | // exclusive lock, resp), or F_UNLOCK (to release a lock) 48 | // The l_whence parameter determines the semantic meaning of l_start. 49 | // The value here, SEEK_SET, is by far the most common... it 50 | // causes l_start to be distance from the beginning of the file. It's 51 | // possible to use SEEK_CUR or SEEK_END, in which case l_start is 52 | // relative to current position or end of file. 53 | region_1.l_type = F_RDLCK; 54 | region_1.l_whence = SEEK_SET; 55 | region_1.l_start = 10; 56 | region_1.l_len = 20; 57 | // a call to fnctl F_GETLK will just read any lock information on a file. 58 | // The usual case is to find out if a region is locked without trying to 59 | // lock it ourselves. The semantic way of using is is generally to set 60 | // the flock.l_pid to -1. Then, when we call F_GETLK, if any lock is 61 | // in place the l_pid entry of our `flock` struct will get set, which 62 | // tells us that there is a lock in place. 63 | // a call to fnctl F_SETLK 64 | // returns -1 if the lock failed. Importantly, fnctl 65 | // does *not* prevent you from reading or modifying data. It's up to your 66 | // programs to use locks properly. It *does* unlock for you whenever 67 | // you close a file descriptor or your program, however if you keep the 68 | // file descriptor open you need to unlock yourself. 69 | // a call to fnctl F_SETLKW is the same semantically, except that instead 70 | // of returning -1 when it cannot lock, it will block until it can, or 71 | // until a signal occurs. 72 | printf("Process %d locking file\n", getpid()); 73 | res = fcntl(fdesc, F_SETLK, ®ion_1); 74 | if (res == -1) fprintf(stderr, "Failed to lock region_1\n"); 75 | 76 | // set up an exclusive (write) lock on bytes 40 to 50. 77 | // any attempt to lock this region (read or write) in another 78 | // process will return -1 (if F_SETLK) or block (if F_SETLKW). 79 | region_2.l_type = F_WRLCK; 80 | region_2.l_whence = SEEK_SET; 81 | region_2.l_start = 40; 82 | region_2.l_len = 10; 83 | printf("Process %d locking file\n", getpid()); 84 | res = fcntl(fdesc, F_SETLK, ®ion_2); 85 | if (res == -1) fprintf(stderr, "Failed to lock region_2\n"); 86 | 87 | 88 | 89 | 90 | 91 | 92 | exit(EXIT_SUCCESS); 93 | } 94 | -------------------------------------------------------------------------------- /ch12_pthreads/attrs_ex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // just to be safe... most modern systems should be fine... 7 | #ifndef _POSIX_THREAD_PRIORITY_SCHEDULING 8 | #error "Sorry, your system does not support thread priority scheduling" 9 | #endif 10 | 11 | void *thread_function(void *arg); 12 | 13 | char message[] = "Hello World"; 14 | int thread_finished = 0; 15 | 16 | /* Simple demo program of setting thread attributes, which all have similar 17 | * apis. The two we set are: 18 | * 19 | * - detached: note that we don't join. The only way we know the child 20 | * thread died is via a global variable, which we wouldn't need if we 21 | * weren't doing a demo. A detached thread is a 'fire-and-forget' thread. 22 | * 23 | * - set priority: first we have to set the schedpolicy, and then we 24 | * can setschedparam with a thread priority. Here we set the child thread 25 | * to have minimum priority. 26 | * 27 | * There's also an api to alter thread attributes on an already created thread, 28 | * but here we just set them in the pthread_create function. 29 | */ 30 | 31 | int main() { 32 | int res; 33 | pthread_t a_thread; 34 | void *thread_result; 35 | pthread_attr_t thread_attr; 36 | int max_priority, min_priority; 37 | struct sched_param scheduling_value; 38 | 39 | // initialize the thread attributes struct 40 | res = pthread_attr_init(&thread_attr); 41 | if (res != 0) { 42 | perror("Attribute creation failed"); 43 | exit(EXIT_FAILURE); 44 | } 45 | 46 | // set the flag that makes the thread detached. 47 | res = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); 48 | if (res != 0) { 49 | perror("Setting detached attribute failed"); 50 | exit(EXIT_FAILURE); 51 | } 52 | 53 | // set the scheduling policy... this doesn't actually change the priority, 54 | // but it sets the thread_attr to a state where priority can be set. 55 | // (this isn't a good in-depth look, just an api demo, see docs for info) 56 | res = pthread_attr_setschedpolicy(&thread_attr, SCHED_OTHER); 57 | if (res != 0) { 58 | perror("Setting schedpolicy failed"); 59 | exit(EXIT_FAILURE); 60 | } 61 | 62 | // query the system for the max and min piorioty (the max is only so we 63 | // can print interesting info, it isn't needed), and set the attributes so 64 | // that the thread gets min priority. 65 | max_priority = sched_get_priority_max(SCHED_OTHER); 66 | min_priority = sched_get_priority_min(SCHED_OTHER); 67 | scheduling_value.sched_priority = min_priority; 68 | res = pthread_attr_setschedparam(&thread_attr, &scheduling_value); 69 | if (res != 0) { 70 | perror("Setting schedpolicy failed"); 71 | exit(EXIT_FAILURE); 72 | } 73 | printf("Scheduling priority set to %d, max allowed was %d\n", 74 | min_priority, max_priority); 75 | 76 | // now that we've set all the attributes, we can creat the thread! 77 | res = pthread_create(&a_thread, &thread_attr, thread_function, 78 | (void *)message); 79 | if (res != 0) { 80 | perror("Thread creation failed"); 81 | exit(EXIT_FAILURE); 82 | } 83 | 84 | // we don't need the attributes anymore, clean them up. Note that we can 85 | // do this as soon as the thread is created, we don't have to wait for it 86 | // to finish. 87 | (void)pthread_attr_destroy(&thread_attr); 88 | 89 | // this isn't needed in principle, but for the sake of a good demo we look 90 | // at a global variable to find out when the detached thread ended. 91 | // ... note all the sleeps; access is racey without locking, but the 92 | // sleeps work well enough for a simple demo. 93 | while(!thread_finished) { 94 | printf("Waiting for thread...\n"); 95 | sleep(1); 96 | } 97 | 98 | printf("Thread finished, bye!\n"); 99 | exit(EXIT_SUCCESS); 100 | } 101 | 102 | /* function to run in our fire-and-forget thread. 103 | * For the sake of the demo, we use thread_finished, a global var, to tell 104 | * the main thread we are done. */ 105 | void *thread_function(void *arg) { 106 | printf("thread_function is running. Argument was %s\n", (char *)arg); 107 | sleep(2); 108 | thread_finished = 1; 109 | pthread_exit(NULL); 110 | } 111 | -------------------------------------------------------------------------------- /ch12_pthreads/mutex_ex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void *thread_function(void *arg); 8 | pthread_mutex_t work_mutex; // somewhat misnamed, protect both work_area and 9 | int time_to_exit = 0; // time_to_exit 10 | 11 | #define WORK_SIZE 1024 12 | char work_area[WORK_SIZE]; 13 | 14 | /* A mostly direct translation of the semaphore example (other than we also 15 | * fix the bug related to the main thread not waiting) into mutex locking. 16 | * 17 | * We add a global variable to handle exit, and move the fgets around 18 | * a bit so that we can properly lock and such. 19 | * 20 | * Every lock call blocks unless / until the lock is free. 21 | */ 22 | 23 | int main() { 24 | int res; 25 | pthread_t a_thread; 26 | void *thread_result; 27 | 28 | // init the mutex 29 | res = pthread_mutex_init(&work_mutex, NULL); 30 | if (res != 0) { 31 | perror("Mutex initialization failed"); 32 | exit(EXIT_FAILURE); 33 | } 34 | 35 | // create the thread 36 | res = pthread_create(&a_thread, NULL, thread_function, NULL); 37 | if (res != 0) { 38 | perror("Thread creation failed"); 39 | exit(EXIT_FAILURE); 40 | } 41 | 42 | // loop and read input. We get the lock first here b/c of the sleep at the 43 | // top of thread_function... we always own the lock when we hit the top 44 | // of the while loop, which is important. 45 | pthread_mutex_lock(&work_mutex); 46 | printf("Input some text. Enter 'end' to finish\n"); 47 | while(!time_to_exit) { 48 | fgets(work_area, WORK_SIZE, stdin); 49 | pthread_mutex_unlock(&work_mutex); 50 | while(1) { 51 | // this loop checks for whether thread_function has read the 52 | // data yet. When it has, we go to the top of the outer loop and 53 | // read the next line. 54 | pthread_mutex_lock(&work_mutex); 55 | if (work_area[0] != '\0') { 56 | pthread_mutex_unlock(&work_mutex); 57 | sleep(1); 58 | } 59 | else { 60 | break; 61 | } 62 | } 63 | } 64 | 65 | // wait for the thread.. I think that under normal circumstances, we 66 | // actually don't wait, b/c the thread already released its lock and 67 | // exited by the last time we acquired the lock. But I'm not sure. In 68 | // general you want to unlock and then join to avoid deadlocks. 69 | pthread_mutex_unlock(&work_mutex); 70 | printf("\nWaiting for thread to finish...\n"); 71 | res = pthread_join(a_thread, &thread_result); 72 | if (res != 0) { 73 | perror("Thread join failed"); 74 | exit(EXIT_FAILURE); 75 | } 76 | printf("Thread joined\n"); 77 | 78 | // cleanup. Note that pthread_join took care of the thread cleanup. 79 | pthread_mutex_destroy(&work_mutex); 80 | exit(EXIT_SUCCESS); 81 | } 82 | 83 | 84 | /* Demo thread function. It sleeps first (which ensures that the main thread 85 | * acquires the first lock), and from then on every time the mutex lock is 86 | * available, it will look at the current value of work_area, count the number 87 | * of characters, reset work_area[0] to '\0' (which is how the main thread 88 | * knows it's time to read more input), and unlock. 89 | * 90 | * After all but the first lock, it also checks that work_area[0] isn't 91 | * already '\0', which is necessary b/c we might still be waiting for input, 92 | * and the main thread unlocks while it's waiting for input. 93 | */ 94 | void *thread_function(void *arg) { 95 | sleep(1); 96 | pthread_mutex_lock(&work_mutex); 97 | while(strncmp("end", work_area, 3) != 0) { 98 | printf("You input %d characters\n", (int) strlen(work_area) -1); 99 | work_area[0] = '\0'; 100 | pthread_mutex_unlock(&work_mutex); 101 | sleep(1); 102 | // until the main thread resets work_area, just keep waiting. 103 | pthread_mutex_lock(&work_mutex); 104 | while (work_area[0] == '\0' ) { 105 | pthread_mutex_unlock(&work_mutex); 106 | sleep(1); 107 | pthread_mutex_lock(&work_mutex); 108 | } 109 | // note that at the top of the loop, the lock is always *on*. 110 | } 111 | // once we see "end", set time_to_exit (note that the lock is *on* here!) 112 | // so the main thread knows we can quit. 113 | time_to_exit = 1; 114 | work_area[0] = '\0'; 115 | pthread_mutex_unlock(&work_mutex); 116 | pthread_exit(0); 117 | } 118 | -------------------------------------------------------------------------------- /ch12_pthreads/semaphore_ex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void *thread_function(void *arg); 9 | sem_t bin_sem; 10 | 11 | #define WORK_SIZE 1024 12 | char work_area[WORK_SIZE]; 13 | 14 | /* Demonstration of basic semaphore functionality, including a prgramming 15 | * error that's easy to make where only one thread blocks (enter FAST at the 16 | * console to see it). 17 | * 18 | * This could be fixed by adding another semaphore, or by waiting and 19 | * posting in both threads. But we'll see a more standard solution - mutexes - 20 | * in another example. 21 | */ 22 | 23 | int main() { 24 | int res; 25 | pthread_t a_thread; 26 | void *thread_result; 27 | 28 | // init the semaphore. 29 | // 30 | // the second input is always 0, which says use this for communicating 31 | // between threads within a process (1 is supposed to allow between- 32 | // process semaphores, but isn't supported for this semaphore library 33 | // on most systems; it will crash. 34 | // 35 | // The third input is the initial value of the underlying int. In general 36 | // a semaphore can handle incr / decr operations on an arbitrary 37 | // nonnegative int, but for most purposes - and all examples we will 38 | // go through - the semaphore is binary, and functions as a lock for 39 | // operations that aren't thread-safe. 40 | res = sem_init(&bin_sem, 0, 0); 41 | if (res != 0) { 42 | perror("Semaphore initialization failed"); 43 | exit(EXIT_FAILURE); 44 | } 45 | 46 | // create. See simple_example.c for discusion 47 | res = pthread_create(&a_thread, NULL, thread_function, NULL); 48 | if (res != 0) { 49 | perror("Thread creation failed"); 50 | exit(EXIT_FAILURE); 51 | } 52 | 53 | // the main thread gets text and puts it in `work_area` 54 | // this operation is not thread safe, so it's controlled via a semaphore 55 | // 56 | // what actually happens? Well, sem_post atomically increases the semaphore 57 | // value. Any sem_wait calls will block if the value is 0, and if/when the 58 | // value is nonzero they decrement the semaphore. As a result, calling 59 | // sem_post will cause the sem_wait to unblock, and also decrement the 60 | // semaphore. 61 | // 62 | // the 'normal' behavior is when you don't enter "FAST". Note that this 63 | // thread never actually waits... the only reason it's "safe" is because 64 | // with console input, we can assume input time is sufficient for the 65 | // thread to finish processing before the next call to fgets unblocks. 66 | // 67 | // but by inputting "FAST", you can see that the write is not actually 68 | // protected... it says there are 8 charaters 3 times, and never shows 4 at 69 | // all! 70 | // 71 | // Why? Well, the initial fgets sets work_area to FAST, and then we 72 | // incr the semaphore with sem_post. But before the thread can respond, 73 | // this thread comes back, and instead of pausing at fgets again, it 74 | // goes into the `if` block, where it posts and extra time, then copies 75 | // "Wheeee..." to the work_area, and then calls sem_post yet again. 76 | // 77 | // By the time the first thread blocks and the second thread starts 78 | // (well, depending on your computer - it's possible but unlikely the 79 | // second thread starts earlier) you've already rewritten work_area, and 80 | // incremented the semaphore 3 times. Hence the thread reads prints 3 81 | // different messages and decrements the semaphore for each one. 82 | printf("Input some text. Enter 'end' to finish, FAST to demo a 'bug'\n"); 83 | while(strncmp("end", work_area, 3) != 0) { 84 | if (strncmp(work_area, "FAST", 4) == 0) { 85 | sem_post(&bin_sem); 86 | strcpy(work_area, "Wheeee..."); 87 | } else { 88 | fgets(work_area, WORK_SIZE, stdin); 89 | } 90 | sem_post(&bin_sem); 91 | } 92 | 93 | // cleanup. Use sem_destroy to free semaphore resources. 94 | sem_destroy(&bin_sem); 95 | exit(EXIT_SUCCESS); 96 | } 97 | 98 | /* Function to run in thread. Ignores argument and returns null. 99 | * 100 | * Waits until the semaphore unlocks this thread, and then it prints 101 | * out the length of the `work_area` string. If it sees "end" as the 102 | * `work_area` string, it stops. */ 103 | void *thread_function(void *arg) { 104 | sem_wait(&bin_sem); // this block until the semaphore is nonzero 105 | while(strncmp("end", work_area, 3) != 0) { 106 | printf("You input %d characters\n", (int) strlen(work_area) - 1); 107 | sem_wait(&bin_sem); 108 | } 109 | pthread_exit(NULL); 110 | } 111 | -------------------------------------------------------------------------------- /ch5_term/magical_menu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* Here we make our last changes to the menu program, by adding term 9 | * control functions. 10 | * 11 | * NOTE: This one needs to be compiled with the -lcurses flag. 12 | * 13 | * The details are a bit hard to follow and probably not worth memorizing, 14 | * see the docs at: 15 | * http://linux.die.net/man/3/tparm 16 | */ 17 | 18 | char *menu[] = { 19 | "a - add new record", 20 | "d - delete record", 21 | "q - quit", 22 | NULL, 23 | }; 24 | 25 | // we have to have a global output so that `char_to_terminal` can use it 26 | // in closure. Why? Because tputs takes a function pointer that accepts a 27 | // char. (This makes you appreciate other languages more... a global var is 28 | // not a nice hack). 29 | FILE * global_output; 30 | 31 | int getchoice(char * greet, char * choices[], FILE * input, FILE * output); 32 | 33 | int char_to_terminal(int char_to_write); 34 | 35 | int main() { 36 | 37 | FILE * input, * output; 38 | struct termios initialsettings, newsettings; 39 | tcgetattr(fileno(stdin), &initialsettings); 40 | newsettings = initialsettings; 41 | newsettings.c_lflag &= ~ICANON; 42 | newsettings.c_lflag &= ~ISIG; 43 | newsettings.c_lflag &= ~ECHO; 44 | 45 | 46 | if (!isatty(fileno(stdout))) { 47 | fprintf(stderr, "You are not at a terminal. That's okay."); 48 | } 49 | 50 | input = fopen("/dev/tty", "r"); 51 | output = fopen("/dev/tty", "w"); 52 | if (!input || !output) { 53 | fprintf(stderr, "Unable to open /dev/tty\n"); 54 | exit(1); 55 | } 56 | if (tcsetattr(fileno(input), TCSANOW, &newsettings) != 0) { 57 | fprintf(stderr, "Could not set tc attributes!\n"); 58 | } 59 | 60 | int choice = 0; 61 | do { 62 | choice = getchoice("Please select an action", menu, input, output); 63 | printf("You have chosen: %c\n", choice); 64 | 65 | // if you leave this off, you'll never actually see the choice! 66 | sleep(2); 67 | 68 | } while (choice != 'q'); 69 | 70 | // always reset tc attributes! 71 | tcsetattr(fileno(input), TCSANOW, &initialsettings); 72 | return 0; 73 | } 74 | 75 | /* Display a menu with a heading line that says "Choice: ", and 76 | * wait for the user to enter a valid choice. 77 | * 78 | * input and output are FILE* here. 79 | */ 80 | int getchoice(char * greet, char * choices[], FILE * input, FILE * output) { 81 | int selected; 82 | int chosen = 0; // bool 83 | int screenrow, screencol = 10; 84 | char *cursor, *clear; 85 | char **option; 86 | 87 | global_output = output; 88 | 89 | /* we should really be checking error conditions here */ 90 | setupterm(NULL, fileno(output), (int *)0); 91 | 92 | // here we are asking the terminfo code to give us the strings we can 93 | // use to control terminal behavior. 94 | cursor = tigetstr("cup"); // cursor position 95 | clear = tigetstr("clear"); // 96 | 97 | screenrow = 4; 98 | // always use tputs to put or putp to put terminal control strings. 99 | // the first entry is the string, the second is the number of lines 100 | // which should be affected, and the third is a callback to a function 101 | // which actually adds the characters. 102 | tputs(clear, 1, char_to_terminal); 103 | // the tparm call parameterizes the `cursor` string, which is sort of 104 | // a template, with the proper details to move the cursor. 105 | tputs(tparm(cursor, screenrow, screencol), 1, char_to_terminal); 106 | 107 | fprintf(output, "Choice: %s\n", greet); 108 | screenrow += 2; 109 | option = choices; 110 | while (*option) { 111 | tputs(tparm(cursor, screenrow, screencol), 1, char_to_terminal); 112 | fprintf(output, "%s\n", *option); 113 | screenrow++; 114 | option++; 115 | } 116 | fprintf(output, "\n"); 117 | 118 | do { 119 | fflush(output); 120 | selected = fgetc(input); 121 | option = choices; 122 | while (*option) { 123 | if (selected == *option[0]) { 124 | chosen = 1; // true 125 | break; 126 | } 127 | option++; 128 | } 129 | if (!chosen) { 130 | tputs(tparm(cursor, screenrow, screencol), 1, char_to_terminal); 131 | fprintf(output, "Unrecognized choice, select again.\n"); 132 | } 133 | } while (!chosen); 134 | 135 | tputs(clear, 1, char_to_terminal); 136 | return selected; 137 | } 138 | 139 | /* This puts a character to `global output`. It's used as a callback to 140 | * `tputs`. */ 141 | int char_to_terminal(int char_to_write) { 142 | if (global_output) putc(char_to_write, global_output); 143 | return 0; 144 | } 145 | -------------------------------------------------------------------------------- /ch8_mysql/cd_prog_ch8/sample_data.sql: -------------------------------------------------------------------------------- 1 | -- clear tables (actually schema.sql drops them and re-adds them anyway) 2 | delete from track; 3 | delete from cd; 4 | delete from artist; 5 | 6 | insert into artist(id, name) values(1, 'Pink Floyd'); 7 | insert into artist(id, name) values(2, 'Genesis'); 8 | insert into artist(id, name) values(3, 'Einaudi'); 9 | insert into artist(id, name) values(4, 'Melanie C.') 10 | 11 | insert into cd(id, title, artist_id, catalogue) 12 | values 13 | (1, 'Dark Side of the Moon', 1, 'B000024D4P'), 14 | (2, 'Wish You Were Here', 1, 'B000024D4P'), 15 | (3, 'A trick of the Tail', 2, 'B000024D4P'), 16 | (4, 'Selling England By the Pound', 2, 'B000024D4P'), 17 | (5, 'I Giorni', 3, 'B000024D4P'), 18 | (6, 'Northern Star', 4, 'B000024D4P'); 19 | 20 | insert into track(cd_id, track_id, title) values(1, 1, 'Speak to me'); 21 | insert into track(cd_id, track_id, title) values(1, 2, 'Breathe'); 22 | insert into track(cd_id, track_id, title) values(1, 3, 'On the run'); 23 | insert into track(cd_id, track_id, title) values(1, 4, 'Time'); 24 | insert into track(cd_id, track_id, title) values(1, 5, 'Great gig in the sky'); 25 | insert into track(cd_id, track_id, title) values(1, 6, 'Money'); 26 | insert into track(cd_id, track_id, title) values(1, 7, 'Us and them'); 27 | insert into track(cd_id, track_id, title) values(1, 8, 'Any colour you like'); 28 | insert into track(cd_id, track_id, title) values(1, 9, 'Brain damage'); 29 | insert into track(cd_id, track_id, title) values(1, 10, 'Eclipse '); 30 | 31 | insert into track(cd_id, track_id, title) values(2, 1, 'Shine on you crazy diamond'); 32 | insert into track(cd_id, track_id, title) values(2, 2, 'Welcome to the machine'); 33 | insert into track(cd_id, track_id, title) values(2, 3, 'Have a cigar'); 34 | insert into track(cd_id, track_id, title) values(2, 4, 'Wish you were here'); 35 | insert into track(cd_id, track_id, title) values(2, 5, 'Shine on you crazy diamond pt.2'); 36 | 37 | insert into track(cd_id, track_id, title) values(3, 1, 'Dance on a volcano'); 38 | insert into track(cd_id, track_id, title) values(3, 2, 'Entangled'); 39 | insert into track(cd_id, track_id, title) values(3, 3, 'Squonk'); 40 | insert into track(cd_id, track_id, title) values(3, 4, 'Madman moon'); 41 | insert into track(cd_id, track_id, title) values(3, 5, 'Robbery assault and battery'); 42 | insert into track(cd_id, track_id, title) values(3, 6, 'Ripples'); 43 | insert into track(cd_id, track_id, title) values(3, 7, 'Trick of the tail'); 44 | insert into track(cd_id, track_id, title) values(3, 8, 'Los Endos'); 45 | 46 | insert into track(cd_id, track_id, title) values(4, 1, 'Dancing with the moonlit knight'); 47 | insert into track(cd_id, track_id, title) values(4, 2, 'I know what I like'); 48 | insert into track(cd_id, track_id, title) values(4, 3, 'Firth of fifth'); 49 | insert into track(cd_id, track_id, title) values(4, 4, 'More fool me'); 50 | insert into track(cd_id, track_id, title) values(4, 5, 'Battle of Epping Forest'); 51 | insert into track(cd_id, track_id, title) values(4, 6, 'After the ordeal'); 52 | insert into track(cd_id, track_id, title) values(4, 7, 'Cinema show'); 53 | insert into track(cd_id, track_id, title) values(4, 8, 'Aisle of plenty '); 54 | 55 | insert into track(cd_id, track_id, title) values(5, 1, 'Melodia Africana (part 1)'); 56 | insert into track(cd_id, track_id, title) values(5, 2, 'I due fiumi'); 57 | insert into track(cd_id, track_id, title) values(5, 3, 'In un altra vita'); 58 | insert into track(cd_id, track_id, title) values(5, 4, 'Melodia Africana (part 2)'); 59 | insert into track(cd_id, track_id, title) values(5, 5, 'Stella del mattino'); 60 | insert into track(cd_id, track_id, title) values(5, 6, 'I giorni'); 61 | insert into track(cd_id, track_id, title) values(5, 7, 'Samba'); 62 | insert into track(cd_id, track_id, title) values(5, 8, 'Melodia Africana (part 3)'); 63 | insert into track(cd_id, track_id, title) values(5, 9, 'La nascita delle cose segrete'); 64 | insert into track(cd_id, track_id, title) values(5, 10, 'Quel che resta'); 65 | insert into track(cd_id, track_id, title) values(5, 11, 'Inizio'); 66 | insert into track(cd_id, track_id, title) values(5, 12, 'Limbo'); 67 | insert into track(cd_id, track_id, title) values(5, 13, 'Bella notte'); 68 | insert into track(cd_id, track_id, title) values(5, 14, 'Canzone Africana (part 4) '); 69 | 70 | insert into track(cd_id, track_id, title) values(6, 1, 'Go!'); 71 | insert into track(cd_id, track_id, title) values(6, 2, 'Northern Star'); 72 | insert into track(cd_id, track_id, title) values(6, 3, 'Going Down'); 73 | insert into track(cd_id, track_id, title) values(6, 4, 'I Turn To You'); 74 | insert into track(cd_id, track_id, title) values(6, 5, 'If That Were Me'); 75 | insert into track(cd_id, track_id, title) values(6, 6, 'Never Be The Same Again'); 76 | insert into track(cd_id, track_id, title) values(6, 7, 'Why'); 77 | insert into track(cd_id, track_id, title) values(6, 8, 'Suddenly Monday'); 78 | insert into track(cd_id, track_id, title) values(6, 9, 'Ga Ga'); 79 | insert into track(cd_id, track_id, title) values(6, 10, 'Be The One'); 80 | insert into track(cd_id, track_id, title) values(6, 11, 'Closer'); 81 | insert into track(cd_id, track_id, title) values(6, 12, 'Feel The Sun'); 82 | -------------------------------------------------------------------------------- /ch15_sockets/select_inet_ex/server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | /* Example of a server over an inet socket which uses select() to wait for 12 | * contact on multiple sockets in a single thread. 13 | * 14 | * The approach we are using - using an fd_set struct - allows waiting for 15 | * the first contact on a variety of file descriptors, and would work for 16 | * listening on multiple input devices of any type (not just sockets). 17 | * 18 | * None of the socket code here is new, so our comments focus on the 19 | * fd_set api. 20 | */ 21 | 22 | int main() { 23 | int server_sockfd, client_sockfd; 24 | int server_len, client_len; 25 | struct sockaddr_in server_address; 26 | struct sockaddr_in client_address; 27 | 28 | // I actually haven't found a satisfactory explanation of why we need 29 | // testfds - which we always set by value to be equal to readfds prior 30 | // to calling `select` - but it seems to be a part of using the api. 31 | fd_set readfds, testfds; 32 | 33 | // handle setting up the server socket fd. Nothing new here. 34 | server_sockfd = socket(AF_INET, SOCK_STREAM, 0); 35 | server_address.sin_family = AF_INET; 36 | server_address.sin_addr.s_addr = inet_addr("127.0.0.1"); 37 | server_address.sin_port = htons(9734); 38 | server_len = sizeof(server_address); 39 | bind(server_sockfd, (struct sockaddr *)&server_address, server_len); 40 | listen(server_sockfd, 5); 41 | client_len = sizeof(client_address); 42 | char ch; 43 | 44 | // initialize the readfds fd_set to be empty 45 | FD_ZERO(&readfds); 46 | // then, add the server socket to readfds 47 | // we'll add client fds as they form in the loop below 48 | FD_SET(server_sockfd, &readfds); 49 | 50 | int result; 51 | int fd; 52 | int nread; 53 | while(1) { 54 | 55 | printf("server waiting\n"); 56 | testfds = readfds; 57 | 58 | // this returns -1 if any of the fds experiences an error, otherwise 59 | // it returns the number of fds that have experienced a result. 60 | // 61 | // .. the last input is a timeout. Setting NULL means no timeout. 62 | // the other two fd_sets are to check for writeablility or errors. 63 | // If we had set a timeout, we would need to check whether `result` is 64 | // 0, which would mean we timed out without any data to read. 65 | // 66 | // I don't fully undertand this api, but the most common use case 67 | // for network programming would be to check for reading, as we do 68 | // here. 69 | result = select(FD_SETSIZE, &testfds, (fd_set *)0, (fd_set *)0, 70 | (struct timeval *)0); 71 | if (result < 1) { 72 | perror("select server"); 73 | exit(1); 74 | } 75 | 76 | for (fd = 0; fd < FD_SETSIZE; fd++) { 77 | // FD_ISSET tells us whether a particular fd is 78 | // (a) part of an fd_set, and (b) had seen 'action' as of the last 79 | // select call. 80 | if (FD_ISSET(fd, &testfds)) { 81 | // handle server socket action - accept new connections 82 | if (fd == server_sockfd) { 83 | client_sockfd = accept(server_sockfd, 84 | (struct sockaddr *)&client_address, 85 | &client_len); 86 | FD_SET(client_sockfd, &readfds); 87 | printf("Adding client on fd %d\n", client_sockfd); 88 | } else { 89 | // this call sets nread to be the number of bytes in the 90 | // socket's buffer (it also returns an error code, 0 for 91 | // success, which we are ignoring here). It's basiclally 92 | // a way of 'peeking' to find out how big a buffer we need 93 | // to read. 94 | // 95 | // If we weren't hard-coding a limit on message size, we 96 | // could then read the whole buffer by mallocing something 97 | // big enough to hold everything. 98 | ioctl(fd, FIONREAD, &nread); 99 | 100 | if (nread == 0) { 101 | // there's no data 102 | // this can only happen when FD_ISSET returns 1 if the 103 | // socket closed. So, we clean up 104 | close(fd); 105 | FD_CLR(fd, &readfds); 106 | printf("Removing client on fd %d\n", fd); 107 | } else { 108 | // there's data! read it, and reply. 109 | read(fd, &ch, 1); 110 | ch++; 111 | printf("Serving client on fd %d\n", fd); 112 | write(fd, &ch, 1); 113 | } 114 | } 115 | } 116 | } 117 | } 118 | } 119 | --------------------------------------------------------------------------------