├── dlsym ├── Makefile ├── dlsym.c └── subject.c ├── epoll ├── Makefile ├── README.md ├── epoll.c └── keys.c ├── eventfd ├── Makefile ├── README.md ├── event1.c ├── event2.c ├── event3.c └── event4.c ├── inotify ├── Makefile ├── dirwatch.c ├── filewatch.c ├── mv_on_close.c └── signal_driven.c ├── pcre ├── Makefile ├── captures.c ├── multi.c └── simple.c ├── pipe ├── Makefile ├── README.md └── pipe1.c ├── posix-msg-queue ├── Makefile ├── README.md ├── mq-read.c ├── mq-unlink.c └── mq-write.c ├── posix-semaphore ├── Makefile ├── README.md ├── psem-create.c ├── psem-getvalue.c ├── psem-post.c ├── psem-unlink.c └── psem-wait.c ├── prctl ├── Makefile └── pr_set_name.c ├── readdir ├── Makefile ├── README.md ├── readdir-lstat.c ├── readdir-suffix.c └── readdir.c ├── readfile ├── Makefile ├── README.md ├── hexdump.c ├── mmap.c ├── slurp.c └── special.c ├── readlink ├── Makefile ├── README.md └── rdlink.c ├── sqlite ├── Makefile ├── README.md ├── create.c ├── insert.c ├── replace.c ├── scan_files.c ├── scan_lines.c ├── select.c └── simple.c ├── template ├── Makefile ├── README.md └── template.c └── uuid ├── Makefile └── get_uuid.c /dlsym/Makefile: -------------------------------------------------------------------------------- 1 | LDFLAGS=-ldl 2 | all: subject.so dlsym 3 | 4 | subject.so: subject.c 5 | $(CC) -shared -o $@ $< 6 | 7 | dlsym: dlsym.o 8 | 9 | .PHONY: clean 10 | 11 | clean: 12 | rm -f subject.so dlsym *.o 13 | -------------------------------------------------------------------------------- /dlsym/dlsym.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* 6 | * test this program using: 7 | * 8 | * ./dlsym subject incr 9 | * ./dlsym subject decr 10 | */ 11 | 12 | int (*bfcn)(int); /* prototype of fcn to be called in lib */ 13 | 14 | int main(int argc, char *argv[]) { 15 | dlerror(); /* clear */ 16 | 17 | if (argc < 2) { 18 | fprintf(stderr,"usage: %s []\n", argv[0]); 19 | exit(-1); 20 | } 21 | char *lib = argv[1]; 22 | char *sym = (argc > 2) ? argv[2] : "incr"; 23 | 24 | void *handle = dlopen(lib, RTLD_LAZY); 25 | if (!handle) { 26 | fprintf(stderr, "dlopen failed: %s\n", dlerror()); 27 | exit(-1); 28 | } 29 | 30 | void *addr = dlsym(handle,sym); 31 | bfcn=addr; 32 | int rc=(*bfcn)(1); 33 | printf("rc is %d\n",rc); 34 | dlclose(handle); 35 | } 36 | -------------------------------------------------------------------------------- /dlsym/subject.c: -------------------------------------------------------------------------------- 1 | int incr(int i) {return i+1;} 2 | int decr(int i) {return i-1;} 3 | -------------------------------------------------------------------------------- /epoll/Makefile: -------------------------------------------------------------------------------- 1 | OBJS= epoll keys 2 | all: $(OBJS) 3 | 4 | clean: 5 | rm -f *.o $(OBJS) 6 | -------------------------------------------------------------------------------- /epoll/README.md: -------------------------------------------------------------------------------- 1 | This is a standard template for an epoll-based, signal handling, 2 | event driven C program. It is minimal- it only waits for a signal. 3 | Every few seconds it does background work. A real application 4 | would put some application logic in and handling of additional 5 | file descriptors. 6 | 7 | The second example keys.c shows using epoll with the same set of 8 | descriptors as well as reading from the keyboard, with key echo off. 9 | -------------------------------------------------------------------------------- /epoll/epoll.c: -------------------------------------------------------------------------------- 1 | /* 2 | * template for an epoll-based event driven program 3 | * signals are also handled via epoll using signalfd 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | struct { 16 | int verbose; 17 | char *prog; 18 | int signal_fd; 19 | int epoll_fd; 20 | int ticks; 21 | } cfg = { 22 | .signal_fd = -1, 23 | .epoll_fd = -1, 24 | }; 25 | 26 | void usage() { 27 | fprintf(stderr,"usage: %s [-v] \n", cfg.prog); 28 | exit(-1); 29 | } 30 | 31 | /* signals that we'll accept via signalfd in epoll */ 32 | int sigs[] = {SIGHUP,SIGTERM,SIGINT,SIGQUIT,SIGALRM}; 33 | 34 | void periodic_work() { 35 | fprintf(stderr,"periodic work...\n"); 36 | } 37 | 38 | int new_epoll(int events, int fd) { 39 | int rc; 40 | struct epoll_event ev; 41 | memset(&ev,0,sizeof(ev)); // placate valgrind 42 | ev.events = events; 43 | ev.data.fd= fd; 44 | if (cfg.verbose) fprintf(stderr,"adding fd %d to epoll\n", fd); 45 | rc = epoll_ctl(cfg.epoll_fd, EPOLL_CTL_ADD, fd, &ev); 46 | if (rc == -1) { 47 | fprintf(stderr,"epoll_ctl: %s\n", strerror(errno)); 48 | } 49 | return rc; 50 | } 51 | 52 | int mod_epoll(int events, int fd) { 53 | int rc; 54 | struct epoll_event ev; 55 | memset(&ev,0,sizeof(ev)); // placate valgrind 56 | ev.events = events; 57 | ev.data.fd= fd; 58 | if (cfg.verbose) fprintf(stderr,"modding fd %d epoll\n", fd); 59 | rc = epoll_ctl(cfg.epoll_fd, EPOLL_CTL_MOD, fd, &ev); 60 | if (rc == -1) { 61 | fprintf(stderr,"epoll_ctl: %s\n", strerror(errno)); 62 | } 63 | return rc; 64 | } 65 | 66 | int handle_signal() { 67 | int rc=-1; 68 | struct signalfd_siginfo info; 69 | 70 | if (read(cfg.signal_fd, &info, sizeof(info)) != sizeof(info)) { 71 | fprintf(stderr,"failed to read signal fd buffer\n"); 72 | goto done; 73 | } 74 | 75 | switch(info.ssi_signo) { 76 | case SIGALRM: 77 | if ((++cfg.ticks % 2) == 0) periodic_work(); 78 | alarm(1); 79 | break; 80 | default: 81 | fprintf(stderr,"got signal %d\n", info.ssi_signo); 82 | goto done; 83 | break; 84 | } 85 | 86 | rc = 0; 87 | 88 | done: 89 | return rc; 90 | } 91 | 92 | int main(int argc, char *argv[]) { 93 | int opt, n; 94 | cfg.prog = argv[0]; 95 | struct epoll_event ev; 96 | 97 | while ( (opt=getopt(argc,argv,"vh")) != -1) { 98 | switch(opt) { 99 | case 'v': cfg.verbose++; break; 100 | case 'h': default: usage(); break; 101 | } 102 | } 103 | 104 | /* block all signals. we take signals synchronously via signalfd */ 105 | sigset_t all; 106 | sigfillset(&all); 107 | sigprocmask(SIG_SETMASK,&all,NULL); 108 | 109 | /* a few signals we'll accept via our signalfd */ 110 | sigset_t sw; 111 | sigemptyset(&sw); 112 | for(n=0; n < sizeof(sigs)/sizeof(*sigs); n++) sigaddset(&sw, sigs[n]); 113 | 114 | /* create the signalfd for receiving signals */ 115 | cfg.signal_fd = signalfd(-1, &sw, 0); 116 | if (cfg.signal_fd == -1) { 117 | fprintf(stderr,"signalfd: %s\n", strerror(errno)); 118 | goto done; 119 | } 120 | 121 | /* set up the epoll instance */ 122 | cfg.epoll_fd = epoll_create(1); 123 | if (cfg.epoll_fd == -1) { 124 | fprintf(stderr,"epoll: %s\n", strerror(errno)); 125 | goto done; 126 | } 127 | 128 | /* add descriptors of interest */ 129 | if (new_epoll(EPOLLIN, cfg.signal_fd)) goto done; // signal socket 130 | 131 | fprintf(stderr,"starting... press ctrl-c to exit\n"); 132 | 133 | alarm(1); 134 | while (epoll_wait(cfg.epoll_fd, &ev, 1, -1) > 0) { 135 | if (cfg.verbose > 1) fprintf(stderr,"epoll reports fd %d\n", ev.data.fd); 136 | if (ev.data.fd == cfg.signal_fd) { if (handle_signal() < 0) goto done; } 137 | } 138 | 139 | done: 140 | if (cfg.epoll_fd != -1) close(cfg.epoll_fd); 141 | if (cfg.signal_fd != -1) close(cfg.signal_fd); 142 | return 0; 143 | } 144 | -------------------------------------------------------------------------------- /epoll/keys.c: -------------------------------------------------------------------------------- 1 | /* 2 | * another epoll based sample program 3 | * this one reads the keyboard and signalfd 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | struct { 17 | int verbose; 18 | char *prog; 19 | int signal_fd; 20 | int epoll_fd; 21 | int ticks; 22 | } cfg = { 23 | .signal_fd = -1, 24 | .epoll_fd = -1, 25 | }; 26 | 27 | void usage() { 28 | fprintf(stderr,"usage: %s [-v] \n", cfg.prog); 29 | exit(-1); 30 | } 31 | 32 | /* signals that we'll accept via signalfd in epoll */ 33 | int sigs[] = {SIGHUP,SIGTERM,SIGINT,SIGQUIT,SIGALRM}; 34 | 35 | void periodic_work() { 36 | fprintf(stderr,"periodic work...\n"); 37 | } 38 | 39 | int new_epoll(int events, int fd) { 40 | int rc; 41 | struct epoll_event ev; 42 | memset(&ev,0,sizeof(ev)); // placate valgrind 43 | ev.events = events; 44 | ev.data.fd= fd; 45 | if (cfg.verbose) fprintf(stderr,"adding fd %d to epoll\n", fd); 46 | rc = epoll_ctl(cfg.epoll_fd, EPOLL_CTL_ADD, fd, &ev); 47 | if (rc == -1) { 48 | fprintf(stderr,"epoll_ctl: %s\n", strerror(errno)); 49 | } 50 | return rc; 51 | } 52 | 53 | int mod_epoll(int events, int fd) { 54 | int rc; 55 | struct epoll_event ev; 56 | memset(&ev,0,sizeof(ev)); // placate valgrind 57 | ev.events = events; 58 | ev.data.fd= fd; 59 | if (cfg.verbose) fprintf(stderr,"modding fd %d epoll\n", fd); 60 | rc = epoll_ctl(cfg.epoll_fd, EPOLL_CTL_MOD, fd, &ev); 61 | if (rc == -1) { 62 | fprintf(stderr,"epoll_ctl: %s\n", strerror(errno)); 63 | } 64 | return rc; 65 | } 66 | 67 | int handle_signal() { 68 | int rc=-1; 69 | struct signalfd_siginfo info; 70 | 71 | if (read(cfg.signal_fd, &info, sizeof(info)) != sizeof(info)) { 72 | fprintf(stderr,"failed to read signal fd buffer\n"); 73 | goto done; 74 | } 75 | 76 | switch(info.ssi_signo) { 77 | case SIGALRM: 78 | if ((++cfg.ticks % 2) == 0) periodic_work(); 79 | alarm(1); 80 | break; 81 | default: 82 | fprintf(stderr,"got signal %d\n", info.ssi_signo); 83 | goto done; 84 | break; 85 | } 86 | 87 | rc = 0; 88 | 89 | done: 90 | return rc; 91 | } 92 | 93 | /* this toggles terminal settings so we read keystrokes on 94 | * stdin immediately (by negating canon and echo flags). 95 | * 96 | * Undo at exit, or user's terminal will appear dead!! 97 | */ 98 | int want_keys(int want_keystrokes) { 99 | int rc = -1; 100 | struct termios t; 101 | 102 | if (isatty(STDIN_FILENO) == 0) return 0; 103 | 104 | if (tcgetattr(STDIN_FILENO, &t) < 0) { 105 | fprintf(stderr,"tcgetattr: %s\n", strerror(errno)); 106 | goto done; 107 | } 108 | 109 | if (want_keystrokes) t.c_lflag &= ~(ICANON|ECHO); 110 | else t.c_lflag |= (ICANON|ECHO); 111 | 112 | if (tcsetattr(STDIN_FILENO, TCSANOW, &t) < 0) { 113 | fprintf(stderr,"tcsetattr: %s\n", strerror(errno)); 114 | goto done; 115 | } 116 | rc = 0; 117 | done: 118 | return rc; 119 | } 120 | 121 | int keyclicks=0; 122 | int handle_stdin(void) { 123 | int rc= -1, bc; 124 | char c; 125 | 126 | bc = read(STDIN_FILENO, &c, sizeof(c)); 127 | if (bc <= 0) goto done; 128 | 129 | if (c == 'q') goto done; /* quit */ 130 | else fprintf(stderr, "%u\n", ++keyclicks); 131 | rc = 0; 132 | 133 | done: 134 | return rc; 135 | } 136 | 137 | int main(int argc, char *argv[]) { 138 | int opt, n; 139 | cfg.prog = argv[0]; 140 | struct epoll_event ev; 141 | 142 | while ( (opt=getopt(argc,argv,"vh")) != -1) { 143 | switch(opt) { 144 | case 'v': cfg.verbose++; break; 145 | case 'h': default: usage(); break; 146 | } 147 | } 148 | 149 | /* block all signals. we take signals synchronously via signalfd */ 150 | sigset_t all; 151 | sigfillset(&all); 152 | sigprocmask(SIG_SETMASK,&all,NULL); 153 | 154 | /* a few signals we'll accept via our signalfd */ 155 | sigset_t sw; 156 | sigemptyset(&sw); 157 | for(n=0; n < sizeof(sigs)/sizeof(*sigs); n++) sigaddset(&sw, sigs[n]); 158 | 159 | /* create the signalfd for receiving signals */ 160 | cfg.signal_fd = signalfd(-1, &sw, 0); 161 | if (cfg.signal_fd == -1) { 162 | fprintf(stderr,"signalfd: %s\n", strerror(errno)); 163 | goto done; 164 | } 165 | 166 | /* unbuffer keypresses from terminal */ 167 | if (want_keys(1) < 0) goto done; 168 | 169 | /* set up the epoll instance */ 170 | cfg.epoll_fd = epoll_create(1); 171 | if (cfg.epoll_fd == -1) { 172 | fprintf(stderr,"epoll: %s\n", strerror(errno)); 173 | goto done; 174 | } 175 | 176 | /* add descriptors of interest */ 177 | if (new_epoll(EPOLLIN, cfg.signal_fd)) goto done; // signal socket 178 | if (new_epoll(EPOLLIN, STDIN_FILENO)) goto done; // keyboard 179 | 180 | fprintf(stderr,"starting... press keys. press q to exit\n"); 181 | 182 | alarm(1); 183 | while (epoll_wait(cfg.epoll_fd, &ev, 1, -1) > 0) { 184 | if (ev.data.fd == cfg.signal_fd) { if (handle_signal() < 0) goto done;} 185 | else if (ev.data.fd == STDIN_FILENO) { if (handle_stdin() < 0) goto done;} 186 | } 187 | 188 | done: 189 | if (cfg.epoll_fd != -1) close(cfg.epoll_fd); 190 | if (cfg.signal_fd != -1) close(cfg.signal_fd); 191 | want_keys(0); /* restore terminal */ 192 | return 0; 193 | } 194 | -------------------------------------------------------------------------------- /eventfd/Makefile: -------------------------------------------------------------------------------- 1 | PROGS=event1 event2 event3 event4 2 | OBJS=$(patsubst %,%.o,$(PROGS)) 3 | all: $(OBJS) $(PROGS) 4 | 5 | # static pattern rule: multiple targets 6 | 7 | $(OBJS): %.o: %.c 8 | $(CC) -c $(CFLAGS) $< 9 | 10 | $(PROGS): %: %.o 11 | $(CC) -o $@ $(CFLAGS) $< $(LDFLAGS) 12 | 13 | .PHONY: clean 14 | 15 | clean: 16 | rm -f $(OBJS) $(PROGS) 17 | -------------------------------------------------------------------------------- /eventfd/README.md: -------------------------------------------------------------------------------- 1 | Example of eventfd 2 | 3 | See eventfd(2). 4 | 5 | An eventfd file descriptor has an internal 8-byte counter whose value is set 6 | at creation and added to by write(). A read() blocks if the counter is zero. 7 | If the counter is non-zero, the read() behaves as such: 8 | 9 | * in regular mode, the read returns the counter value. Counter is reset to zero. 10 | * in semaphore mode, the read returns 1 (in 8 bytes). Internal counter decrements. 11 | 12 | The current value of the internal counter is exposed in /proc//fdinfo/. 13 | 14 | % cat /proc/6188/fdinfo/3 15 | pos: 0 16 | flags: 02 17 | mnt_id: 11 18 | eventfd-count: 64 19 | 20 | Above, the internal counter's current value is 0x64 (decimal 100). 21 | 22 | Examples 23 | 24 | * event1: simple use of eventfd between processes 25 | * event2: shows how multiple writes add together 26 | * event3: shows looking at the counter value in /proc 27 | * event4: semaphore mode, each read returns 1 til counter reaches 0 28 | -------------------------------------------------------------------------------- /eventfd/event1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char *argv[]) { 9 | unsigned int initval; 10 | int fd, flags; 11 | uint64_t u,v; 12 | ssize_t nr; 13 | pid_t pid; 14 | 15 | initval = 0; 16 | flags = 0; 17 | fd = eventfd(initval, flags); 18 | if (fd < 0) { 19 | fprintf(stderr,"eventfd: %s\n", strerror(errno)); 20 | goto done; 21 | } 22 | 23 | pid = fork(); 24 | if (pid < 0) { 25 | fprintf(stderr,"fork: %s\n", strerror(errno)); 26 | goto done; 27 | } 28 | 29 | if (pid > 0) { /* parent */ 30 | 31 | sleep(1); 32 | fprintf(stderr, "parent: writing\n"); 33 | 34 | u = 100; 35 | nr = write(fd, &u, sizeof(u)); 36 | if (nr < 0) { 37 | fprintf(stderr,"write: %s\n", strerror(errno)); 38 | goto done; 39 | } 40 | 41 | wait(NULL); 42 | 43 | } else { /* child */ 44 | 45 | fprintf(stderr, "child: reading\n"); 46 | nr = read(fd, &v, sizeof(v)); 47 | if (nr < 0) { 48 | fprintf(stderr,"read: %s\n", strerror(errno)); 49 | goto done; 50 | } 51 | 52 | fprintf(stderr, "child: read %lu\n", (unsigned long)v); 53 | } 54 | 55 | done: 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /eventfd/event2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char *argv[]) { 9 | unsigned int initval; 10 | int fd, flags; 11 | uint64_t u,v; 12 | ssize_t nr; 13 | pid_t pid; 14 | 15 | initval = 0; 16 | flags = 0; 17 | fd = eventfd(initval, flags); 18 | if (fd < 0) { 19 | fprintf(stderr,"eventfd: %s\n", strerror(errno)); 20 | goto done; 21 | } 22 | 23 | pid = fork(); 24 | if (pid < 0) { 25 | fprintf(stderr,"fork: %s\n", strerror(errno)); 26 | goto done; 27 | } 28 | 29 | if (pid > 0) { /* parent */ 30 | 31 | sleep(1); 32 | fprintf(stderr, "parent: writing\n"); 33 | 34 | u = 100; 35 | nr = write(fd, &u, sizeof(u)); 36 | if (nr < 0) { 37 | fprintf(stderr,"write: %s\n", strerror(errno)); 38 | goto done; 39 | } 40 | 41 | sleep(1); 42 | 43 | u = 150; 44 | nr = write(fd, &u, sizeof(u)); 45 | if (nr < 0) { 46 | fprintf(stderr,"write: %s\n", strerror(errno)); 47 | goto done; 48 | } 49 | 50 | u = 150; 51 | nr = write(fd, &u, sizeof(u)); 52 | if (nr < 0) { 53 | fprintf(stderr,"write: %s\n", strerror(errno)); 54 | goto done; 55 | } 56 | 57 | 58 | wait(NULL); 59 | 60 | } else { /* child */ 61 | 62 | fprintf(stderr, "child: reading\n"); 63 | nr = read(fd, &v, sizeof(v)); 64 | if (nr < 0) { 65 | fprintf(stderr,"read: %s\n", strerror(errno)); 66 | goto done; 67 | } 68 | 69 | fprintf(stderr, "child: read %lu\n", (unsigned long)v); 70 | 71 | sleep(1); 72 | 73 | nr = read(fd, &v, sizeof(v)); 74 | if (nr < 0) { 75 | fprintf(stderr,"read: %s\n", strerror(errno)); 76 | goto done; 77 | } 78 | 79 | fprintf(stderr, "child: read %lu\n", (unsigned long)v); 80 | 81 | } 82 | 83 | done: 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /eventfd/event3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char *argv[]) { 9 | unsigned int initval; 10 | int fd, flags; 11 | uint64_t u,v; 12 | char unused; 13 | ssize_t nr; 14 | pid_t pid; 15 | 16 | initval = 0; 17 | flags = 0; 18 | fd = eventfd(initval, flags); 19 | if (fd < 0) { 20 | fprintf(stderr,"eventfd: %s\n", strerror(errno)); 21 | goto done; 22 | } 23 | 24 | pid = fork(); 25 | if (pid < 0) { 26 | fprintf(stderr,"fork: %s\n", strerror(errno)); 27 | goto done; 28 | } 29 | 30 | if (pid > 0) { /* parent */ 31 | 32 | fprintf(stderr, "parent: writing\n"); 33 | 34 | u = 100; 35 | nr = write(fd, &u, sizeof(u)); 36 | if (nr < 0) { 37 | fprintf(stderr,"write: %s\n", strerror(errno)); 38 | goto done; 39 | } 40 | 41 | fprintf(stderr, "view counter (hex) in /proc/%u/fdinfo/%u\n", (int)getpid(), fd); 42 | 43 | wait(NULL); 44 | 45 | } else { /* child */ 46 | 47 | /* wait for user to press enter */ 48 | sleep(1); 49 | fprintf(stderr, "press \n"); 50 | read(STDIN_FILENO, &unused, sizeof(unused)); 51 | 52 | fprintf(stderr, "child: reading\n"); 53 | nr = read(fd, &v, sizeof(v)); 54 | if (nr < 0) { 55 | fprintf(stderr,"read: %s\n", strerror(errno)); 56 | goto done; 57 | } 58 | 59 | fprintf(stderr, "child: read %lu\n", (unsigned long)v); 60 | 61 | fprintf(stderr, "view counter again then\n"); 62 | fprintf(stderr, "press \n"); 63 | read(STDIN_FILENO, &unused, sizeof(unused)); 64 | } 65 | 66 | done: 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /eventfd/event4.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char *argv[]) { 9 | unsigned int initval; 10 | int fd, flags, posts=0; 11 | uint64_t u,v; 12 | ssize_t nr; 13 | pid_t pid; 14 | 15 | initval = 10; 16 | flags = EFD_SEMAPHORE|EFD_NONBLOCK; 17 | fd = eventfd(initval, flags); 18 | if (fd < 0) { 19 | fprintf(stderr,"eventfd: %s\n", strerror(errno)); 20 | goto done; 21 | } 22 | 23 | pid = fork(); 24 | if (pid < 0) { 25 | fprintf(stderr,"fork: %s\n", strerror(errno)); 26 | goto done; 27 | } 28 | 29 | if (pid > 0) { /* parent */ 30 | 31 | fprintf(stderr, "parent: writing\n"); 32 | 33 | u = 3; 34 | nr = write(fd, &u, sizeof(u)); 35 | if (nr < 0) { 36 | fprintf(stderr,"write: %s\n", strerror(errno)); 37 | goto done; 38 | } 39 | 40 | wait(NULL); 41 | 42 | } else { /* child */ 43 | 44 | sleep(1); 45 | fprintf(stderr, "child: reading\n"); 46 | while (1) { 47 | nr = read(fd, &v, sizeof(v)); 48 | if (nr < 0) { 49 | if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) break; 50 | fprintf(stderr,"read: %s\n", strerror(errno)); 51 | goto done; 52 | } else if (nr > 0) { 53 | fprintf(stderr, "child: read %lu\n", (unsigned long)v); 54 | posts++; 55 | } 56 | } 57 | 58 | fprintf(stderr, "%u posts\n", posts); 59 | } 60 | 61 | done: 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /inotify/Makefile: -------------------------------------------------------------------------------- 1 | all: dirwatch filewatch mv_on_close signal_driven 2 | 3 | .PHONY: clean 4 | 5 | clean: 6 | rm -f dirwatch filewatch *.o mv_on_close signal_driven 7 | -------------------------------------------------------------------------------- /inotify/dirwatch.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* start this program with a directory argument. 7 | * then go into that directory and create, remove, 8 | * rename, or read and write to files and watch 9 | * as inotify receives the event notifications. 10 | * Requires Linux 2.6.13 or higher */ 11 | 12 | int main(int argc, char *argv[]) { 13 | int fd, wd, mask, rc; 14 | char *dir, *name; 15 | 16 | if (argc != 2) { 17 | fprintf(stderr,"usage: %s \n", argv[0]); 18 | exit(-1); 19 | } 20 | 21 | dir = argv[1]; 22 | 23 | if ( (fd = inotify_init()) == -1) { 24 | perror("inotify_init failed"); 25 | exit(-1); 26 | } 27 | 28 | mask = IN_ALL_EVENTS; 29 | if ( (wd = inotify_add_watch(fd, dir, mask)) == -1) { 30 | perror("inotify_add_watch failed"); 31 | exit(-1); 32 | } 33 | 34 | /* see inotify(7) as inotify_event has a trailing name 35 | * field allocated beyond the fixed structure; we must 36 | * allocate enough room for the kernel to populate it */ 37 | struct inotify_event *eb, *ev, *nx; 38 | size_t eb_sz = sizeof(*eb) + PATH_MAX, sz; 39 | if ( (eb = malloc(eb_sz)) == NULL) { 40 | fprintf(stderr, "out of memory\n"); 41 | exit(-1); 42 | } 43 | 44 | /* one read will produce one or more event structures */ 45 | while ( (rc=read(fd,eb,eb_sz)) > 0) { 46 | for(ev = eb; rc > 0; ev = nx) { 47 | 48 | sz = sizeof(*ev) + ev->len; 49 | nx = (struct inotify_event*)((char*)ev + sz); 50 | rc -= sz; 51 | 52 | name = (ev->len ? ev->name : dir); 53 | printf("%s ", name); 54 | if (ev->mask & IN_ACCESS) printf(" IN_ACCESS"); 55 | if (ev->mask & IN_MODIFY) printf(" IN_MODIFY"); 56 | if (ev->mask & IN_ATTRIB) printf(" IN_ATTRIB"); 57 | if (ev->mask & IN_CLOSE_WRITE) printf(" IN_CLOSE_WRITE"); 58 | if (ev->mask & IN_CLOSE_NOWRITE) printf(" IN_CLOSE_NOWRITE"); 59 | if (ev->mask & IN_OPEN) printf(" IN_OPEN"); 60 | if (ev->mask & IN_MOVED_FROM) printf(" IN_MOVED_FROM"); 61 | if (ev->mask & IN_MOVED_TO) printf(" IN_MOVED_TO"); 62 | if (ev->mask & IN_CREATE) printf(" IN_CREATE"); 63 | if (ev->mask & IN_DELETE) printf(" IN_DELETE"); 64 | if (ev->mask & IN_DELETE_SELF) printf(" IN_DELETE_SELF"); 65 | if (ev->mask & IN_MOVE_SELF) printf(" IN_MOVE_SELF"); 66 | if (ev->mask & IN_IGNORED) printf(" IN_IGNORED"); 67 | if (ev->mask & IN_ISDIR) printf(" IN_ISDIR"); 68 | if (ev->mask & IN_Q_OVERFLOW) printf(" IN_Q_OVERFLOW"); 69 | if (ev->mask & IN_UNMOUNT) printf(" IN_UNMOUNT"); 70 | printf("\n"); 71 | } 72 | } 73 | 74 | close(fd); 75 | } 76 | -------------------------------------------------------------------------------- /inotify/filewatch.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* start this program with a file argument. 7 | * then remove, rename or read and write to it 8 | * as inotify receives the event notifications. 9 | * Requires Linux 2.6.13 or higher */ 10 | 11 | int main(int argc, char *argv[]) { 12 | int fd, wd, mask, rc; 13 | char *file, *name; 14 | 15 | if (argc != 2) { 16 | fprintf(stderr,"usage: %s \n", argv[0]); 17 | exit(-1); 18 | } 19 | 20 | file = argv[1]; 21 | 22 | if ( (fd = inotify_init()) == -1) { 23 | perror("inotify_init failed"); 24 | exit(-1); 25 | } 26 | 27 | mask = IN_ALL_EVENTS; 28 | if ( (wd = inotify_add_watch(fd, file, mask)) == -1) { 29 | perror("inotify_add_watch failed"); 30 | exit(-1); 31 | } 32 | 33 | /* see inotify(7) as inotify_event has a trailing name 34 | * field allocated beyond the fixed structure; we must 35 | * allocate enough room for the kernel to populate it */ 36 | struct inotify_event *eb, *ev, *nx; 37 | size_t eb_sz = sizeof(*eb) + PATH_MAX, sz; 38 | if ( (eb = malloc(eb_sz)) == NULL) { 39 | fprintf(stderr, "out of memory\n"); 40 | exit(-1); 41 | } 42 | 43 | /* one read will produce one or more event structures */ 44 | while ( (rc=read(fd,eb,eb_sz)) > 0) { 45 | for(ev = eb; rc > 0; ev = nx) { 46 | 47 | sz = sizeof(*ev) + ev->len; 48 | nx = (struct inotify_event*)((char*)ev + sz); 49 | rc -= sz; 50 | 51 | name = (ev->len ? ev->name : file); 52 | printf("%s ", name); 53 | if (ev->mask & IN_ACCESS) printf(" IN_ACCESS"); 54 | if (ev->mask & IN_MODIFY) printf(" IN_MODIFY"); 55 | if (ev->mask & IN_ATTRIB) printf(" IN_ATTRIB"); 56 | if (ev->mask & IN_CLOSE_WRITE) printf(" IN_CLOSE_WRITE"); 57 | if (ev->mask & IN_CLOSE_NOWRITE) printf(" IN_CLOSE_NOWRITE"); 58 | if (ev->mask & IN_OPEN) printf(" IN_OPEN"); 59 | if (ev->mask & IN_MOVED_FROM) printf(" IN_MOVED_FROM"); 60 | if (ev->mask & IN_MOVED_TO) printf(" IN_MOVED_TO"); 61 | if (ev->mask & IN_CREATE) printf(" IN_CREATE"); 62 | if (ev->mask & IN_DELETE) printf(" IN_DELETE"); 63 | if (ev->mask & IN_DELETE_SELF) printf(" IN_DELETE_SELF"); 64 | if (ev->mask & IN_MOVE_SELF) printf(" IN_MOVE_SELF"); 65 | if (ev->mask & IN_IGNORED) printf(" IN_IGNORED"); 66 | if (ev->mask & IN_ISDIR) printf(" IN_ISDIR"); 67 | if (ev->mask & IN_Q_OVERFLOW) printf(" IN_Q_OVERFLOW"); 68 | if (ev->mask & IN_UNMOUNT) printf(" IN_UNMOUNT"); 69 | printf("\n"); 70 | } 71 | } 72 | 73 | close(fd); 74 | } 75 | -------------------------------------------------------------------------------- /inotify/mv_on_close.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* usage: mv_on_close 9 | * 10 | * whenever a file in watch-dir is closed (if it was open for writing), 11 | * it is moved into the dest-dir. 12 | * 13 | * This implementation is not robust- it only works within a filesystem! 14 | */ 15 | 16 | int main(int argc, char *argv[]) { 17 | int fd, wd, mask, rc; 18 | char *dir, *dest, *name, oldname[PATH_MAX],newname[PATH_MAX]; 19 | 20 | if (argc != 3) { 21 | fprintf(stderr,"usage: %s \n", argv[0]); 22 | exit(-1); 23 | } 24 | 25 | dir = argv[1]; int olen = strlen(dir); 26 | dest = argv[2]; int dlen = strlen(dest); 27 | memcpy(newname, dest, dlen); newname[dlen]='/'; 28 | memcpy(oldname, dir, olen); oldname[olen]='/'; 29 | 30 | if ( (fd = inotify_init()) == -1) { 31 | perror("inotify_init failed"); 32 | exit(-1); 33 | } 34 | 35 | mask = IN_CLOSE_WRITE; 36 | if ( (wd = inotify_add_watch(fd, dir, mask)) == -1) { 37 | perror("inotify_add_watch failed"); 38 | exit(-1); 39 | } 40 | 41 | /* see inotify(7) as inotify_event has a trailing name 42 | * field allocated beyond the fixed structure; we must 43 | * allocate enough room for the kernel to populate it */ 44 | struct inotify_event *eb, *ev, *nx; 45 | size_t eb_sz = sizeof(*eb) + PATH_MAX, sz; 46 | if ( (eb = malloc(eb_sz)) == NULL) { 47 | fprintf(stderr, "out of memory\n"); 48 | exit(-1); 49 | } 50 | 51 | /* one read will produce one or more event structures */ 52 | while ( (rc=read(fd,eb,eb_sz)) > 0) { 53 | for(ev = eb; rc > 0; ev = nx) { 54 | 55 | sz = sizeof(*ev) + ev->len; 56 | nx = (struct inotify_event*)((char*)ev + sz); 57 | rc -= sz; 58 | 59 | name = (ev->len ? ev->name : dir); 60 | memcpy(&newname[dlen+1],name,strlen(name)+1); 61 | memcpy(&oldname[olen+1],name,strlen(name)+1); 62 | fprintf(stderr, "%s --> %s\n", oldname, newname); 63 | 64 | if (rename(oldname, newname)) { 65 | fprintf(stderr,"failed to rename: %s\n",strerror(errno)); 66 | } 67 | #if 0 68 | if (ev->mask & IN_ACCESS) printf(" IN_ACCESS"); 69 | if (ev->mask & IN_MODIFY) printf(" IN_MODIFY"); 70 | if (ev->mask & IN_ATTRIB) printf(" IN_ATTRIB"); 71 | if (ev->mask & IN_CLOSE_WRITE) printf(" IN_CLOSE_WRITE"); 72 | if (ev->mask & IN_CLOSE_NOWRITE) printf(" IN_CLOSE_NOWRITE"); 73 | if (ev->mask & IN_OPEN) printf(" IN_OPEN"); 74 | if (ev->mask & IN_MOVED_FROM) printf(" IN_MOVED_FROM"); 75 | if (ev->mask & IN_MOVED_TO) printf(" IN_MOVED_TO"); 76 | if (ev->mask & IN_CREATE) printf(" IN_CREATE"); 77 | if (ev->mask & IN_DELETE) printf(" IN_DELETE"); 78 | if (ev->mask & IN_DELETE_SELF) printf(" IN_DELETE_SELF"); 79 | if (ev->mask & IN_MOVE_SELF) printf(" IN_MOVE_SELF"); 80 | printf("\n"); 81 | #endif 82 | } 83 | } 84 | 85 | close(fd); 86 | } 87 | -------------------------------------------------------------------------------- /inotify/signal_driven.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | /* signals that we'll accept during sigwaitinfo */ 16 | int sigs[] = {SIGIO,SIGHUP,SIGTERM,SIGINT,SIGALRM}; 17 | 18 | void usage(char *prog) { 19 | fprintf(stderr,"usage: %s dir [dir ...]\n", prog); 20 | exit(-1); 21 | } 22 | 23 | const size_t eb_sz = sizeof(struct inotify_event) + PATH_MAX; 24 | 25 | int setup_watch(int argc, char *argv[], int fd, struct inotify_event **eb) { 26 | int i,wd,mask=IN_ALL_EVENTS; 27 | char *dir; 28 | 29 | for(i=1; i < argc; i++) { 30 | dir = argv[i]; 31 | if ( (wd = inotify_add_watch(fd, dir, mask)) == -1) { 32 | perror("inotify_add_watch failed"); 33 | exit(-1); 34 | } 35 | } 36 | if ( (*eb = malloc(eb_sz)) == NULL) { 37 | fprintf(stderr,"out of memory\n"); 38 | exit(-1); 39 | } 40 | } 41 | 42 | int read_events(int fd, struct inotify_event *eb) { 43 | struct inotify_event *ev, *nx; 44 | char *dir = "", *name; 45 | size_t sz; 46 | int rc; 47 | 48 | while ( (rc=read(fd,eb,eb_sz)) > 0) { 49 | for(ev = eb; rc > 0; ev = nx) { 50 | 51 | sz = sizeof(*ev) + ev->len; 52 | nx = (struct inotify_event*)((char*)ev + sz); 53 | rc -= sz; 54 | 55 | name = (ev->len ? ev->name : dir); 56 | printf("%s ", name); 57 | if (ev->mask & IN_ACCESS) printf(" IN_ACCESS"); 58 | if (ev->mask & IN_MODIFY) printf(" IN_MODIFY"); 59 | if (ev->mask & IN_ATTRIB) printf(" IN_ATTRIB"); 60 | if (ev->mask & IN_CLOSE_WRITE) printf(" IN_CLOSE_WRITE"); 61 | if (ev->mask & IN_CLOSE_NOWRITE) printf(" IN_CLOSE_NOWRITE"); 62 | if (ev->mask & IN_OPEN) printf(" IN_OPEN"); 63 | if (ev->mask & IN_MOVED_FROM) printf(" IN_MOVED_FROM"); 64 | if (ev->mask & IN_MOVED_TO) printf(" IN_MOVED_TO"); 65 | if (ev->mask & IN_CREATE) printf(" IN_CREATE"); 66 | if (ev->mask & IN_DELETE) printf(" IN_DELETE"); 67 | if (ev->mask & IN_DELETE_SELF) printf(" IN_DELETE_SELF"); 68 | if (ev->mask & IN_MOVE_SELF) printf(" IN_MOVE_SELF"); 69 | printf("\n"); 70 | } 71 | } 72 | return rc; 73 | } 74 | 75 | int main(int argc, char *argv[]) { 76 | int fd, rc, n, signo, num_bytes; 77 | struct inotify_event *eb; 78 | 79 | if (argc < 2) usage(argv[0]); 80 | 81 | if ( (fd = inotify_init()) == -1) { 82 | perror("inotify_init failed"); 83 | exit(-1); 84 | } 85 | 86 | /* request SIGIO to our pid when fd is ready; see fcntl(2) */ 87 | int fl = fcntl(fd, F_GETFL); 88 | fl |= O_ASYNC | O_NONBLOCK; 89 | fcntl(fd, F_SETFL, fl); 90 | fcntl(fd, F_SETOWN, getpid()); 91 | fcntl(fd, F_SETSIG, SIGIO); 92 | 93 | /* block all signals. stay blocked except in sigwaitinfo */ 94 | sigset_t all; 95 | sigfillset(&all); 96 | sigprocmask(SIG_SETMASK,&all,NULL); 97 | 98 | /* a few signals we'll accept during sigwaitinfo */ 99 | sigset_t sw; 100 | sigemptyset(&sw); 101 | for(n=0; n < sizeof(sigs)/sizeof(*sigs); n++) sigaddset(&sw, sigs[n]); 102 | 103 | setup_watch(argc,argv,fd,&eb); 104 | 105 | siginfo_t info; 106 | alarm(1); 107 | 108 | while ( (signo = sigwaitinfo(&sw, &info)) > 0) { 109 | switch(signo) { 110 | case SIGALRM: 111 | alarm(1); 112 | /* many kernels even into 3.x do not reliably generate 113 | * the signal on inotify descriptors. so we check whether 114 | * there are bytes available on that descriptor manually */ 115 | if (ioctl(fd,FIONREAD,&num_bytes) != 0) { 116 | fprintf(stderr,"ioctl error %s\n",strerror(errno)); 117 | goto done; 118 | } 119 | if (num_bytes == 0) break; 120 | fprintf(stderr,"unsignaled data on inotify descriptor (%u bytes)\n",num_bytes); 121 | /* so, fall through */ 122 | case SIGIO: 123 | rc = read_events(fd,eb); 124 | if (rc == 0) goto done; 125 | if ((rc == -1) && (errno == EAGAIN)) break; /* no data */ 126 | if (rc == -1) { 127 | fprintf(stderr,"read error %s\n", strerror(errno)); 128 | goto done; 129 | } 130 | break; 131 | default: 132 | fprintf(stderr,"got signal %d\n", signo); 133 | goto done; 134 | break; 135 | } 136 | } 137 | 138 | done: 139 | close(fd); 140 | return 0; 141 | } 142 | -------------------------------------------------------------------------------- /pcre/Makefile: -------------------------------------------------------------------------------- 1 | LDFLAGS=-lpcre 2 | 3 | all: multi simple captures 4 | 5 | multi: multi.o 6 | simple: simple.o 7 | captures: captures.o 8 | 9 | .PHONY: clean 10 | 11 | clean: 12 | rm -f *.o multi simple captures 13 | -------------------------------------------------------------------------------- /pcre/captures.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define OVECSZ 30 /* must be multiple of 3 */ 7 | 8 | /* sample usage: ./captures 'abc(\d+)(_(\d+))?' abc123_999 */ 9 | 10 | int main(int argc, char *argv[]) { 11 | int i,rc,j; 12 | 13 | if (argc < 3) { 14 | fprintf(stderr, "usage: ...\n"); 15 | exit(-1); 16 | } 17 | 18 | const char *err; int off, ovec[OVECSZ]; 19 | 20 | pcre *re = pcre_compile(argv[1], 0, &err, &off, NULL); 21 | if (re == NULL) { 22 | fprintf(stderr, "error in regex %s: %s (offset %u)\n", argv[1], err, off); 23 | exit(-1); 24 | } 25 | 26 | for(i=2; i < argc; i++) { 27 | char *s = argv[i]; 28 | printf("testing %s:\n",s); 29 | if ( (rc=pcre_exec(re, NULL, s, strlen(s), 0, 0, ovec, OVECSZ)) > 0) { 30 | for(j=0; j < rc*2; j+=2) { 31 | printf(" $%u matched %.*s\n", j/2, ovec[j+1]-ovec[j], &s[ovec[j]]); 32 | } 33 | } 34 | printf("\n"); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /pcre/multi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* sample program that matches input against several regex's, 7 | * extracts captured subpatterns. compile with -lpcre and test with: 8 | * 9 | * ./template one,two,three abc123 10 | * 11 | */ 12 | 13 | #define OVECLEN 30 /* must be multiple of three. */ 14 | 15 | typedef struct { 16 | char *regex; 17 | pcre *re; 18 | pcre_extra *x; 19 | } regex_t; 20 | 21 | regex_t rexes[] = { 22 | {.regex = "abc(\\d+)", }, 23 | {.regex = "^(\\w+),(\\w+),(\\w+)$", }, 24 | }; 25 | 26 | int main(int argc, char *argv[]) { 27 | int i,j, ovec[OVECLEN], rc; regex_t *r; 28 | 29 | /* for pcre to report compilation errors back to us */ 30 | const char *err; 31 | int off; 32 | 33 | for(i=0; i < sizeof(rexes)/sizeof(*rexes); i++) { 34 | r = &rexes[i]; 35 | /* compile the regular expression */ 36 | r->re = pcre_compile(r->regex, 0, &err, &off, NULL); 37 | if (r->re == NULL) { 38 | fprintf(stderr,"error %s in pattern %s at %u\n", err, r->regex, off); 39 | exit(-1); 40 | } 41 | 42 | /* optional: study the pattern */ 43 | r->x = pcre_study(r->re, 0, &err); 44 | if (err) { 45 | fprintf(stderr,"study of %s failed: %s\n", r->regex, err); 46 | exit(-1); 47 | } 48 | } 49 | 50 | /* now we loop over some input and test the regex */ 51 | for(i=0; i < argc; i++) { 52 | for(j=0; j < sizeof(rexes)/sizeof(*rexes); j++) { 53 | r = &rexes[j]; 54 | rc = pcre_exec(r->re, r->x, argv[i], strlen(argv[i]), 0, 0, ovec, OVECLEN); 55 | if (rc >= 0) { 56 | printf("%s matches %s: first capture %.*s\n", argv[i], r->regex, 57 | ovec[3]-ovec[2], &argv[i][ovec[2]]); 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /pcre/simple.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char *argv[]) { 7 | int i,rc; 8 | 9 | /* for pcre to report compilation errors back to us */ 10 | const char *err; 11 | int off; 12 | 13 | char *regex = "hello,?\\sworld!?"; 14 | 15 | pcre *re = pcre_compile(regex, 0, &err, &off, NULL); 16 | if (re == NULL) { 17 | printf("error %s in pattern %s at offset %u\n", err, regex, off); 18 | exit(-1); 19 | } 20 | 21 | char *tests[] = { "hello, world!", "hello world!", "hello world" }; 22 | 23 | /* now we coule loop over some input and test the regex */ 24 | for(i=0; i < sizeof(tests)/sizeof(*tests); i++) { 25 | rc = pcre_exec(re, NULL, tests[i], strlen(tests[i]), 0, 0, NULL, 0); 26 | if (rc >= 0) { 27 | printf("%s matches %s\n", tests[i], regex); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pipe/Makefile: -------------------------------------------------------------------------------- 1 | PROGS=pipe1 pipe2 2 | OBJS=$(patsubst %,%.o,$(PROGS)) 3 | all: $(OBJS) $(PROGS) 4 | 5 | # static pattern rule: multiple targets 6 | 7 | $(OBJS): %.o: %.c 8 | $(CC) -c $(CFLAGS) $< 9 | 10 | $(PROGS): %: %.o 11 | $(CC) -o $@ $(CFLAGS) $< $(LDFLAGS) 12 | 13 | .PHONY: clean 14 | 15 | clean: 16 | rm -f $(OBJS) $(PROGS) 17 | -------------------------------------------------------------------------------- /pipe/README.md: -------------------------------------------------------------------------------- 1 | Example of pipe 2 | 3 | See pipe(2). 4 | 5 | A call to pipe() results in a pair of file descriptors, the "write end" and the 6 | "read end". A typical usage is that the pipe is created, then the process 7 | forks, then the writer closes the read end, and vice versa. Then the writer 8 | writes and the reader reads. A pipe is uni-directonal from writer to reader. 9 | 10 | After a fork, it is important to have the reader close the write end, to detect 11 | EOF properly, since EOF only occurs when all the write descriptors are closed. 12 | 13 | Examples 14 | 15 | * pipe1: simple use of pipe between processes 16 | -------------------------------------------------------------------------------- /pipe/pipe1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define R 0 8 | #define W 1 9 | 10 | int main(int argc, char *argv[]) { 11 | char buf[100], *msg; 12 | int fd[2], sc; 13 | ssize_t nr; 14 | pid_t pid; 15 | 16 | sc = pipe(fd); 17 | if (sc < 0) { 18 | fprintf(stderr,"pipe: %s\n", strerror(errno)); 19 | exit(-1); 20 | } 21 | 22 | pid = fork(); 23 | if (pid < 0) { 24 | fprintf(stderr,"fork: %s\n", strerror(errno)); 25 | exit(-1); 26 | } 27 | 28 | if (pid > 0) { /* parent */ 29 | 30 | /* parent close read end */ 31 | close( fd[R] ); 32 | 33 | msg = "hello, world!"; 34 | nr = write(fd[W], msg, strlen(msg)); 35 | if (nr < 0) { 36 | fprintf(stderr,"write: %s\n", strerror(errno)); 37 | exit(-1); 38 | } 39 | 40 | close( fd[W] ); 41 | wait(NULL); 42 | 43 | } else { /* child */ 44 | 45 | /* child close write end */ 46 | close( fd[W] ); 47 | 48 | do { 49 | 50 | fprintf(stderr, "child: reading\n"); 51 | nr = read(fd[R], buf, sizeof(buf)); 52 | if (nr < 0) fprintf(stderr,"read: %s\n", strerror(errno)); 53 | else if (nr == 0) fprintf(stderr,"read: eof\n"); 54 | else fprintf(stderr, "child: read %.*s\n", (int)nr, buf); 55 | 56 | } while( nr > 0); 57 | } 58 | 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /posix-msg-queue/Makefile: -------------------------------------------------------------------------------- 1 | PROGS=mq-read mq-write mq-unlink 2 | OBJS=$(patsubst %,%.o,$(PROGS)) 3 | 4 | CFLAGS=-g -Wall -Wextra 5 | LDFLAGS=-lrt 6 | 7 | all: $(OBJS) $(PROGS) 8 | 9 | # static pattern rule: multiple targets 10 | 11 | $(OBJS): %.o: %.c 12 | $(CC) -c $(CFLAGS) $< 13 | 14 | $(PROGS): %: %.o 15 | $(CC) -o $@ $(CFLAGS) $< $(LDFLAGS) 16 | 17 | .PHONY: clean 18 | 19 | clean: 20 | rm -f $(OBJS) $(PROGS) 21 | -------------------------------------------------------------------------------- /posix-msg-queue/README.md: -------------------------------------------------------------------------------- 1 | # POSIX message queues. 2 | 3 | Message queues allow processes to exchange messages by opening the same queue. 4 | See `mq_overview(7)`. On Linux, message queues: 5 | 6 | * are identified using a flat, absolute namespace e.g. /somename 7 | * persist until a process uses `mq_unlink` or the system reboots 8 | * descriptors work with epoll; on Linux, they're file descriptors 9 | * min/max message sizes and queue lengths in /proc/sys/fs/mqueue 10 | * require applications link with `-lrt` 11 | * are listed in /dev/mqueue 12 | 13 | See `mq-read.c` and `mq-write.c`. Try them out: 14 | 15 | % make 16 | % ./mq-read /foo 17 | 18 | Now in another terminal, 19 | 20 | % ./mq-write /foo hello 21 | 22 | You can play around with read/write to see how the queue will accept up to 23 | 10 messages before the writer blocks. Run the reader repeatedly to drain it. 24 | 25 | Inspect the queue via the /proc filesystem: 26 | 27 | % cat /dev/mqueue/foo 28 | QSIZE:0 NOTIFY:0 SIGNO:0 NOTIFY_PID:0 29 | 30 | QSIZE shows the number of bytes queued across all messages. The other fields 31 | relate to `mq_notify(3)`. 32 | 33 | If /dev/mqueue is not already mounted, `sem_overview(7)` says to mount it using: 34 | 35 | % mkdir /dev/mqueue 36 | % mount -t mqueue none /dev/mqueue 37 | 38 | When you are done, unlink the message queue. A reboot would also unlink it. 39 | 40 | % ./mq-unlink /foo 41 | 42 | You could also use the /dev/mqueue filesystem to remove a queue: 43 | 44 | % rm /dev/mqueue/foo 45 | 46 | -------------------------------------------------------------------------------- /posix-msg-queue/mq-read.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* 11 | * demonstrate opening a POSIX message queue and reading from it. 12 | * 13 | * usage: 14 | * 15 | * ./mq-open /foo 16 | * 17 | * see: 18 | * mq_overview(7) 19 | * mq_open(3) 20 | * mq_receive(3) 21 | * 22 | */ 23 | 24 | #define MSG_MAX 8192 /* minimum in some kernels, see mq_overview(7) */ 25 | 26 | struct { 27 | char *prog; 28 | int verbose; 29 | char *name; 30 | mqd_t fd; 31 | } CF; 32 | 33 | void usage(void) { 34 | fprintf(stderr,"usage: %s [-v] \n", CF.prog); 35 | exit(-1); 36 | } 37 | 38 | int main(int argc, char *argv[]) { 39 | char buf[MSG_MAX]; 40 | int opt, rc=-1; 41 | ssize_t nr; 42 | 43 | CF.prog = argv[0]; 44 | 45 | while ( (opt = getopt(argc,argv,"vh")) > 0) { 46 | switch(opt) { 47 | case 'v': CF.verbose++; break; 48 | case 'h': default: usage(); break; 49 | } 50 | } 51 | 52 | if (optind >= argc) usage(); 53 | 54 | struct mq_attr attr = { 55 | .mq_maxmsg = 10, /* max messages on queue */ 56 | .mq_msgsize = MSG_MAX, /* max size of a message */ 57 | }; 58 | 59 | CF.name = argv[optind++]; 60 | CF.fd = mq_open(CF.name, O_RDONLY | O_CREAT, 0600, &attr); 61 | if (CF.fd == (mqd_t)-1) { 62 | fprintf(stderr,"mq_open %s: %s\n", CF.name, strerror(errno)); 63 | goto done; 64 | } 65 | 66 | nr = mq_receive(CF.fd, buf, MSG_MAX, NULL); 67 | if (nr < 0) { 68 | fprintf(stderr, "mq_receive: %s\n", strerror(errno)); 69 | goto done; 70 | } 71 | 72 | printf("received %d bytes: %.*s\n", (int)nr, (int)nr, buf); 73 | 74 | rc = 0; 75 | 76 | done: 77 | if (CF.fd != (mqd_t)-1) mq_close(CF.fd); 78 | return rc; 79 | } 80 | -------------------------------------------------------------------------------- /posix-msg-queue/mq-unlink.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* 11 | * demonstrate unlinking a POSIX message queue 12 | * 13 | * usage: 14 | * 15 | * ./mq-unlink /foo 16 | * 17 | * see: 18 | * mq_overview(7) 19 | * mq_unlink(3) 20 | * 21 | */ 22 | 23 | struct { 24 | char *prog; 25 | int verbose; 26 | char *name; 27 | } CF; 28 | 29 | void usage(void) { 30 | fprintf(stderr,"usage: %s [-v] \n", CF.prog); 31 | exit(-1); 32 | } 33 | 34 | int main(int argc, char *argv[]) { 35 | int opt, rc=-1, sc; 36 | 37 | CF.prog = argv[0]; 38 | 39 | while ( (opt = getopt(argc,argv,"vh")) > 0) { 40 | switch(opt) { 41 | case 'v': CF.verbose++; break; 42 | case 'h': default: usage(); break; 43 | } 44 | } 45 | 46 | if (optind >= argc) usage(); 47 | 48 | CF.name = argv[optind++]; 49 | sc = mq_unlink(CF.name); 50 | if (sc == (mqd_t)-1) { 51 | fprintf(stderr,"mq_unlink %s: %s\n", CF.name, strerror(errno)); 52 | goto done; 53 | } 54 | 55 | rc = 0; 56 | 57 | done: 58 | return rc; 59 | } 60 | -------------------------------------------------------------------------------- /posix-msg-queue/mq-write.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* 11 | * demonstrate opening a POSIX message queue and writing to it. 12 | * 13 | * usage: 14 | * 15 | * ./mq-write /foo Hello 16 | * 17 | * see: 18 | * mq_overview(7) 19 | * mq_open(3) 20 | * mq_send(3) 21 | * 22 | */ 23 | struct { 24 | char *prog; 25 | int verbose; 26 | char *name; 27 | mqd_t fd; 28 | } CF; 29 | 30 | void usage(void) { 31 | fprintf(stderr,"usage: %s [-v] \n", CF.prog); 32 | exit(-1); 33 | } 34 | 35 | int main(int argc, char *argv[]) { 36 | int opt, rc=-1, sc; 37 | char *msg; 38 | 39 | CF.prog = argv[0]; 40 | 41 | while ( (opt = getopt(argc,argv,"vh")) > 0) { 42 | switch(opt) { 43 | case 'v': CF.verbose++; break; 44 | case 'h': default: usage(); break; 45 | } 46 | } 47 | 48 | if (optind >= argc) usage(); 49 | 50 | CF.name = argv[optind++]; 51 | CF.fd = mq_open(CF.name, O_WRONLY); 52 | if (CF.fd == (mqd_t)-1) { 53 | fprintf(stderr,"mq_open %s: %s\n", CF.name, strerror(errno)); 54 | goto done; 55 | } 56 | 57 | msg = (optind < argc) ? argv[optind++] : "hello"; 58 | sc = mq_send(CF.fd, msg, strlen(msg), 0); 59 | if (sc < 0) { 60 | fprintf(stderr, "mq_send: %s\n", strerror(errno)); 61 | goto done; 62 | } 63 | 64 | rc = 0; 65 | 66 | done: 67 | if (CF.fd != (mqd_t)-1) mq_close(CF.fd); 68 | return rc; 69 | } 70 | -------------------------------------------------------------------------------- /posix-semaphore/Makefile: -------------------------------------------------------------------------------- 1 | PROGS=psem-create psem-wait psem-post psem-unlink psem-getvalue 2 | 3 | OBJS=$(patsubst %,%.o,$(PROGS)) 4 | all: $(OBJS) $(PROGS) 5 | 6 | CFLAGS=-pthread 7 | LDFLAGS= 8 | 9 | # static pattern rule: multiple targets 10 | 11 | $(OBJS): %.o: %.c 12 | $(CC) -c $(CFLAGS) $< 13 | 14 | $(PROGS): %: %.o 15 | $(CC) -o $@ $(CFLAGS) $< $(LDFLAGS) 16 | 17 | 18 | .PHONY: clean 19 | 20 | clean: 21 | rm -f $(OBJS) $(PROGS) 22 | -------------------------------------------------------------------------------- /posix-semaphore/README.md: -------------------------------------------------------------------------------- 1 | POSIX semaphors provide an atomic post/wait mechanism. They come in named 2 | and unnamed versions. The examples here are based on The Linx Programming 3 | Interface by Michael Kerrisk. They show usage of named semaphores. The Linux 4 | implementation of named semaphores, available since kernel 2.6, internally 5 | creates the semaphore in /dev/shm/sem.name. 6 | 7 | As with the usual semaphore behavior- each post increments its value, and each 8 | wait decrements it- immediately if it's already positive, or blocking until it 9 | becomes positive first. Thus a producer can use it to notify a consumer of how 10 | many units are available to consume, by posting that many times. 11 | 12 | % ./psem-create -c /demo 13 | % ./psem-wait /demo & 14 | % ./psem-getvalue /demo 15 | % ./psem-post /demo 16 | % ./psem-unlink /demo 17 | -------------------------------------------------------------------------------- /posix-semaphore/psem-create.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct { 10 | int verbose; 11 | char *prog; 12 | char *file; 13 | sem_t *sem; 14 | } CF; 15 | 16 | void usage() { 17 | fprintf(stderr,"usage: %s [-v] -c [-x] \n", CF.prog); 18 | exit(-1); 19 | } 20 | 21 | int main(int argc, char *argv[]) { 22 | int opt, rc=-1; 23 | CF.prog = argv[0]; 24 | int flags=0, perms=0644, initial_value=0; 25 | 26 | while ( (opt = getopt(argc,argv,"vcxh")) > 0) { 27 | switch(opt) { 28 | case 'v': CF.verbose++; break; 29 | case 'c': flags |= O_CREAT; break; 30 | case 'x': flags |= O_EXCL; break; 31 | case 'h': default: usage(); break; 32 | } 33 | } 34 | 35 | if (argc > optind) CF.file = argv[optind++]; 36 | else usage(); 37 | 38 | CF.sem = sem_open(CF.file, flags, perms, initial_value); 39 | if (CF.sem == SEM_FAILED) { 40 | fprintf(stderr,"sem_open %s: %s\n", CF.file, strerror(errno)); 41 | goto done; 42 | } 43 | 44 | rc = 0; 45 | 46 | done: 47 | if (CF.sem && (CF.sem != SEM_FAILED)) sem_close(CF.sem); 48 | return rc; 49 | } 50 | -------------------------------------------------------------------------------- /posix-semaphore/psem-getvalue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct { 10 | int verbose; 11 | char *prog; 12 | char *file; 13 | sem_t *sem; 14 | } CF; 15 | 16 | void usage() { 17 | fprintf(stderr,"usage: %s [-v] \n", CF.prog); 18 | exit(-1); 19 | } 20 | 21 | int main(int argc, char *argv[]) { 22 | int opt, rc=-1; 23 | CF.prog = argv[0]; 24 | int flags=0; 25 | 26 | while ( (opt = getopt(argc,argv,"vh")) > 0) { 27 | switch(opt) { 28 | case 'v': CF.verbose++; break; 29 | case 'h': default: usage(); break; 30 | } 31 | } 32 | 33 | if (argc > optind) CF.file = argv[optind++]; 34 | else usage(); 35 | 36 | CF.sem = sem_open(CF.file, flags); 37 | if (CF.sem == SEM_FAILED) { 38 | fprintf(stderr,"sem_open %s: %s\n", CF.file, strerror(errno)); 39 | goto done; 40 | } 41 | 42 | int value; 43 | if (sem_getvalue(CF.sem, &value) == -1) { 44 | fprintf(stderr,"sem_getvalue %s: %s\n", CF.file, strerror(errno)); 45 | goto done; 46 | } 47 | 48 | fprintf(stderr, "sem_getvalue: %d\n", value); 49 | rc = 0; 50 | 51 | done: 52 | if (CF.sem && (CF.sem != SEM_FAILED)) sem_close(CF.sem); 53 | return rc; 54 | } 55 | -------------------------------------------------------------------------------- /posix-semaphore/psem-post.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct { 10 | int verbose; 11 | char *prog; 12 | char *file; 13 | sem_t *sem; 14 | } CF; 15 | 16 | void usage() { 17 | fprintf(stderr,"usage: %s [-v] \n", CF.prog); 18 | exit(-1); 19 | } 20 | 21 | int main(int argc, char *argv[]) { 22 | int opt, rc=-1; 23 | CF.prog = argv[0]; 24 | int flags=0; 25 | 26 | while ( (opt = getopt(argc,argv,"vh")) > 0) { 27 | switch(opt) { 28 | case 'v': CF.verbose++; break; 29 | case 'h': default: usage(); break; 30 | } 31 | } 32 | 33 | if (argc > optind) CF.file = argv[optind++]; 34 | else usage(); 35 | 36 | CF.sem = sem_open(CF.file, flags); 37 | if (CF.sem == SEM_FAILED) { 38 | fprintf(stderr,"sem_open %s: %s\n", CF.file, strerror(errno)); 39 | goto done; 40 | } 41 | 42 | if (sem_post(CF.sem) == -1) { 43 | fprintf(stderr,"sem_post %s: %s\n", CF.file, strerror(errno)); 44 | goto done; 45 | } 46 | 47 | rc = 0; 48 | 49 | done: 50 | if (CF.sem && (CF.sem != SEM_FAILED)) sem_close(CF.sem); 51 | return rc; 52 | } 53 | -------------------------------------------------------------------------------- /posix-semaphore/psem-unlink.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct { 10 | int verbose; 11 | char *prog; 12 | char *file; 13 | } CF; 14 | 15 | void usage() { 16 | fprintf(stderr,"usage: %s [-v] \n", CF.prog); 17 | exit(-1); 18 | } 19 | 20 | int main(int argc, char *argv[]) { 21 | int opt, rc=-1; 22 | CF.prog = argv[0]; 23 | int flags=0; 24 | 25 | while ( (opt = getopt(argc,argv,"vh")) > 0) { 26 | switch(opt) { 27 | case 'v': CF.verbose++; break; 28 | case 'h': default: usage(); break; 29 | } 30 | } 31 | 32 | if (argc > optind) CF.file = argv[optind++]; 33 | else usage(); 34 | 35 | if (sem_unlink(CF.file) == -1) { 36 | fprintf(stderr,"sem_unlink %s: %s\n", CF.file, strerror(errno)); 37 | goto done; 38 | } 39 | 40 | rc = 0; 41 | 42 | done: 43 | return rc; 44 | } 45 | -------------------------------------------------------------------------------- /posix-semaphore/psem-wait.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct { 10 | int verbose; 11 | char *prog; 12 | char *file; 13 | sem_t *sem; 14 | } CF; 15 | 16 | void usage() { 17 | fprintf(stderr,"usage: %s [-v] \n", CF.prog); 18 | exit(-1); 19 | } 20 | 21 | int main(int argc, char *argv[]) { 22 | int opt, rc=-1; 23 | CF.prog = argv[0]; 24 | int flags=0; 25 | 26 | while ( (opt = getopt(argc,argv,"vh")) > 0) { 27 | switch(opt) { 28 | case 'v': CF.verbose++; break; 29 | case 'h': default: usage(); break; 30 | } 31 | } 32 | 33 | if (argc > optind) CF.file = argv[optind++]; 34 | else usage(); 35 | 36 | CF.sem = sem_open(CF.file, flags); 37 | if (CF.sem == SEM_FAILED) { 38 | fprintf(stderr,"sem_open %s: %s\n", CF.file, strerror(errno)); 39 | goto done; 40 | } 41 | 42 | if (sem_wait(CF.sem) == -1) { 43 | fprintf(stderr,"sem_wait %s: %s\n", CF.file, strerror(errno)); 44 | goto done; 45 | } 46 | 47 | fprintf(stderr, "[%ld] sem_wait: succeeded\n", (long)getpid()); 48 | rc = 0; 49 | 50 | done: 51 | if (CF.sem && (CF.sem != SEM_FAILED)) sem_close(CF.sem); 52 | return rc; 53 | } 54 | -------------------------------------------------------------------------------- /prctl/Makefile: -------------------------------------------------------------------------------- 1 | OBJS=pr_set_name 2 | all: $(OBJS) 3 | 4 | .PHONY: clean 5 | 6 | clean: 7 | rm -f $(OBJS) 8 | -------------------------------------------------------------------------------- /prctl/pr_set_name.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* set name of calling thread; see prctl(2) */ 6 | 7 | int main(int argc, char *argv[]) { 8 | int rc = -1; 9 | char c; 10 | 11 | char *name = (argc > 1) ? argv[1] : "twix"; 12 | if (prctl(PR_SET_NAME, name) < 0) { 13 | fprintf(stderr, "prctl: %s\n", strerror(errno)); 14 | goto done; 15 | } 16 | 17 | fprintf(stderr, "pid %d thread name set to %s\n", getpid(), name); 18 | 19 | fprintf(stderr, "compare output of:\n\n"); 20 | fprintf(stderr, " ps -a (shows new name)\n"); 21 | fprintf(stderr, " top (shows new name)\n"); 22 | fprintf(stderr, " pstree (shows new name)\n"); 23 | fprintf(stderr, " grep Name /proc//status (shows new name)\n"); 24 | fprintf(stderr, " ps -ax (shows old name)\n"); 25 | fprintf(stderr, " cat /proc//cmdline (shows old name)\n"); 26 | fprintf(stderr, "\n"); 27 | 28 | fprintf(stderr, "press to quit: "); 29 | read(0, &c, 1); 30 | rc = 0; 31 | 32 | done: 33 | return rc; 34 | } 35 | -------------------------------------------------------------------------------- /readdir/Makefile: -------------------------------------------------------------------------------- 1 | OBJS = readdir readdir-lstat readdir-suffix 2 | all: $(OBJS) 3 | 4 | .PHONY: clean 5 | 6 | CFLAGS=-Wall 7 | 8 | clean: 9 | rm -f $(OBJS) *.o 10 | -------------------------------------------------------------------------------- /readdir/README.md: -------------------------------------------------------------------------------- 1 | Show usage of readdir(3) to iterate over a directory. 2 | 3 | An example of reading a directory, and doing a optional 4 | suffix match on the filenames, is in readdir-suffix.c 5 | 6 | % ./readdir-suffix -d /etc -s .conf 7 | suffix match: logrotate.conf 8 | suffix match: debconf.conf 9 | suffix match: ltrace.conf 10 | 11 | The directory entry has a field called `d_type` which 12 | seems useful in distinguishing a regular file from a 13 | directory or block device, fifo or other kind of file. 14 | The readdir program prints a string representation of 15 | this field. 16 | 17 | % /readdir /etc 18 | 1: inode 655913 type DT_DIR update-motd.d 19 | 2: inode 655635 type DT_DIR iproute2 20 | 3: inode 655706 type DT_REG networks 21 | 22 | However, on some filesystems `d_type` is not populated. 23 | We can call lstat(2) on each entry to get a more robust 24 | set of mode bits. This is shown in readdir-lstat.c. 25 | 26 | % ./readdir-lstat /etc 27 | 1: inode 655913 type DT_DIR mode directory name update-motd.d 28 | 2: inode 655635 type DT_DIR mode directory name iproute2 29 | 3: inode 655706 type DT_REG mode file name networks 30 | 31 | -------------------------------------------------------------------------------- /readdir/readdir-lstat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | char path[PATH_MAX]; 11 | 12 | int main(int argc, char *argv[]) { 13 | int rc = -1, i = 0; 14 | char *dirname, *type, *mode; 15 | struct dirent *dent; 16 | struct stat s; 17 | DIR *d = NULL; 18 | size_t dl, el; 19 | 20 | dirname = (argc > 1) ? argv[1] : "."; 21 | 22 | d = opendir(dirname); 23 | if (d == NULL) { 24 | fprintf(stderr, "opendir %s: %s\n", dirname, strerror(errno)); 25 | goto done; 26 | } 27 | 28 | while ( (dent = readdir(d)) != NULL) { 29 | 30 | switch(dent->d_type) { 31 | case DT_BLK: type = "DT_BLK"; break; 32 | case DT_CHR: type = "DT_CHR"; break; 33 | case DT_DIR: type = "DT_DIR"; break; 34 | case DT_FIFO: type = "DT_FIFO"; break; 35 | case DT_LNK: type = "DT_LNK"; break; 36 | case DT_REG: type = "DT_REG"; break; 37 | case DT_SOCK: type = "DT_SOCK"; break; 38 | case DT_UNKNOWN: type = "DT_UNKNOWN"; break; 39 | default: type = "other"; break; 40 | } 41 | 42 | /* now to lstat this entry we need to formulate it into a path */ 43 | dl = strlen(dirname); 44 | el = strlen(dent->d_name); 45 | if (dl+1+el+1 > sizeof(path)) goto done; 46 | memcpy(path, dirname, dl); 47 | path[dl] = '/'; 48 | memcpy(&path[dl+1], dent->d_name, el+1); 49 | 50 | if (lstat(path, &s) < 0) { 51 | fprintf(stderr, "stat %s: %s\n", path, strerror(errno)); 52 | continue; 53 | } 54 | 55 | mode = "unknown"; 56 | if (S_ISREG(s.st_mode)) mode = "file"; 57 | if (S_ISDIR(s.st_mode)) mode = "directory"; 58 | if (S_ISCHR(s.st_mode)) mode = "chardev"; 59 | if (S_ISBLK(s.st_mode)) mode = "blockdev"; 60 | if (S_ISFIFO(s.st_mode)) mode = "fifo"; 61 | if (S_ISLNK(s.st_mode)) mode = "symlink"; 62 | if (S_ISSOCK(s.st_mode)) mode = "socket"; 63 | 64 | printf(" %d: inode %lu type %s mode %s name %s\n", ++i, 65 | (unsigned long)dent->d_ino, type, mode, dent->d_name); 66 | } 67 | 68 | printf("%d entries found.\n", i); 69 | rc = 0; 70 | 71 | done: 72 | if (d) closedir(d); 73 | return rc; 74 | } 75 | -------------------------------------------------------------------------------- /readdir/readdir-suffix.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* 10 | * 11 | * simple example of reading a directory and doing a suffix match on files 12 | * 13 | * Example usage: 14 | * 15 | * ./readdir-suffix -d /etc -s .conf 16 | * 17 | */ 18 | 19 | struct { 20 | int verbose; 21 | char *dir; 22 | char *suffix; 23 | char *prog; 24 | } CF = { 25 | }; 26 | 27 | void usage() { 28 | fprintf(stderr, "usage: %s [-v] [-s ] -d \n", CF.prog); 29 | exit(-1); 30 | } 31 | 32 | int match_suffix(char *file, char *suffix) { 33 | size_t file_len, suffix_len; 34 | char *file_suffix; 35 | 36 | /* not enforcing suffix match? */ 37 | if (suffix == NULL) return 1; 38 | 39 | file_len = strlen(file); 40 | suffix_len = strlen(suffix); 41 | 42 | /* file too short for suffix match? */ 43 | if (file_len < suffix_len) return 0; 44 | 45 | file_suffix = &file[ file_len - suffix_len ]; 46 | return strcmp(file_suffix, suffix) ? 0 : 1; 47 | } 48 | 49 | int main(int argc, char *argv[]) { 50 | struct dirent *dent; 51 | int opt, rc=-1; 52 | DIR *d = NULL; 53 | char *file; 54 | 55 | CF.prog = argv[0]; 56 | 57 | while ( (opt = getopt(argc,argv,"vhd:s:")) > 0) { 58 | switch(opt) { 59 | case 'v': CF.verbose++; break; 60 | case 'd': CF.dir = strdup(optarg); break; 61 | case 's': CF.suffix = strdup(optarg); break; 62 | case 'h': default: usage(argv[0]); break; 63 | } 64 | } 65 | 66 | if (CF.dir == NULL) usage(); 67 | 68 | d = opendir(CF.dir); 69 | if (d == NULL) { 70 | fprintf(stderr, "opendir %s: %s\n", CF.dir, strerror(errno)); 71 | goto done; 72 | } 73 | 74 | while ( (dent = readdir(d)) != NULL) { 75 | file = dent->d_name; 76 | if (match_suffix(file, CF.suffix) == 0) continue; 77 | fprintf(stderr, "suffix match: %s\n", file); 78 | } 79 | 80 | rc = 0; 81 | 82 | done: 83 | if (d) closedir(d); 84 | return rc; 85 | } 86 | -------------------------------------------------------------------------------- /readdir/readdir.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char *argv[]) { 7 | int rc = -1, i = 0; 8 | char *path, *type; 9 | struct dirent *dent; 10 | DIR *d = NULL; 11 | 12 | path = (argc > 1) ? argv[1] : "."; 13 | 14 | d = opendir(path); 15 | if (d == NULL) { 16 | fprintf(stderr, "opendir %s: %s\n", path, strerror(errno)); 17 | goto done; 18 | } 19 | 20 | while ( (dent = readdir(d)) != NULL) { 21 | 22 | switch(dent->d_type) { 23 | case DT_BLK: type = "DT_BLK"; break; 24 | case DT_CHR: type = "DT_CHR"; break; 25 | case DT_DIR: type = "DT_DIR"; break; 26 | case DT_FIFO: type = "DT_FIFO"; break; 27 | case DT_LNK: type = "DT_LNK"; break; 28 | case DT_REG: type = "DT_REG"; break; 29 | case DT_SOCK: type = "DT_SOCK"; break; 30 | case DT_UNKNOWN: type = "DT_UNKNOWN"; break; 31 | default: type = "other"; break; 32 | } 33 | 34 | printf(" %d: inode %lu type %s %s\n", ++i, (unsigned long)dent->d_ino, 35 | type, dent->d_name); 36 | } 37 | 38 | printf("%d entries found.\n", i); 39 | rc = 0; 40 | 41 | done: 42 | if (d) closedir(d); 43 | return rc; 44 | } 45 | -------------------------------------------------------------------------------- /readfile/Makefile: -------------------------------------------------------------------------------- 1 | all: slurp mmap special hexdump 2 | 3 | CFLAGS=-Wall -Wextra 4 | 5 | slurp: slurp.c 6 | mmap: mmap.c 7 | hexdump: hexdump.c 8 | special: special.c 9 | 10 | .PHONY: clean 11 | 12 | clean: 13 | rm -f *.o slurp mmap special hexdump 14 | -------------------------------------------------------------------------------- /readfile/README.md: -------------------------------------------------------------------------------- 1 | Here's a link back to my [main GitHub page](http://troydhanson.github.io/). 2 | 3 | # Three ways to read a whole file in C 4 | 5 | The word "slurp" is sometimes used for reading a file all at once, in contrast 6 | to reading it line-by-line or in records. In C it is easy to slurp a file. 7 | 8 | * slurp.c: get file size, allocate buffer, read file into buffer 9 | * mmap.c: get file size, map file into memory 10 | * special.c: use realloc/read to read file 11 | 12 | ## Memory mapped files 13 | 14 | It is convenient to map a file into memory. This avoids the need to allocate 15 | a buffer and copy the file contents into the buffer. When a file is mapped, 16 | its descriptor can be closed and the mapped region remains intact. The code 17 | should eventually call `munmap()` to release the mapping. 18 | 19 | ### hexdump 20 | 21 | As an example, `hexdump.c` maps a file into memory and dumps it in hex. 22 | 23 | ## Special files 24 | 25 | For files such as those in /proc on Linux, stat reports `st_size` as 0 bytes. 26 | These files get generated programmatically only when read. For special files, 27 | we pick a buffer size, then try to read the whole file into it. If needed, 28 | realloc is used to grow the buffer until the whole file fits. 29 | 30 | -------------------------------------------------------------------------------- /readfile/hexdump.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int verbose=0; 12 | void usage(char *prog) { 13 | fprintf(stderr, "usage: %s [-v] \n", prog); 14 | exit(-1); 15 | } 16 | 17 | static void hexdump(char *buf, size_t len) { 18 | size_t i,n=0; 19 | unsigned char c; 20 | while(n < len) { 21 | fprintf(stderr,"%08x ", (int)n); 22 | for(i=0; i < 16; i++) { 23 | c = (n+i < len) ? buf[n+i] : 0; 24 | if (n+i < len) fprintf(stderr,"%.2x ", c); 25 | else fprintf(stderr, " "); 26 | } 27 | for(i=0; i < 16; i++) { 28 | c = (n+i < len) ? buf[n+i] : ' '; 29 | if (c < 0x20 || c > 0x7e) c = '.'; 30 | fprintf(stderr,"%c",c); 31 | } 32 | fprintf(stderr,"\n"); 33 | n += 16; 34 | } 35 | } 36 | 37 | char *map(char *file, size_t *len) { 38 | int fd = -1, rc = -1; 39 | char *buf = NULL; 40 | struct stat s; 41 | 42 | *len = 0; 43 | 44 | if ( (fd = open(file, O_RDONLY)) == -1) { 45 | fprintf(stderr,"open %s: %s\n", file, strerror(errno)); 46 | goto done; 47 | } 48 | 49 | if (fstat(fd, &s) == -1) { 50 | fprintf(stderr,"fstat %s: %s\n", file, strerror(errno)); 51 | goto done; 52 | } 53 | 54 | /* only files with a non-zero length can map successfully */ 55 | if (s.st_size == 0) fprintf(stderr,"%s: zero size\n", file); 56 | 57 | buf = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 58 | if (buf == MAP_FAILED) { 59 | fprintf(stderr, "mmap %s: %s\n", file, strerror(errno)); 60 | goto done; 61 | } 62 | 63 | rc = 0; 64 | *len = s.st_size; 65 | 66 | done: 67 | if (fd != -1) close(fd); 68 | if ((rc < 0) && (buf != NULL) && (buf != MAP_FAILED)) munmap(buf, s.st_size); 69 | return (rc < 0) ? NULL : buf; 70 | } 71 | 72 | int main(int argc, char * argv[]) { 73 | char *file, *buf=NULL; 74 | size_t len;; 75 | int opt; 76 | 77 | while ( (opt = getopt(argc, argv, "v+")) != -1) { 78 | switch (opt) { 79 | case 'v': 80 | verbose++; 81 | break; 82 | default: 83 | usage(argv[0]); 84 | break; 85 | } 86 | } 87 | 88 | if (optind < argc) file=argv[optind++]; 89 | else usage(argv[0]); 90 | buf = map(file, &len); 91 | if (buf == NULL) goto done; 92 | 93 | printf("mmap'd %s at %p: %lu bytes\n", file, buf, (long unsigned)len); 94 | hexdump(buf, len); 95 | 96 | done: 97 | if (buf) munmap(buf, len); 98 | } 99 | -------------------------------------------------------------------------------- /readfile/mmap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | /* mmap file, placing its size in len and returning address or NULL on error. 12 | * caller should munmap the buffer eventually. 13 | */ 14 | char *map(char *file, size_t *len) { 15 | int fd = -1, rc = -1, sc; 16 | char *buf = NULL; 17 | struct stat s; 18 | 19 | fd = open(file, O_RDONLY); 20 | if (fd < 0) { 21 | fprintf(stderr,"open: %s\n", strerror(errno)); 22 | goto done; 23 | } 24 | 25 | sc = fstat(fd, &s); 26 | if (sc < 0) { 27 | fprintf(stderr,"fstat: %s\n", strerror(errno)); 28 | goto done; 29 | } 30 | 31 | if (s.st_size == 0) { 32 | fprintf(stderr,"error: mmap zero size file\n"); 33 | goto done; 34 | } 35 | 36 | buf = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 37 | if (buf == MAP_FAILED) { 38 | fprintf(stderr, "mmap: %s\n", strerror(errno)); 39 | buf = NULL; 40 | goto done; 41 | } 42 | 43 | rc = 0; 44 | *len = s.st_size; 45 | 46 | done: 47 | if (fd != -1) close(fd); 48 | if (rc && buf) { munmap(buf, s.st_size); buf = NULL; } 49 | return buf; 50 | } 51 | 52 | int main(int argc, char * argv[]) { 53 | char *file, *buf; 54 | size_t len;; 55 | 56 | if (argc < 2) { 57 | fprintf(stderr, "usage: %s \n", argv[0]); 58 | exit(-1); 59 | } 60 | 61 | file = argv[1]; 62 | buf = map(file, &len); 63 | 64 | if (buf) { 65 | printf("mapped %s: %u bytes\n", file, (unsigned)len); 66 | munmap(buf, len); 67 | } 68 | 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /readfile/slurp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* read a file, placing its size into len, returning buffer or NULL on error. 11 | * caller should free the buffer eventually. 12 | */ 13 | char *slurp(char *file, size_t *len) { 14 | int fd = -1, rc = -1, sc; 15 | char *buf=NULL; 16 | struct stat s; 17 | ssize_t nr; 18 | 19 | sc = stat(file, &s); 20 | if (sc < 0) { 21 | fprintf(stderr,"stat: %s\n", strerror(errno)); 22 | goto done; 23 | } 24 | 25 | *len = s.st_size; 26 | fd = open(file, O_RDONLY); 27 | if (fd < 0) { 28 | fprintf(stderr,"open: %s\n", strerror(errno)); 29 | goto done; 30 | } 31 | 32 | buf = malloc(*len); 33 | if (buf == NULL) { 34 | fprintf(stderr, "out of memory\n"); 35 | goto done; 36 | } 37 | 38 | nr = read(fd, buf, *len); 39 | if (nr != (ssize_t)*len) { 40 | fprintf(stderr,"read: incomplete\n"); 41 | goto done; 42 | } 43 | 44 | rc = 0; 45 | 46 | done: 47 | if (fd != -1) close(fd); 48 | if (rc && buf) { free(buf); buf = NULL; } 49 | return buf; 50 | } 51 | 52 | int main(int argc, char * argv[]) { 53 | char *file, *buf; 54 | size_t len;; 55 | 56 | if (argc < 2) { 57 | fprintf(stderr, "usage: %s \n", argv[0]); 58 | exit(-1); 59 | } 60 | 61 | file = argv[1]; 62 | buf = slurp(file, &len); 63 | 64 | if (buf) { 65 | printf("slurped %s: %u bytes\n", file, (unsigned)len); 66 | free(buf); 67 | } 68 | 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /readfile/special.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* read a file of unknown size, such as special files in /proc. 10 | * place its size into len, returning buffer or NULL on error. 11 | * caller should free the buffer eventually. 12 | */ 13 | char *slurp_special(char *file, size_t *len) { 14 | char *buf=NULL, *b, *tmp; 15 | int fd = -1, rc = -1, eof=0; 16 | size_t sz, br=0, l; 17 | ssize_t nr; 18 | 19 | /* initial guess at a sufficient buffer size */ 20 | sz = 1000; 21 | 22 | fd = open(file, O_RDONLY); 23 | if (fd < 0) { 24 | fprintf(stderr,"open: %s\n", strerror(errno)); 25 | goto done; 26 | } 27 | 28 | while(!eof) { 29 | 30 | tmp = realloc(buf, sz); 31 | if (tmp == NULL) { 32 | fprintf(stderr, "out of memory\n"); 33 | goto done; 34 | } 35 | 36 | buf = tmp; 37 | b = buf + br; 38 | l = sz - br; 39 | 40 | do { 41 | nr = read(fd, b, l); 42 | if (nr < 0) { 43 | fprintf(stderr,"read: %s\n", strerror(errno)); 44 | goto done; 45 | } 46 | 47 | b += nr; 48 | l -= nr; 49 | br += nr; 50 | 51 | /* out of space? double buffer size */ 52 | if (l == 0) { 53 | sz *= 2; 54 | fprintf(stderr, "realloc to %lu\n", sz); 55 | break; 56 | } 57 | 58 | if (nr == 0) eof = 1; 59 | 60 | } while (nr > 0); 61 | } 62 | 63 | *len = br; 64 | rc = 0; 65 | 66 | done: 67 | if (fd != -1) close(fd); 68 | if (rc && buf) { free(buf); buf = NULL; } 69 | return buf; 70 | } 71 | 72 | int main(int argc, char * argv[]) { 73 | char *file, *buf; 74 | size_t len;; 75 | 76 | if (argc < 2) { 77 | fprintf(stderr, "usage: %s \n", argv[0]); 78 | exit(-1); 79 | } 80 | 81 | file = argv[1]; 82 | buf = slurp_special(file, &len); 83 | 84 | if (buf) { 85 | printf("slurped %s: %u bytes\n", file, (unsigned)len); 86 | free(buf); 87 | } 88 | 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /readlink/Makefile: -------------------------------------------------------------------------------- 1 | all: rdlink 2 | 3 | .PHONY: clean 4 | 5 | CFLAGS=-Wall 6 | 7 | clean: 8 | rm -f rdlink *.o 9 | -------------------------------------------------------------------------------- /readlink/README.md: -------------------------------------------------------------------------------- 1 | See rdlink.c showing usage of readlink(2) to get a symlink target. 2 | 3 | % make 4 | % ./rdlink /proc/$$/exe 5 | /proc/3086/exe -> /bin/bash 6 | 7 | -------------------------------------------------------------------------------- /readlink/rdlink.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /* example of readlink(2) */ 8 | 9 | int main(int argc, char *argv[]) { 10 | char *link, target[100]; 11 | ssize_t sc; 12 | 13 | if (argc < 2) { 14 | fprintf(stderr,"usage: %s \n", argv[0]); 15 | exit(-1); 16 | } 17 | 18 | link = argv[1]; 19 | sc = readlink(link, target, sizeof(target)); 20 | if (sc < 0) { 21 | fprintf(stderr,"readlink: %s\n", strerror(errno)); 22 | exit(-1); 23 | } 24 | 25 | if (sc == sizeof(target)) fprintf(stderr, "truncation occurred!\n"); 26 | 27 | /* print the first "sc" bytes of target. its NOT null-terminated!*/ 28 | printf("%s -> %.*s\n", link, (int)sc, target); 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /sqlite/Makefile: -------------------------------------------------------------------------------- 1 | PROGS=simple select insert create replace scan_files scan_lines 2 | OBJS=$(patsubst %,%.o,$(PROGS)) 3 | 4 | CFLAGS=-g -Wall -Wextra 5 | LDFLAGS=-lsqlite3 6 | 7 | all: $(OBJS) $(PROGS) 8 | 9 | # static pattern rule: multiple targets 10 | 11 | $(OBJS): %.o: %.c 12 | $(CC) -c $(CFLAGS) $< 13 | 14 | $(PROGS): %: %.o 15 | $(CC) -o $@ $(CFLAGS) $< $(LDFLAGS) 16 | 17 | .PHONY: clean 18 | 19 | clean: 20 | rm -f $(OBJS) $(PROGS) 21 | -------------------------------------------------------------------------------- /sqlite/README.md: -------------------------------------------------------------------------------- 1 | See https://www.sqlite.org 2 | 3 | Sqlite is probably installed on your Linux host already. You'll need to add the 4 | development headers to compile to the C API. 5 | 6 | On a typical yum based host: 7 | 8 | yum install sqlite 9 | yum install sqlite-devel 10 | 11 | Or on Ubuntu using apt: 12 | 13 | apt install sqlite3 14 | apt install libsqlite3-dev 15 | 16 | ## Sqlite3 17 | 18 | The sqlite3 binary can be used on the command line. This creates example.db: 19 | 20 | % sqlite] sqlite3 example.db 21 | sqlite> create table people (name text, age integer); 22 | 23 | sqlite> .tables 24 | people 25 | 26 | sqlite> .schema people 27 | CREATE TABLE people (name text, age integer); 28 | 29 | sqlite> insert into people values ("ben", 8); 30 | sqlite> insert into people values ("sarah", 5); 31 | 32 | sqlite> select * from people; 33 | ben|8 34 | sarah|5 35 | sqlite> .exit 36 | 37 | ## C API 38 | 39 | See simple.c in this directory. It is an example from the SQLite documentation. 40 | Run "make" to compile simple from simple.c. 41 | 42 | ./simple example.db "select * from people;" 43 | name = ben 44 | age = 8 45 | 46 | name = sarah 47 | age = 5 48 | 49 | The simple.c example uses the convenience wrapper `sqlite3_exec`. 50 | 51 | ### C examples 52 | 53 | The programs `create.c`, `insert.c`, and `select.c` show basic C API usage. 54 | 55 | % make 56 | % rm -f example.db 57 | % ./create example.db 58 | % ./insert example.db 59 | % ./select example.db 60 | name: ben age 8 61 | name: isaac age 13 62 | 63 | #### Directory scan example 64 | 65 | There is an example of scanning a directory tree and populating a table of 66 | filenames in `scan_files.c`. The first usage builds the database. The second 67 | prints it (`-p`). 68 | 69 | ./scan_files -b files.db -d /usr/share/dict 70 | ./scan_files -b files.db -p 71 | 72 | The example `scan_lines.c` takes it a step further, scanning all the files 73 | it finds in the directory tree, producing a table of line offsets in each file. 74 | (In the resulting records, sequences of newlines are reduced to one record, 75 | and leading or trailing newlines in the file are ignored). 76 | 77 | ./scan_lines -b tmp.db -d /usr/share/dict 78 | ./scan_lines -b tmp.db -p | head 79 | 80 | ## SQLite Documentation 81 | 82 | * Core C API: https://www.sqlite.org/cintro.html 83 | * SQL language: https://www.sqlite.org/lang.html 84 | * Datatypes: https://www.sqlite.org/datatype3.html 85 | 86 | ## Lessons learned 87 | 88 | ### Proceed to `SQLITE_DONE` 89 | 90 | Always let `sqlite3_step` progress until it returns `SQLITE_DONE`- even if 91 | the query can only ever return one row. The memory used to get the columns from 92 | a row is held until the row is stepped to the next row (or to `SQLITE_DONE`). 93 | Furthermore other queries can eventually show the table as locked unless you 94 | release it by stepping all the way to `SQLITE_DONE`. 95 | 96 | ### Increase busy timeout 97 | 98 | Increase the timeout for lock acquisition. I use 10 seconds. 99 | 100 | sqlite3_busy_timeout(db, 10000); 101 | 102 | Otherwise the application can give up on the lock so fast, that even a brief 103 | competing query from a user's sqlite3 session can disrupt the application. 104 | 105 | ### Use "IF NOT EXISTS" and "OR REPLACE" 106 | 107 | It's easy to create a table and its indexes if they don't already exist: 108 | 109 | CREATE TABLE IF NOT EXISTS ... 110 | CREATE INDEX IF NOT EXISTS ... 111 | 112 | ### Use "OR REPLACE" 113 | 114 | Replacing a row (where primary key of the insert matches an existing row) can 115 | be done as a REPLACE instead of a DELETE/INSERT. 116 | 117 | INSERT OR REPLACE INTO ... 118 | 119 | ### Bind indexes are 1-based, column indexes are 0-based 120 | 121 | sqlite3_bind_text(ps_del, 1, ...); # bind first value (1) 122 | sqlite3_column_int64(ps_query, 0); # get first column (0) 123 | 124 | ## Examples 125 | 126 | ## Basic 127 | 128 | A few examples of SQL to jog the memory: 129 | 130 | CREATE TABLE IF NOT EXISTS files (name TEXT PRIMARY KEY, size INTEGER); 131 | CREATE INDEX IF NOT EXISTS bysize ON files(size); 132 | DELETE FROM files WHERE name = "blue"; 133 | SELECT SUM(size) from files; 134 | INSERT OR REPLACE INTO files VALUES ("red", 100); 135 | 136 | A multi-column primary key: 137 | 138 | CREATE TABLE lines (id INTEGER, pos INTEGER, sortkey INTEGER, 139 | CONSTRAINT pk PRIMARY KEY (id, pos)); 140 | 141 | ## Join 142 | 143 | CREATE TABLE files (name TEXT PRIMARY KEY, id INTEGER); 144 | CREATE TABLE lines (id INTEGER, pos INTEGER, sortkey INTEGER); 145 | SELECT f.name, l.pos, l.sortkey FROM files f, lines l WHERE l.id = f.id 146 | 147 | ### Join with group by 148 | 149 | SELECT l.id, name, count(*) 150 | FROM lines l, files f 151 | WHERE l.id = f.id 152 | GROUP BY l.id; 153 | 154 | -------------------------------------------------------------------------------- /sqlite/create.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* 5 | an example of creating a table using SQL's CREATE table 6 | 7 | */ 8 | 9 | int exec_sql(sqlite3 *db, char *sql) { 10 | sqlite3_stmt *ppStmt=NULL; 11 | int sc, rc = -1; 12 | 13 | sc = sqlite3_prepare_v2(db, sql, -1, &ppStmt, NULL); 14 | if( sc!=SQLITE_OK ){ 15 | fprintf(stderr, "sqlite3_prepare: %s\n", sqlite3_errstr(sc)); 16 | goto done; 17 | } 18 | 19 | sc = sqlite3_step(ppStmt); 20 | switch(sc) { 21 | case SQLITE_ROW: 22 | fprintf(stderr, "more results -- unexpected\n"); 23 | break; 24 | case SQLITE_DONE: 25 | break; 26 | default: 27 | fprintf(stderr,"sqlite3_step: error %s\n", sqlite3_errstr(sc)); 28 | goto done; 29 | break; 30 | } 31 | 32 | sc = sqlite3_finalize(ppStmt); 33 | if (sc != SQLITE_OK) { 34 | fprintf(stderr,"sqlite3_finalize: %s\n", sqlite3_errstr(sc)); 35 | goto done; 36 | } 37 | 38 | 39 | rc = 0; 40 | 41 | done: 42 | return rc; 43 | } 44 | 45 | int main(int argc, char **argv){ 46 | int rc=-1, sc; 47 | sqlite3 *db=NULL; 48 | char *sql; 49 | 50 | if( argc<2 ){ 51 | fprintf(stderr, "Usage: %s DATABASE\n", argv[0]); 52 | goto done; 53 | } 54 | 55 | sc = sqlite3_open(argv[1], &db); 56 | if( sc ){ 57 | fprintf(stderr, "sqlite3_open: %s\n", sqlite3_errstr(sc)); 58 | goto done; 59 | } 60 | 61 | sql = "CREATE TABLE people (name TEXT PRIMARY KEY, age INTEGER);"; 62 | if (exec_sql(db, sql) < 0) goto done; 63 | 64 | rc = 0; 65 | 66 | done: 67 | sqlite3_close(db); // NULL allowed 68 | return rc; 69 | } 70 | -------------------------------------------------------------------------------- /sqlite/insert.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* 5 | 6 | This example of using sqlite uses the function sequence: 7 | 8 | open/prepare/bind/step/close 9 | 10 | to insert values into the database. 11 | 12 | To try it, first create the example.db: 13 | 14 | % sqlite3 example.db 15 | sqlite> create table people (name text, age integer); 16 | sqlite> .exit 17 | 18 | Then run this program to insert two rows: 19 | 20 | % ./insert example.db 21 | 22 | Then query the table: 23 | 24 | % ./select example.db 25 | */ 26 | 27 | int reset(sqlite3_stmt *ppStmt) { 28 | int sc, rc = -1; 29 | 30 | sc = sqlite3_reset(ppStmt); 31 | if (sc != SQLITE_OK) { 32 | fprintf(stderr,"sqlite3_reset: %s\n", sqlite3_errstr(sc)); 33 | goto done; 34 | } 35 | 36 | sc = sqlite3_clear_bindings(ppStmt); 37 | if (sc != SQLITE_OK) { 38 | fprintf(stderr,"sqlite3_clear_bindings: %s\n", sqlite3_errstr(sc)); 39 | goto done; 40 | } 41 | 42 | rc = 0; 43 | 44 | done: 45 | return rc; 46 | } 47 | 48 | int do_insert(sqlite3_stmt *ppStmt, char *name, int age) { 49 | int sc, rc = -1; 50 | 51 | sc = sqlite3_bind_text(ppStmt, 1, name, -1, SQLITE_TRANSIENT); 52 | if (sc != SQLITE_OK) { 53 | fprintf(stderr, "sqlite3_bind_text: %s\n", sqlite3_errstr(sc)); 54 | goto done; 55 | } 56 | 57 | sc = sqlite3_bind_int( ppStmt, 2, age); 58 | if (sc != SQLITE_OK) { 59 | fprintf(stderr, "sqlite3_bind_int: %s\n", sqlite3_errstr(sc)); 60 | goto done; 61 | } 62 | 63 | sc = sqlite3_step(ppStmt); 64 | switch(sc) { 65 | case SQLITE_ROW: 66 | fprintf(stderr, "more results -- unexpected on insert!\n"); 67 | break; 68 | case SQLITE_DONE: 69 | /* normal- reset but do not finalize til all inserts done */ 70 | if (reset(ppStmt) < 0) goto done; 71 | break; 72 | default: 73 | fprintf(stderr,"sqlite3_step: error %s\n", sqlite3_errstr(sc)); 74 | goto done; 75 | break; 76 | } 77 | 78 | rc = 0; 79 | 80 | done: 81 | return rc; 82 | } 83 | 84 | int main(int argc, char **argv){ 85 | sqlite3_stmt *ppStmt; 86 | int rc=-1, sc; 87 | sqlite3 *db=NULL; 88 | char *sql; 89 | 90 | if( argc<2 ){ 91 | fprintf(stderr, "Usage: %s DATABASE\n", argv[0]); 92 | goto done; 93 | } 94 | 95 | sc = sqlite3_open(argv[1], &db); 96 | if( sc ){ 97 | fprintf(stderr, "sqlite3_open: %s\n", sqlite3_errmsg(db)); 98 | goto done; 99 | } 100 | 101 | /* prepare statement - we'll substitute values into */ 102 | sql = "insert into people values ($NAME, $AGE);"; 103 | sc = sqlite3_prepare_v2(db, sql, -1, &ppStmt, NULL); 104 | if( sc!=SQLITE_OK ){ 105 | fprintf(stderr, "sqlite3_prepare: %s\n", sqlite3_errstr(sc)); 106 | goto done; 107 | } 108 | 109 | /* insert rows */ 110 | if (do_insert(ppStmt, "ben", 8) < 0) goto done; 111 | if (do_insert(ppStmt, "isaac", 13) < 0) goto done; 112 | 113 | /* done with statement */ 114 | sc = sqlite3_finalize(ppStmt); 115 | if (sc != SQLITE_OK) { 116 | fprintf(stderr,"sqlite3_finalize: %s\n", sqlite3_errstr(sc)); 117 | goto done; 118 | } 119 | 120 | rc = 0; 121 | 122 | done: 123 | sqlite3_close(db); // NULL allowed 124 | return rc; 125 | } 126 | -------------------------------------------------------------------------------- /sqlite/replace.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* 5 | 6 | An example of creating a table using CREATE TABLE IF NOT EXISTS, 7 | 8 | "IF NOT EXISTS" 9 | 10 | see https://www.sqlite.org/lang_createtable.html 11 | 12 | When this program inserts two rows having the same primary key value, 13 | using INSERT OR REPLACE it overwrites the existing row. 14 | 15 | "INSERT OR REPLACE" 16 | 17 | see https://www.sqlite.org/lang_insert.html 18 | see https://www.sqlite.org/lang_conflict.html 19 | */ 20 | 21 | int exec_sql(sqlite3 *db, char *sql) { 22 | sqlite3_stmt *ppStmt=NULL; 23 | int sc, rc = -1; 24 | 25 | fprintf(stderr, "Executing %s\n", sql); 26 | 27 | sc = sqlite3_prepare_v2(db, sql, -1, &ppStmt, NULL); 28 | if( sc!=SQLITE_OK ){ 29 | fprintf(stderr, "sqlite3_prepare: %s\n", sqlite3_errstr(sc)); 30 | goto done; 31 | } 32 | 33 | sc = sqlite3_step(ppStmt); 34 | switch(sc) { 35 | case SQLITE_ROW: 36 | fprintf(stderr, "more results -- unexpected\n"); 37 | break; 38 | case SQLITE_DONE: 39 | break; 40 | default: 41 | fprintf(stderr,"sqlite3_step: error %s\n", sqlite3_errstr(sc)); 42 | goto done; 43 | break; 44 | } 45 | 46 | sc = sqlite3_finalize(ppStmt); 47 | if (sc != SQLITE_OK) { 48 | fprintf(stderr,"sqlite3_finalize: %s\n", sqlite3_errstr(sc)); 49 | goto done; 50 | } 51 | 52 | 53 | rc = 0; 54 | 55 | done: 56 | return rc; 57 | } 58 | 59 | int main(int argc, char **argv){ 60 | int rc=-1, sc; 61 | sqlite3 *db=NULL; 62 | char *sql; 63 | 64 | if( argc<2 ){ 65 | fprintf(stderr, "Usage: %s DATABASE\n", argv[0]); 66 | goto done; 67 | } 68 | 69 | sc = sqlite3_open(argv[1], &db); 70 | if( sc ){ 71 | fprintf(stderr, "sqlite3_open: %s\n", sqlite3_errmsg(db)); 72 | goto done; 73 | } 74 | 75 | sql = "CREATE TABLE IF NOT EXISTS people (name TEXT PRIMARY KEY, age INTEGER);"; 76 | if (exec_sql(db, sql) < 0) goto done; 77 | 78 | sql = "INSERT OR REPLACE INTO people VALUES (\"isaac\", 12);"; 79 | if (exec_sql(db, sql) < 0) goto done; 80 | 81 | sql = "INSERT OR REPLACE INTO people VALUES (\"isaac\", 13);"; 82 | if (exec_sql(db, sql) < 0) goto done; 83 | 84 | rc = 0; 85 | 86 | done: 87 | sqlite3_close(db); // NULL allowed 88 | return rc; 89 | } 90 | -------------------------------------------------------------------------------- /sqlite/scan_files.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /* 15 | * populate table from a directory tree 16 | * 17 | * this is an example of building a table 18 | * from a recursive directory scan. it is 19 | * useful as a starting point - this demo 20 | * inserts only the filenames and an id 21 | * 22 | */ 23 | 24 | struct { 25 | int verbose; 26 | char *prog; 27 | enum {mode_build, mode_print} mode; 28 | 29 | /* file structure to index */ 30 | char dir[PATH_MAX]; 31 | int recurse; 32 | char *suffix; 33 | int file_id; 34 | 35 | /* db info */ 36 | char *db_name; 37 | sqlite3 *db; 38 | sqlite3_stmt *insert_stmt; 39 | sqlite3_stmt *select_stmt; 40 | int truncate; 41 | 42 | 43 | } CF = { 44 | .mode = mode_build, 45 | .recurse = 1, 46 | .truncate = 1, 47 | }; 48 | 49 | void usage() { 50 | fprintf(stderr, "usage: %s [options] -b \n", CF.prog); 51 | fprintf(stderr, "\n"); 52 | fprintf(stderr, "build mode (default):\n"); 53 | fprintf(stderr, " -d (directory root to scan)\n"); 54 | fprintf(stderr, " -r [0|1] (scan recursively; default: 1)\n"); 55 | fprintf(stderr, " -t [0|1] (truncate db; default: 1)\n"); 56 | fprintf(stderr, " -s (only files matching suffix)\n"); 57 | fprintf(stderr, "\n"); 58 | fprintf(stderr, "print mode:\n"); 59 | fprintf(stderr, " -p (print db)\n"); 60 | fprintf(stderr, "\n"); 61 | fprintf(stderr, "general options:\n"); 62 | fprintf(stderr, " -v (verbose, repeatable)\n"); 63 | fprintf(stderr, "\n"); 64 | exit(-1); 65 | } 66 | 67 | /* helper function for create statements that return no row */ 68 | int exec_sql(sqlite3 *db, char *sql) { 69 | sqlite3_stmt *ps=NULL; 70 | int sc, rc = -1; 71 | 72 | if (CF.verbose) fprintf(stderr, "executing SQL: %s\n", sql); 73 | 74 | sc = sqlite3_prepare_v2(db, sql, -1, &ps, NULL); 75 | if (sc != SQLITE_OK ){ 76 | fprintf(stderr, "sqlite3_prepare: %s\n", sqlite3_errstr(sc)); 77 | goto done; 78 | } 79 | 80 | sc = sqlite3_step(ps); 81 | if (sc != SQLITE_DONE) { 82 | fprintf(stderr, "sqlite3_step: result unexpected\n"); 83 | goto done; 84 | } 85 | 86 | sc = sqlite3_finalize(ps); 87 | if (sc != SQLITE_OK) { 88 | fprintf(stderr,"sqlite3_finalize: %s\n", sqlite3_errstr(sc)); 89 | goto done; 90 | } 91 | 92 | rc = 0; 93 | 94 | done: 95 | return rc; 96 | } 97 | 98 | int setup_db(void) { 99 | int sc, rc = -1; 100 | char *sql; 101 | 102 | sc = sqlite3_open(CF.db_name, &CF.db); 103 | if( sc ){ 104 | fprintf(stderr, "sqlite3_open: %s\n", sqlite3_errstr(sc)); 105 | goto done; 106 | } 107 | 108 | /* create table */ 109 | sql = "CREATE TABLE IF NOT EXISTS files (name TEXT PRIMARY KEY, id INTEGER);"; 110 | if (exec_sql(CF.db, sql) < 0) goto done; 111 | 112 | /* truncate optionally */ 113 | if ((CF.mode == mode_build) && CF.truncate) { 114 | sql = "DELETE FROM files;"; 115 | if (exec_sql(CF.db, sql) < 0) goto done; 116 | } 117 | 118 | /* prepare insert statement - we substitute values in later */ 119 | sql = "insert into files values ($NAME, $ID);"; 120 | sc = sqlite3_prepare_v2(CF.db, sql, -1, &CF.insert_stmt, NULL); 121 | if( sc!=SQLITE_OK ){ 122 | fprintf(stderr, "sqlite3_prepare: %s\n", sqlite3_errstr(sc)); 123 | goto done; 124 | } 125 | 126 | /* prepare select statement */ 127 | sql = "select name, id from files;"; 128 | sc = sqlite3_prepare_v2(CF.db, sql, -1, &CF.select_stmt, NULL); 129 | if( sc!=SQLITE_OK ){ 130 | fprintf(stderr, "sqlite3_prepare: %s\n", sqlite3_errstr(sc)); 131 | goto done; 132 | } 133 | 134 | rc = 0; 135 | 136 | done: 137 | return rc; 138 | } 139 | 140 | int print_db(void) { 141 | int rc = -1, id, file_count=0; 142 | const unsigned char *name; 143 | 144 | while (sqlite3_step(CF.select_stmt) == SQLITE_ROW) { 145 | name = sqlite3_column_text(CF.select_stmt, 0); 146 | id = sqlite3_column_int(CF.select_stmt, 1); 147 | printf("%s %d\n", name, id); 148 | file_count++; 149 | } 150 | 151 | printf("%d files\n", file_count); 152 | 153 | rc = 0; 154 | 155 | return rc; 156 | } 157 | 158 | int reset(sqlite3_stmt *ps) { 159 | int sc, rc = -1; 160 | 161 | sc = sqlite3_reset(ps); 162 | if (sc != SQLITE_OK) { 163 | fprintf(stderr,"sqlite3_reset: %s\n", sqlite3_errstr(sc)); 164 | goto done; 165 | } 166 | 167 | sc = sqlite3_clear_bindings(ps); 168 | if (sc != SQLITE_OK) { 169 | fprintf(stderr,"sqlite3_clear_bindings: %s\n", sqlite3_errstr(sc)); 170 | goto done; 171 | } 172 | 173 | rc = 0; 174 | 175 | done: 176 | return rc; 177 | } 178 | 179 | int insert_file(sqlite3_stmt *ps, char *name, int id) { 180 | int sc, rc = -1; 181 | 182 | if (CF.verbose) fprintf(stderr, "inserting %s id %d\n", name, id); 183 | 184 | sc = sqlite3_bind_text(ps, 1, name, -1, SQLITE_TRANSIENT); 185 | if (sc != SQLITE_OK) { 186 | fprintf(stderr, "sqlite3_bind_text: %s\n", sqlite3_errstr(sc)); 187 | goto done; 188 | } 189 | 190 | sc = sqlite3_bind_int( ps, 2, id); 191 | if (sc != SQLITE_OK) { 192 | fprintf(stderr, "sqlite3_bind_int: %s\n", sqlite3_errstr(sc)); 193 | goto done; 194 | } 195 | 196 | /* insert */ 197 | sc = sqlite3_step(ps); 198 | if (sc != SQLITE_DONE) { 199 | fprintf(stderr,"sqlite3_step: unexpected result\n"); 200 | goto done; 201 | } 202 | 203 | if (reset(ps) < 0) goto done; 204 | 205 | rc = 0; 206 | 207 | done: 208 | return rc; 209 | } 210 | 211 | int add_file(char *file) { 212 | int rc = -1, sc; 213 | 214 | /* add file reference to file table */ 215 | sc = insert_file(CF.insert_stmt, file, CF.file_id); 216 | if (sc < 0) goto done; 217 | CF.file_id++; 218 | 219 | rc = 0; 220 | 221 | done: 222 | return rc; 223 | } 224 | 225 | int is_suffix(char *file) { 226 | size_t file_len, suffix_len; 227 | char *file_suffix; 228 | 229 | /* not enforcing suffix match? */ 230 | if (CF.suffix == NULL) return 1; 231 | 232 | file_len = strlen(file); 233 | suffix_len = strlen(CF.suffix); 234 | 235 | /* file too short for suffix match? */ 236 | if (file_len < suffix_len) return 0; 237 | 238 | file_suffix = &file[ file_len - suffix_len ]; 239 | return strcmp(file_suffix, CF.suffix) ? 0 : 1; 240 | } 241 | 242 | /* add directory to tree, recursively by option. 243 | * function recursion depth bounded by fs depth 244 | * 245 | * returns 246 | * < 0 on error 247 | * 0 success 248 | */ 249 | int add_dir(char *dir) { 250 | char path[PATH_MAX]; 251 | struct dirent *dent; 252 | int rc = -1, ec; 253 | DIR *d = NULL; 254 | struct stat s; 255 | size_t l, el; 256 | 257 | if (CF.verbose) fprintf(stderr, "adding directory %s\n", dir); 258 | 259 | l = strlen(dir); 260 | d = opendir(dir); 261 | if (d == NULL) { 262 | fprintf(stderr, "opendir %s: %s\n", dir, strerror(errno)); 263 | goto done; 264 | } 265 | 266 | /* iterate over directory contents. use stat to distinguish regular files 267 | * from directories (etc). stat is more robust than using dent->d_type */ 268 | while ( (dent = readdir(d)) != NULL) { 269 | 270 | /* skip the . and .. directories */ 271 | if (!strcmp(dent->d_name, ".")) continue; 272 | if (!strcmp(dent->d_name, "..")) continue; 273 | 274 | /* formulate path to dir entry */ 275 | el = strlen(dent->d_name); 276 | if (l+1+el+1 > PATH_MAX) { 277 | fprintf(stderr, "path too long: %s/%s\n", dir, dent->d_name); 278 | goto done; 279 | } 280 | memcpy(path, dir, l); 281 | path[l] = '/'; 282 | memcpy(&path[l+1], dent->d_name, el+1); 283 | 284 | /* lstat to determine its type */ 285 | ec = lstat(path, &s); 286 | if (ec < 0) { 287 | fprintf(stderr, "lstat %s: %s\n", path, strerror(errno)); 288 | goto done; 289 | } 290 | 291 | if (S_ISREG(s.st_mode) && is_suffix(path)) { 292 | if (add_file(path) < 0) goto done; 293 | if (CF.verbose) fprintf(stderr, "adding regular file %s\n", path); 294 | } 295 | 296 | if (CF.recurse && (S_ISDIR(s.st_mode))) { 297 | if (add_dir(path) < 0) goto done; 298 | } 299 | } 300 | 301 | rc = 0; 302 | 303 | done: 304 | if (d) closedir(d); 305 | return rc; 306 | } 307 | 308 | int release_db(void) { 309 | int sc, rc = -1; 310 | 311 | /* done with select statement */ 312 | sc = sqlite3_finalize(CF.select_stmt); 313 | if (sc != SQLITE_OK) { 314 | fprintf(stderr,"sqlite3_finalize: %s\n", sqlite3_errstr(sc)); 315 | goto done; 316 | } 317 | 318 | /* done with insert statement */ 319 | sc = sqlite3_finalize(CF.insert_stmt); 320 | if (sc != SQLITE_OK) { 321 | fprintf(stderr,"sqlite3_finalize: %s\n", sqlite3_errstr(sc)); 322 | goto done; 323 | } 324 | 325 | sqlite3_close(CF.db); // NULL allowed 326 | 327 | rc = 0; 328 | 329 | done: 330 | return rc; 331 | } 332 | 333 | int main(int argc, char *argv[]) { 334 | char *dir = NULL; 335 | int opt, rc=-1; 336 | 337 | CF.prog = argv[0]; 338 | 339 | while ( (opt = getopt(argc,argv,"vphd:r:t:s:b:")) > 0) { 340 | switch(opt) { 341 | case 'v': CF.verbose++; break; 342 | case 'd': dir = strdup(optarg); break; 343 | case 'r': CF.recurse=atoi(optarg); break; 344 | case 't': CF.truncate=atoi(optarg); break; 345 | case 'p': CF.mode = mode_print; break; 346 | case 's': CF.suffix = strdup(optarg); break; 347 | case 'b': CF.db_name = strdup(optarg); break; 348 | case 'h': default: usage(argv[0]); break; 349 | } 350 | } 351 | 352 | if (CF.db_name == NULL) usage(); 353 | 354 | /* set up database */ 355 | if (setup_db() < 0) goto done; 356 | 357 | switch (CF.mode) { 358 | case mode_build: 359 | if (dir == NULL) usage(); 360 | if (realpath(dir, CF.dir) == NULL) { 361 | fprintf(stderr, "realpath %s: %s\n", dir, strerror(errno)); 362 | goto done; 363 | } 364 | if (add_dir(CF.dir) < 0) goto done; 365 | break; 366 | case mode_print: 367 | if (print_db() < 0) goto done; 368 | break; 369 | default: 370 | assert(0); 371 | goto done; 372 | break; 373 | } 374 | 375 | rc = 0; 376 | 377 | done: 378 | if (dir) free(dir); 379 | if (CF.suffix) free(CF.suffix); 380 | if (CF.db_name) free(CF.db_name); 381 | release_db(); 382 | return rc; 383 | } 384 | -------------------------------------------------------------------------------- /sqlite/scan_lines.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | /* 17 | * populate table from a directory tree 18 | * 19 | * this is an example of building a table 20 | * from a recursive directory scan. 21 | * there are two tables: the files table 22 | * and the lines table. 23 | * 24 | */ 25 | 26 | struct { 27 | int verbose; 28 | char *prog; 29 | enum {mode_build, mode_print} mode; 30 | 31 | /* file structure to index */ 32 | char dir[PATH_MAX]; 33 | int recurse; 34 | char *suffix; 35 | int file_id; 36 | 37 | /* db info */ 38 | char *db_name; 39 | sqlite3 *db; 40 | sqlite3_stmt *insert_stmt; 41 | sqlite3_stmt *select_stmt; 42 | sqlite3_stmt *insert_rcrd; 43 | int truncate; 44 | 45 | 46 | } CF = { 47 | .mode = mode_build, 48 | .recurse = 1, 49 | .truncate = 1, 50 | }; 51 | 52 | void usage() { 53 | fprintf(stderr, "usage: %s [options] -b \n", CF.prog); 54 | fprintf(stderr, "\n"); 55 | fprintf(stderr, "build mode (default):\n"); 56 | fprintf(stderr, " -d (directory root to scan)\n"); 57 | fprintf(stderr, " -r [0|1] (scan recursively; default: 1)\n"); 58 | fprintf(stderr, " -t [0|1] (truncate db; default: 1)\n"); 59 | fprintf(stderr, " -s (only files matching suffix)\n"); 60 | fprintf(stderr, "\n"); 61 | fprintf(stderr, "print mode:\n"); 62 | fprintf(stderr, " -p (print db)\n"); 63 | fprintf(stderr, "\n"); 64 | fprintf(stderr, "general options:\n"); 65 | fprintf(stderr, " -v (verbose, repeatable)\n"); 66 | fprintf(stderr, "\n"); 67 | exit(-1); 68 | } 69 | 70 | /* helper function for create statements that return no row */ 71 | int exec_sql(sqlite3 *db, char *sql) { 72 | sqlite3_stmt *ps=NULL; 73 | int sc, rc = -1; 74 | 75 | if (CF.verbose) fprintf(stderr, "executing SQL: %s\n", sql); 76 | 77 | sc = sqlite3_prepare_v2(db, sql, -1, &ps, NULL); 78 | if (sc != SQLITE_OK ){ 79 | fprintf(stderr, "sqlite3_prepare: %s\n", sqlite3_errstr(sc)); 80 | goto done; 81 | } 82 | 83 | sc = sqlite3_step(ps); 84 | if (sc != SQLITE_DONE) { 85 | fprintf(stderr, "sqlite3_step: result unexpected\n"); 86 | goto done; 87 | } 88 | 89 | sc = sqlite3_finalize(ps); 90 | if (sc != SQLITE_OK) { 91 | fprintf(stderr,"sqlite3_finalize: %s\n", sqlite3_errstr(sc)); 92 | goto done; 93 | } 94 | 95 | rc = 0; 96 | 97 | done: 98 | return rc; 99 | } 100 | 101 | int setup_db(void) { 102 | int sc, rc = -1; 103 | char *sql; 104 | 105 | sc = sqlite3_open(CF.db_name, &CF.db); 106 | if( sc ){ 107 | fprintf(stderr, "sqlite3_open: %s\n", sqlite3_errstr(sc)); 108 | goto done; 109 | } 110 | 111 | /* 112 | * files table 113 | */ 114 | sql = "CREATE TABLE IF NOT EXISTS files (name TEXT PRIMARY KEY, id INTEGER);"; 115 | if (exec_sql(CF.db, sql) < 0) goto done; 116 | 117 | /* truncate optionally */ 118 | if ((CF.mode == mode_build) && CF.truncate) { 119 | sql = "DELETE FROM files;"; 120 | if (exec_sql(CF.db, sql) < 0) goto done; 121 | } 122 | 123 | /* prepare insert statement - we substitute values in later */ 124 | sql = "insert into files values ($NAME, $ID);"; 125 | sc = sqlite3_prepare_v2(CF.db, sql, -1, &CF.insert_stmt, NULL); 126 | if( sc!=SQLITE_OK ){ 127 | fprintf(stderr, "sqlite3_prepare: %s\n", sqlite3_errstr(sc)); 128 | goto done; 129 | } 130 | 131 | /* 132 | * lines table 133 | */ 134 | sql = "CREATE TABLE IF NOT EXISTS lines " 135 | "(id INTEGER, pos INTEGER, sortkey INTEGER);"; 136 | if (exec_sql(CF.db, sql) < 0) goto done; 137 | 138 | /* truncate optionally */ 139 | if ((CF.mode == mode_build) && CF.truncate) { 140 | sql = "DELETE FROM lines;"; 141 | if (exec_sql(CF.db, sql) < 0) goto done; 142 | } 143 | 144 | /* index */ 145 | sql = "CREATE INDEX IF NOT EXISTS bykey ON lines(sortkey);"; 146 | if (exec_sql(CF.db, sql) < 0) goto done; 147 | 148 | /* prepare select statement */ 149 | sql = "select f.name, l.pos, l.sortkey from files f, lines l " 150 | "where l.id = f.id;"; 151 | sc = sqlite3_prepare_v2(CF.db, sql, -1, &CF.select_stmt, NULL); 152 | if( sc!=SQLITE_OK ){ 153 | fprintf(stderr, "sqlite3_prepare: %s\n", sqlite3_errstr(sc)); 154 | goto done; 155 | } 156 | 157 | /* prepare insert statement - we substitute values in later */ 158 | sql = "insert into lines values ($ID, $POS, $SORTKEY);"; 159 | sc = sqlite3_prepare_v2(CF.db, sql, -1, &CF.insert_rcrd, NULL); 160 | if( sc!=SQLITE_OK ){ 161 | fprintf(stderr, "sqlite3_prepare: %s\n", sqlite3_errstr(sc)); 162 | goto done; 163 | } 164 | 165 | rc = 0; 166 | 167 | done: 168 | return rc; 169 | } 170 | 171 | int print_db(void) { 172 | const unsigned char *name; 173 | int rc = -1, pos, num; 174 | 175 | while (sqlite3_step(CF.select_stmt) == SQLITE_ROW) { 176 | name = sqlite3_column_text(CF.select_stmt, 0); 177 | num = sqlite3_column_int(CF.select_stmt, 2); 178 | pos = sqlite3_column_int(CF.select_stmt, 1); 179 | printf("%s: line %d: byte: 0x%x\n", name, num, pos); 180 | } 181 | 182 | rc = 0; 183 | 184 | return rc; 185 | } 186 | 187 | int reset(sqlite3_stmt *ps) { 188 | int sc, rc = -1; 189 | 190 | sc = sqlite3_reset(ps); 191 | if (sc != SQLITE_OK) { 192 | fprintf(stderr,"sqlite3_reset: %s\n", sqlite3_errstr(sc)); 193 | goto done; 194 | } 195 | 196 | sc = sqlite3_clear_bindings(ps); 197 | if (sc != SQLITE_OK) { 198 | fprintf(stderr,"sqlite3_clear_bindings: %s\n", sqlite3_errstr(sc)); 199 | goto done; 200 | } 201 | 202 | rc = 0; 203 | 204 | done: 205 | return rc; 206 | } 207 | 208 | /* maps a file into memory. caller should clean up 209 | * by calling munmap(buf,len) when done with file */ 210 | char *map(char *file, size_t *len) { 211 | int fd = -1, rc = -1; 212 | char *buf = NULL; 213 | struct stat s; 214 | 215 | *len = 0; 216 | 217 | if ( (fd = open(file, O_RDONLY)) == -1) { 218 | fprintf(stderr,"open %s: %s\n", file, strerror(errno)); 219 | goto done; 220 | } 221 | 222 | if (fstat(fd, &s) == -1) { 223 | fprintf(stderr,"fstat %s: %s\n", file, strerror(errno)); 224 | goto done; 225 | } 226 | 227 | buf = (s.st_size > 0) ? 228 | mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0) : 229 | NULL; 230 | if (buf == MAP_FAILED) { 231 | fprintf(stderr, "mmap %s: %s\n", file, strerror(errno)); 232 | goto done; 233 | } 234 | 235 | rc = 0; 236 | *len = s.st_size; 237 | 238 | done: 239 | if (fd != -1) close(fd); 240 | if ((rc < 0) && (buf != NULL) && (buf != MAP_FAILED)) munmap(buf, s.st_size); 241 | return (rc < 0) ? NULL : buf; 242 | } 243 | 244 | 245 | int insert_file(sqlite3_stmt *ps, char *name, int id) { 246 | int sc, rc = -1; 247 | 248 | sc = sqlite3_bind_text(ps, 1, name, -1, SQLITE_TRANSIENT); 249 | if (sc != SQLITE_OK) { 250 | fprintf(stderr, "sqlite3_bind_text: %s\n", sqlite3_errstr(sc)); 251 | goto done; 252 | } 253 | 254 | sc = sqlite3_bind_int( ps, 2, id); 255 | if (sc != SQLITE_OK) { 256 | fprintf(stderr, "sqlite3_bind_int: %s\n", sqlite3_errstr(sc)); 257 | goto done; 258 | } 259 | 260 | /* insert */ 261 | sc = sqlite3_step(ps); 262 | if (sc != SQLITE_DONE) { 263 | fprintf(stderr,"sqlite3_step: unexpected result\n"); 264 | goto done; 265 | } 266 | 267 | if (reset(ps) < 0) goto done; 268 | 269 | rc = 0; 270 | 271 | done: 272 | return rc; 273 | } 274 | 275 | int insert_line(sqlite3_stmt *ps, int id, size_t pos, int key) { 276 | int sc, rc = -1; 277 | 278 | sc = sqlite3_bind_int( ps, 1, id); 279 | if (sc != SQLITE_OK) { 280 | fprintf(stderr, "sqlite3_bind_int: %s\n", sqlite3_errstr(sc)); 281 | goto done; 282 | } 283 | 284 | sc = sqlite3_bind_int( ps, 2, pos); 285 | if (sc != SQLITE_OK) { 286 | fprintf(stderr, "sqlite3_bind_int: %s\n", sqlite3_errstr(sc)); 287 | goto done; 288 | } 289 | 290 | sc = sqlite3_bind_int( ps, 3, key); 291 | if (sc != SQLITE_OK) { 292 | fprintf(stderr, "sqlite3_bind_int: %s\n", sqlite3_errstr(sc)); 293 | goto done; 294 | } 295 | 296 | /* insert */ 297 | sc = sqlite3_step(ps); 298 | if (sc != SQLITE_DONE) { 299 | fprintf(stderr,"sqlite3_step: unexpected result\n"); 300 | goto done; 301 | } 302 | 303 | if (reset(ps) < 0) goto done; 304 | 305 | rc = 0; 306 | 307 | done: 308 | return rc; 309 | } 310 | 311 | /* scan file and enter its lines into table */ 312 | int get_lines(sqlite3_stmt *ps, char *file, int id) { 313 | char *buf=NULL; 314 | int sc, rc = -1, key=0; 315 | size_t len; 316 | 317 | /* if this call fails, because the file has 0-length, (etc), 318 | it will give us a NULL buf and zero len; rest is no-op */ 319 | buf = map(file, &len); 320 | 321 | /* in this example we're looking for line delimiters */ 322 | char *p = buf; 323 | char *eob = buf + len; 324 | while(p < eob) { 325 | while((*p == '\n') && (p < eob)) p++; /* squeeze leading newlines */ 326 | if (p < eob) { 327 | sc = insert_line(ps, id, p-buf, key); 328 | if (sc < 0) goto done; 329 | key++; 330 | } 331 | while((*p != '\n') && (p < eob)) p++; /* find end of current line */ 332 | } 333 | 334 | rc = 0; 335 | 336 | done: 337 | if (buf) munmap(buf, len); 338 | return rc; 339 | } 340 | 341 | int add_file(char *file) { 342 | int rc = -1, sc; 343 | 344 | /* add file reference to file table */ 345 | sc = insert_file(CF.insert_stmt, file, CF.file_id); 346 | if (sc < 0) goto done; 347 | 348 | /* open the file up, find lines, insert them */ 349 | sc = get_lines(CF.insert_rcrd, file, CF.file_id); 350 | if (sc < 0) goto done; 351 | 352 | CF.file_id++; 353 | 354 | rc = 0; 355 | 356 | done: 357 | return rc; 358 | } 359 | 360 | int is_suffix(char *file) { 361 | size_t file_len, suffix_len; 362 | char *file_suffix; 363 | 364 | /* not enforcing suffix match? */ 365 | if (CF.suffix == NULL) return 1; 366 | 367 | file_len = strlen(file); 368 | suffix_len = strlen(CF.suffix); 369 | 370 | /* file too short for suffix match? */ 371 | if (file_len < suffix_len) return 0; 372 | 373 | file_suffix = &file[ file_len - suffix_len ]; 374 | return strcmp(file_suffix, CF.suffix) ? 0 : 1; 375 | } 376 | 377 | /* add directory to tree, recursively by option. 378 | * function recursion depth bounded by fs depth 379 | * 380 | * returns 381 | * < 0 on error 382 | * 0 success 383 | */ 384 | int add_dir(char *dir) { 385 | char path[PATH_MAX]; 386 | struct dirent *dent; 387 | int rc = -1, ec; 388 | DIR *d = NULL; 389 | struct stat s; 390 | size_t l, el; 391 | 392 | if (CF.verbose) fprintf(stderr, "adding directory %s\n", dir); 393 | 394 | l = strlen(dir); 395 | d = opendir(dir); 396 | if (d == NULL) { 397 | fprintf(stderr, "opendir %s: %s\n", dir, strerror(errno)); 398 | goto done; 399 | } 400 | 401 | /* iterate over directory contents. use stat to distinguish regular files 402 | * from directories (etc). stat is more robust than using dent->d_type */ 403 | while ( (dent = readdir(d)) != NULL) { 404 | 405 | /* skip the . and .. directories */ 406 | if (!strcmp(dent->d_name, ".")) continue; 407 | if (!strcmp(dent->d_name, "..")) continue; 408 | 409 | /* formulate path to dir entry */ 410 | el = strlen(dent->d_name); 411 | if (l+1+el+1 > PATH_MAX) { 412 | fprintf(stderr, "path too long: %s/%s\n", dir, dent->d_name); 413 | goto done; 414 | } 415 | memcpy(path, dir, l); 416 | path[l] = '/'; 417 | memcpy(&path[l+1], dent->d_name, el+1); 418 | 419 | /* lstat to determine its type */ 420 | ec = lstat(path, &s); 421 | if (ec < 0) { 422 | fprintf(stderr, "lstat %s: %s\n", path, strerror(errno)); 423 | goto done; 424 | } 425 | 426 | if (S_ISREG(s.st_mode) && is_suffix(path)) { 427 | if (add_file(path) < 0) goto done; 428 | if (CF.verbose) fprintf(stderr, "adding regular file %s\n", path); 429 | } 430 | 431 | if (CF.recurse && (S_ISDIR(s.st_mode))) { 432 | if (add_dir(path) < 0) goto done; 433 | } 434 | } 435 | 436 | rc = 0; 437 | 438 | done: 439 | if (d) closedir(d); 440 | return rc; 441 | } 442 | 443 | int release_db(void) { 444 | int sc, rc = -1; 445 | 446 | /* done with select statement */ 447 | sc = sqlite3_finalize(CF.select_stmt); 448 | if (sc != SQLITE_OK) { 449 | fprintf(stderr,"sqlite3_finalize: %s\n", sqlite3_errstr(sc)); 450 | goto done; 451 | } 452 | 453 | /* done with insert statement */ 454 | sc = sqlite3_finalize(CF.insert_stmt); 455 | if (sc != SQLITE_OK) { 456 | fprintf(stderr,"sqlite3_finalize: %s\n", sqlite3_errstr(sc)); 457 | goto done; 458 | } 459 | 460 | /* done with insert statement */ 461 | sc = sqlite3_finalize(CF.insert_rcrd); 462 | if (sc != SQLITE_OK) { 463 | fprintf(stderr,"sqlite3_finalize: %s\n", sqlite3_errstr(sc)); 464 | goto done; 465 | } 466 | 467 | sqlite3_close(CF.db); // NULL allowed 468 | 469 | rc = 0; 470 | 471 | done: 472 | return rc; 473 | } 474 | 475 | int main(int argc, char *argv[]) { 476 | char *dir = NULL; 477 | int opt, rc=-1; 478 | 479 | CF.prog = argv[0]; 480 | 481 | while ( (opt = getopt(argc,argv,"vphd:r:t:s:b:")) > 0) { 482 | switch(opt) { 483 | case 'v': CF.verbose++; break; 484 | case 'd': dir = strdup(optarg); break; 485 | case 'r': CF.recurse=atoi(optarg); break; 486 | case 't': CF.truncate=atoi(optarg); break; 487 | case 'p': CF.mode = mode_print; break; 488 | case 's': CF.suffix = strdup(optarg); break; 489 | case 'b': CF.db_name = strdup(optarg); break; 490 | case 'h': default: usage(argv[0]); break; 491 | } 492 | } 493 | 494 | if (CF.db_name == NULL) usage(); 495 | 496 | /* set up database */ 497 | if (setup_db() < 0) goto done; 498 | 499 | switch (CF.mode) { 500 | case mode_build: 501 | if (dir == NULL) usage(); 502 | if (realpath(dir, CF.dir) == NULL) { 503 | fprintf(stderr, "realpath %s: %s\n", dir, strerror(errno)); 504 | goto done; 505 | } 506 | if (add_dir(CF.dir) < 0) goto done; 507 | break; 508 | case mode_print: 509 | if (print_db() < 0) goto done; 510 | break; 511 | default: 512 | assert(0); 513 | goto done; 514 | break; 515 | } 516 | 517 | rc = 0; 518 | 519 | done: 520 | if (dir) free(dir); 521 | if (CF.suffix) free(CF.suffix); 522 | if (CF.db_name) free(CF.db_name); 523 | release_db(); 524 | return rc; 525 | } 526 | -------------------------------------------------------------------------------- /sqlite/select.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* 5 | This example of using sqlite uses the standard function sequence: 6 | open/prepare/step/close 7 | 8 | To try it, first create the example.db with this content: 9 | 10 | % sqlite3 example.db 11 | sqlite> create table people (name text, age integer); 12 | sqlite> insert into people values ("ben", 8); 13 | sqlite> insert into people values ("sarah", 5); 14 | sqlite> .exit 15 | 16 | Then run this program: 17 | 18 | % ./select example.db 19 | 20 | */ 21 | 22 | int dump_columns(sqlite3_stmt *ppStmt) { 23 | const unsigned char *name; 24 | int age; 25 | name = sqlite3_column_text(ppStmt, 0); 26 | age = sqlite3_column_int(ppStmt, 1); 27 | printf("name: %s age %d\n", name, age); 28 | return 0; 29 | } 30 | 31 | int main(int argc, char **argv){ 32 | sqlite3 *db=NULL; 33 | sqlite3_stmt *ppStmt; 34 | int rc=-1, sc, more; 35 | 36 | if( argc<2 ){ 37 | fprintf(stderr, "Usage: %s DATABASE [SQL-STATEMENT]\n", argv[0]); 38 | goto done; 39 | } 40 | 41 | sc = sqlite3_open(argv[1], &db); 42 | if( sc ){ 43 | fprintf(stderr, "sqlite3_open: %s\n", sqlite3_errmsg(db)); 44 | goto done; 45 | } 46 | 47 | char *sql = (argc > 2) ? argv[2] : "SELECT * from people;"; 48 | sc = sqlite3_prepare_v2(db, sql, -1, &ppStmt, NULL); 49 | if( sc!=SQLITE_OK ){ 50 | fprintf(stderr, "sqlite3_prepare: %s\n", sqlite3_errstr(sc)); 51 | goto done; 52 | } 53 | 54 | more = 1; 55 | while(more) { 56 | sc = sqlite3_step(ppStmt); 57 | switch(sc) { 58 | case SQLITE_ROW: dump_columns(ppStmt); break; 59 | case SQLITE_DONE: 60 | more = 0; 61 | sc = sqlite3_finalize(ppStmt); 62 | if (sc != SQLITE_OK) { 63 | fprintf(stderr,"sqlite3_finalize: %s\n", sqlite3_errstr(sc)); 64 | } 65 | break; 66 | default: 67 | fprintf(stderr,"sqlite3_step: error %d\n", sc); 68 | more = 0; 69 | break; 70 | } 71 | } 72 | 73 | rc = 0; 74 | 75 | done: 76 | sqlite3_close(db); // NULL allowed 77 | return rc; 78 | } 79 | -------------------------------------------------------------------------------- /sqlite/simple.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static int callback(__attribute__((__unused__)) void *NotUsed, 5 | int argc, char **argv, char **azColName){ 6 | int i; 7 | for(i=0; i 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct { 9 | char *prog; 10 | int verbose; 11 | char *file; /* file to read. if null, read stdin */ 12 | int fd; /* input file descriptor def.0=stdin */ 13 | } CF = { 14 | .fd = -1, 15 | }; 16 | 17 | void usage() { 18 | fprintf(stderr,"usage: %s [-v] \n", CF.prog); 19 | exit(-1); 20 | } 21 | 22 | int main(int argc, char *argv[]) { 23 | int opt, rc=-1; 24 | 25 | CF.prog = argv[0]; 26 | 27 | while ( (opt = getopt(argc,argv,"vh")) > 0) { 28 | switch(opt) { 29 | case 'v': CF.verbose++; break; 30 | case 'h': default: usage(); break; 31 | } 32 | } 33 | 34 | if (optind >= argc) usage(); 35 | 36 | CF.file = argv[optind++]; 37 | CF.fd = open(CF.file ,O_RDONLY); 38 | if (CF.fd == -1) { 39 | fprintf(stderr,"open %s: %s\n", CF.file, strerror(errno)); 40 | goto done; 41 | } 42 | 43 | rc = 0; 44 | 45 | done: 46 | if (CF.fd != -1) close(CF.fd); 47 | return rc; 48 | } 49 | -------------------------------------------------------------------------------- /uuid/Makefile: -------------------------------------------------------------------------------- 1 | # expects uuid-dev package (Ubuntu) or similar 2 | 3 | all: get_uuid 4 | 5 | get_uuid: get_uuid.c 6 | $(CC) -o $@ $< -luuid 7 | 8 | .PHONY: clean 9 | 10 | clean: 11 | rm -f get_uuid 12 | -------------------------------------------------------------------------------- /uuid/get_uuid.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | int main() { 4 | uuid_t u; 5 | uuid_generate(u); 6 | printf("uuid is %d bytes\n", (int)(sizeof(u))); 7 | 8 | int i; 9 | for(i=0; i<16;i++) { 10 | printf("%.2x%c", (unsigned)(u[i]), (i<15)?'-':'\n'); 11 | } 12 | } 13 | --------------------------------------------------------------------------------