diff --git a/opengl/README.md b/opengl/README.md
index 2bee729..f20d56b 100644
--- a/opengl/README.md
+++ b/opengl/README.md
@@ -18,11 +18,11 @@ Requirement of freeglut.
GLUT is a higher level interface built on top of GL, and adds things like:
-- mouse/keyboard input.
+- mouse/keyboard input.
This requires callback functions to be called from an event loop.
-- higher level geometric objects like spheres, cubes and teapots.
+- higher level geometric objects like spheres, cubes and teapots.
The original GLUT implementation is proprietary, but open source implementations exist such as Freeglut.
diff --git a/posix/Makefile b/posix/Makefile
new file mode 120000
index 0000000..cbd0d3e
--- /dev/null
+++ b/posix/Makefile
@@ -0,0 +1 @@
+../Makefile_many
\ No newline at end of file
diff --git a/posix/Makefile_params b/posix/Makefile_params
new file mode 100644
index 0000000..3fe046e
--- /dev/null
+++ b/posix/Makefile_params
@@ -0,0 +1 @@
+LIBS := -lm -pthread
diff --git a/posix/README.md b/posix/README.md
new file mode 100644
index 0000000..75a6b6f
--- /dev/null
+++ b/posix/README.md
@@ -0,0 +1 @@
+POSIX C API.
diff --git a/posix/main.c b/posix/main.c
new file mode 100644
index 0000000..148d617
--- /dev/null
+++ b/posix/main.c
@@ -0,0 +1,3197 @@
+/*
+Cheat on the POSIX C API.
+
+The POSIX C API is mostly an extension of the ANSI C API.
+ANSI C features shall not be discussed here.
+
+#implementations
+
+ On most Linux systems as of 2013, the POSIX C API is implemented by the GNU C library:
+ .
+
+ The GNU documentation states that POSIX compliance
+ is a design goal of the GNU C library.
+
+ #windows
+
+ TODO Is there a Windows implementation for the POSIX C API? Official?
+
+#headers
+
+ List of all headers:
+
+ POSIX defines certain things *inside*
+ headers with the same name as the ANSI stdlib ones
+ which are only activated if you add the defines *before
+ including those files*!
+
+ GCC: if you want to access them with the `-ansi -c99` flags,
+ you need to define `XXX_XOPEN_SOURCE`
+
+ There are other preprocessor defines which may expose posix functions such as `_POSIX_C_SOURCE` and `POSIX_SOURCE`
+ For the GNU C library, see `man feature_test_macros` for an explanaition.
+
+ The actual value of the preprocessor refers to the POSIX version. For example:
+
+ - 500: issue 5, 1995
+ - 600: issue 6, 2004
+ - 700: issue 7, 2008
+
+#TODO
+
+ #ptrace
+
+ one process (tracer) observes another process' (tracee) memory directly
+
+
+*/
+
+#define _XOPEN_SOURCE 700
+//#define _POSIX_C_SOURCE 200112L
+//#define POSIX_SOURCE
+
+//ansi headers in which POSIX places extensions
+
+#include
+#include //NZERO
+#include //M_PI, M_PI_2, M_PI_4:
+#include
+#include //popen(), perror()
+#include
+#include
+#include //strerror
+
+//posix only headers:
+
+#include
+#include
+#include
+#include //creat, O_CREAT
+#include
+#include //strfmon
+#include //gethostbyname
+#include
+#include
+#include //getpwuid, getpwnam, getpwent
+#include
+#include
+#include //mmap, munmap
+#include //rusage, getrusage, rlimit, getrlimit
+#include //select, FD_ZERO, FD_SET
+#include //shmget, shmat, etc.
+#include
+#include //S_IRUSR and family,
+#include //lots of posix realted typedef types
+#include //uname, struct utsname
+#include //wait, sleep
+#include //syslog
+#include //major posix header. Anything that is not elsewhere is here.
+
+extern char **environ;
+
+/* pthreads related */
+
+#define NUM_THREADS 5
+
+ pthread_mutex_t main_thread_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+ void* main_thread(void* vargument)
+ {
+ int argument;
+
+ argument = *((int*)vargument);
+
+ pthread_mutex_lock(&main_thread_mutex);
+ printf("tid = %d\n", argument);
+ //all threads of a process have the same PID
+ printf(" getpid() = %llu\n", (uintmax_t)getpid());
+ printf(" pthread_self() = %llu\n", (uintmax_t)pthread_self());
+ pthread_mutex_unlock(&main_thread_mutex);
+
+ return NULL;
+ }
+
+int main(int argc, char** argv)
+{
+ /*
+ #Namespace
+
+ POSIX adds many further per header reserved names which it would be wise to follow even on ANSI C:
+ section "The Name Space".
+ */
+
+ /*
+ #errors
+
+ Typical error dealing conventions POSIX are:
+
+ - if the return value is not needed, functions return 0 on successs and either -1 on failure
+ or an integer which indicates failure cause
+
+ - if the return value is strictly positive, return -1 on error
+
+ - if the return value is a pointer, return `NULL` on error
+
+ - if the return value can be any integer (`ptrace` for example), return `-1`, but force the user to
+ clear `errno` before making the call, and check if `errno != -` after the call.
+
+ Whenever there is an error, set `errno` accordingly to determine what was the cause of the erro
+
+ #errno.h
+
+ Is defined by ANSI C, but more predefined error constants are added extended in POSIX,
+
+ Only differences from ANSI C shall be commented here. Note that errno,
+ perror and strerror for example are all in ANSI C.
+
+ Some of the POSIX specific errors and their error messages are:
+
+ - EPERM: Operation not permitted
+
+ when users try to do something which requires previledges that they don't have,
+ such as being the root user.
+
+ - ENOENT: No such file or directory
+ - EINTR: Interrupted system call
+ - EIO: I/O Error
+ - EBUSY: Device or resource busy
+ - EEXIST: File exists
+ - EINVAL: Invalid argument
+ - EMFILE: Too many open files
+ - ENODEV: No such device
+ - EISDIR: Is a directory
+ - ENOTDIR: Isn’t a directory
+
+ Functions that modify errno document that. The convention is that only functions which fail modify
+ errno, not those that succeed.
+
+ errno can be modified as `errno = 0` for example.
+ */
+ {
+ /*
+ #errno
+
+ errno can be modified by functions to contain a description of certain
+ standard errors.
+
+ errno is a lvalue and users can explicitly modify it.
+ This is necessary in certain error checking situations, when the error can only be decided
+ by changes in errno and not by the return value.
+
+ 0 indicates no error (ANSI C)
+
+ Since many function may change errno, you must use the functions that
+ depend on errno immediatelly after the function that generates the error
+ */
+ {
+ char *dirname = "i_dont_exist";
+
+ //assure that dirname does not exist
+ if(access(dirname, F_OK) == 0 )
+ assert(rmdir(dirname) != -1 );
+ errno = 0;
+
+ rmdir(dirname);
+ assert(errno != 0);
+
+ //sucessful calls do *not* set errno to 0
+ mkdir(dirname, 0777);
+ rmdir(dirname);
+ assert(errno != 0);
+ }
+ }
+
+ /*
+ #printf
+
+ This discusses `printf` and `sprintf` POSIX extensions to ANSI.
+ */
+ {
+ /*
+ #dollar
+
+ `%2$d` means: use second argument. Treat the following arguments as if this one did not exist.
+
+ Has been incorporated in POSIX, but may break ANCI C code! (unlikely).
+
+ For that reason, compiling this generates warnings on gcc, and you should avoid this feature as:
+
+ - it is unlikely to be incorporated in ANSI C since it is a breaking change
+
+ - if you ever decide to increase portability to ANSI C
+ (in case some other key functions you were using someday get ANSI C alternatives),
+ you will have to correct this
+ */
+ {
+ //char buf[256];
+ //sprintf(buf, "%2$d %d %d", 0, 1);
+ //assert(strcmp(buf, "1 0 1") == 0);
+ }
+ }
+
+ /*
+ #string functions
+
+ POSIX offers some extra convenience functions to common string operations which are not present in ANSI C.
+ */
+ {
+ /*
+ str
+ */
+
+ /*
+ #strfmon
+
+ Monetary string formatting.
+ */
+ {
+ const int size = 16;
+ char out[size];
+ strfmon(out, size, "%n", 1234.567);
+ printf("%s", out);
+ assert(strcmp(out, "1234.57") == 0 );
+ }
+ }
+
+ /*
+ #syslog
+
+ writes error messages to standard system files
+
+ interface:
+
+ void syslog(int priority, const char *message, arguments...);
+
+ error levels:
+
+ - LOG_EMERG Description
+ - LOG_ALERT An emergency situation
+ - LOG_CRIT High-priority problem, such as database corruption
+ - LOG_ERR Critical error, such as hardware failure
+ - LOG_WARNING Errors
+ - LOG_NOTICE Warning
+ - LOG_INFO Special conditions requiring attention
+ - LOG_DEBUG Informational messages
+
+ error source:
+
+ - LOG_USER: a user space application
+ - LOG_LOCAL[0-7]: left for admins to specify
+
+ message: accepts format strings similar to printf with extensions
+
+ - %m: errno message string
+ */
+ {
+ //TODO0 this breaks my poor program, why? needs root?
+
+ //syslog(LOG_ERR | LOG_USER, "syslog test: %m\n");
+ }
+
+ /*
+ #environment variables
+
+ each process includes a list of its environment variables
+
+ those can be modified for the process
+
+ child processes inherit those variables, so this is a way
+ for processes to communicate
+ */
+ {
+ /*
+ #getenv
+
+ specified by ANSI C
+
+ #setenv
+
+ not specified by ANSI C TODO check
+
+ #putenv
+
+ don't use, just use `setenv` instead. POSIX 7 itself says this.
+ */
+ {
+ assert(setenv("HOME", "asdf", true) != -1 );
+ assert(strcmp(getenv("HOME"), "asdf" ) == 0 );
+
+ //with overwrite false, if existing is not overwritten
+ //but error is not returned:
+
+ assert(setenv("HOME", "qwer", false) != -1 );
+ assert(strcmp(getenv("HOME"), "asdf" ) == 0 );
+ }
+
+ /*
+ #environ
+
+ automatically set by POSIX libraries linked to
+
+ is a list of strings of type `VAR=val`
+ */
+ if (0) {
+ //print entire environment
+ char **env = environ;
+ puts("environ:");
+ while (*env) {
+ printf(" %s\n", *env);
+ env++;
+ }
+ }
+ }
+
+ /*
+ #math.h
+
+ the `M_PI` constants are defined by POSIX inside of math.h
+ */
+ {
+ //#constants
+ {
+ //ansi c way of calculating some constants:
+ const float PI = acos(-1);
+ const float E = exp(1);
+
+ //POSIX provides macros that expand to those constants:
+ assert(fabs(M_E - E) < 1e-6 );
+ assert(fabs(M_PI - PI) < 1e-6 );
+ assert(fabs(M_PI/2.0 - M_PI_2) < 1e-6 );
+ assert(fabs(M_PI/4.0 - M_PI_4) < 1e-6 );
+ }
+
+ /*
+ #bessel
+
+ As of POSIX 7, the only major function addition to the math library
+ seems to be bessel functions.
+
+ TODO understand, specially why is it so important to be here?
+
+
+ */
+ {
+ //double j0(double);
+ //double j1(double);
+ //double jn(int, double);
+ }
+ }
+
+ /*
+ #sleep
+
+ Non busy sleep, that is, stop program execution for a given time,
+ and let other programs run in the meantime.
+
+ There is no portable standard way of doing this.
+ */
+ if (0) {
+ printf("sleep:\n");
+ for(int i=0; i<3; i++)
+ {
+ printf("%d\n", i);
+ sleep(1);
+ }
+ printf("\n");
+ }
+
+ /*
+ #times
+
+ Get real time, user time and system time.
+ */
+ {
+ //TODO0 example
+ }
+
+ /*
+ #file descriptors
+
+ `int` identifier to a data stream.
+
+ Many file descriptors can point to a single file.
+
+ One very important property of file descriptors is the current position from which read and write shall operate.
+ Reads and writes move the current position forward.
+
+ #file descriptors vs ANSI C FILE objects
+
+ ANSI C supports only the concept of file pointers via the `FILE` macro.
+
+ POSIX extends ANSI C and contains both function that manipulate ANSI C `FILE` objects
+ and file descriptors, which are integers that identify a file descriptor for the OS.
+
+ Operations that use file desriptors:
+
+ - open, close, write
+
+ Operations that use FILE pointers:
+
+ - ANSI C `fopen`, `fclose`, `ftell`, etc. functions
+
+ Since they are specific to POSIX, functions that use file pointers often give more options
+ than ANSI C functions that use `FILE*` objects.
+
+ If you don't need that greater level of control,
+ just use the ANSI C functions for greater portability.
+
+ It is possible to convert freely to and from `FILE*` via fdopen and fileno.
+
+ #fdopen
+
+ Convert file descriptor to `FILE*`.
+
+ #fileno
+
+ Convert `FILE*` to file descriptor.
+
+ #open
+
+ man 2 open
+
+ open file descriptors such as files
+
+ returns an `int` (file descriptor number) instead of a file
+
+ Interfaces:
+
+ int open(const char *pathname, int flags);
+ int open(const char *pathname, int flags, mode_t mode);
+
+ Flags. Must specify one and only of the following:
+
+ - O_WRONLY: write only
+ - O_RDONLY: read only.
+
+ Undefined behaviour with O_TRUNC.
+
+ TODO0 can be used with O_CREAT?
+
+ - O_RDWR: read and write
+
+ Other important flags.
+
+ - O_APPEND: If the file exists, open fd at the end of the file.
+
+ - O_TRUNC: If the file exists, open fd at the end of the file,
+ set its length to zero, discarding existing contents.
+
+ Undefined behaviour with O_RDONLY.
+
+ - O_CREAT: If the file does not exit, creat it, with permissions given in mode.
+
+ Mode must be specified when this flag is set, and is ignored if this is not set.
+
+ - O_EXCL: Used with O_CREAT, ensures that the caller creates the file.
+
+ The open is atomic; that is, no two opens can open at the same time.
+
+ If the file already exists, open will fail.
+
+ Useful if multiple programs may try to create the same file.
+
+ Mode can be specified via oring predefine permission values of type:
+
+ - S_IRWXU 00700 user (file owner) has read, write and execute permission
+ - S_IRUSR 00400 user has read permission
+ - S_IWUSR 00200 user has write permission
+ - S_IXUSR 00100 user has execute permission
+
+ For group and other: G and GRP, O and OTH.
+
+ Return value: `-1` on error and set perror.
+
+ #creat
+
+ Same as:
+
+ open(path, O_WRONLY|O_CREAT|O_TRUNC, mode);
+
+ #close
+
+ File descriptors occupy memory and are thus a finite resource.
+
+ When you are done with one, release it with a close call.
+
+ #write
+
+ Write to file descriptor, such as one representing a file gotten via `open`
+ or one representing a pipe obtained with `pipe`.
+
+ Increments the current position of the file descriptor.
+ For regular files, if this becomes greater than the current file size,
+ then the file size is increased as needed.
+
+ #return value
+
+ Returns number of bytes writen.
+
+ For regular files, POSIX does not say much when the number of bytes writen is less than the ammount required
+ and that usually only happens in bad cases:
+
+ - signal received in the middle of a write
+
+ If it receives a signal before writing anything, returns -1.
+
+ - no more space in the filesystem
+
+ - no permission to write such large files
+
+ For pipes, this may occur in less bad scenarios, for example if the pipe buffer is filled,
+ the write may either:
+
+ - be partial if `nbytes > PIPE_BUF`
+
+ - block until more space is available depending on the `O_NONBLOCK` state.
+
+ On error, return -1 and set errno.
+
+ It seems that POSIX does not say if zero return values on non-zero size write requests.
+ It is unlikely that an implementation will return 0, since that would make no progress,
+ and it should return `-1` and set errno instead to report error cases.
+
+ #atomicity of simultaneous writes
+
+ Writes of less than `PIPE_BUF` bytes cannot be interleaved with other writes.
+
+ Larger writes can.
+
+ TODO0 are writes to seekable files atomic? Seems not:
+ for pipes we know yes for writes smaller than PIPE_BUF.
+
+ #pwrite
+
+ Same as write, but writes to a given offset and does not update fd position.
+
+ It cannot be done on non seekable files.
+
+ #read
+
+ POSIX 7 docs:
+
+ Read bytes from a file descriptor.
+
+ Interface:
+
+ ssize_t read(int fildes, void *buf, size_t nbyte);
+
+ The behavior of multiple concurrent reads on the same pipe, FIFO, or terminal device is unspecified.
+
+ If the starting position is at or after the end-of-file, 0 shall be returned.
+
+ If the value of `nbyte` is larger than {SSIZE_MAX}, the result is implementation-defined.
+ In practice this is rarely the case, because `SSIZE_MAX` is the size of a `size_t` type,
+ which is usually an integer, giving around 2Gb.
+
+ The exact behaviour of read depends on the fd type: pipes and regular files have slightly different rules.
+
+ #read pipe
+
+ When attempting to read from an empty pipe or FIFO:
+
+ - If no process has the pipe open for writing, read() shall return 0 to indicate end-of-file.
+
+ - If some process has the pipe open for writing and O_NONBLOCK is set,
+ read() shall return -1 and set errno to [EAGAIN].
+
+ - If some process has the pipe open for writing and O_NONBLOCK is clear,
+ read() shall block the calling thread until some data is written or
+ the pipe is closed by all processes that had the pipe open for writing.
+
+ #return value
+
+ Returns number of bytes read.
+
+ Quoting POSIX:
+
+ The value returned may be less than nbyte if:
+
+ - the number of bytes left in the file is less than nbyte
+
+ - the read() request was interrupted by a signal
+
+ - if the file is a pipe or FIFO or special file and has fewer
+ than nbyte bytes immediately available for reading.
+
+ Therefore on a regular file, this is how the end of file can be recognized.
+
+ On error return -1 and set errno.
+
+ #lseek
+
+ Like ANSI C fseek for file descriptors.
+
+ lseek cannot be done on certain file descriptor types which are not seekable,
+ for example on pipes.
+
+ #lseek after eof
+
+ If data is writen with offset after file size, file size is increased and data skipped reads `(int)0` (`'\0'`).
+
+ This contrasts with ANSI C fseek, in which this is undefined behaviour.
+ #fcntl
+
+ Manipulate a file descriptor.
+
+ Check if a fd is open:
+
+ #dup
+
+ Duplicate file descriptor.
+
+ Same as:
+
+ fcntl(fildes, F_DUPFD, 0);
+ */
+ {
+ int fd;
+ char in[] = "abcd";
+ int nbytes = strlen(in);
+ int nbytes_total, nbytes_last;
+ char *out = malloc (nbytes + 1);
+ char *fname = "open.tmp";
+
+ /*
+ write
+
+ A robust write usage that either outputs all its bytes,
+ or gives an error.
+ */
+ {
+ fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
+ if (fd == -1) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ } else {
+ nbytes_total = 0;
+ while (nbytes_total < nbytes) {
+ nbytes_last = write(fd, in + nbytes_total, nbytes - nbytes_total);
+ if (nbytes_last == -1) {
+ perror("write");
+ exit(EXIT_FAILURE);
+ }
+ nbytes_total += nbytes_last;
+ }
+
+ if (close(fd) == -1 ) {
+ perror("close");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ //read
+ {
+ fd = open(fname, O_RDONLY);
+ if (fd == -1) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ } else {
+ nbytes_total = 0;
+ while ((nbytes_last = read(fd, out, nbytes) ) > 0 ) {
+ //compare output as it comes out, even if less than nbytes comes
+ assert(memcmp(in + nbytes_total, out, nbytes_last) == 0 );
+ nbytes_total += nbytes_last;
+ }
+ if (nbytes_last == -1) {
+ perror("read");
+ exit(EXIT_FAILURE);
+ }
+
+ if (close(fd) == -1 ) {
+ perror("close");
+ exit(EXIT_FAILURE);
+ }
+ close(fd);
+ }
+ }
+
+ //BAD forget O_CREAT on non-existent file gives ENOENT
+ {
+ fd = open("/i/do/not/exist", O_RDONLY, S_IRWXU);
+ if (fd == -1) {
+ assert(errno == ENOENT);
+ }
+ else {
+ assert(false);
+ }
+ }
+
+ //BAD write on a O_RDONLY fd gives errno EBADF
+ {
+ int fd;
+ char *fname = "write_rdonly.tmp";
+
+ fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
+ if (fd == -1) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ }
+ else {
+ if (close(fd) == -1 ) {
+ perror("close");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ fd = open(fname, O_RDONLY);
+ if (fd == -1) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ }
+ else {
+ if (write(fd, "a", 1) == -1 ) {
+ assert(errno == EBADF);
+ }
+ else {
+ assert(false);
+ }
+
+ if (close(fd) == -1 ) {
+ perror("close");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ /*
+ open and write without truncate
+
+ after write 1: abcd
+ after write 2: 01cd
+ */
+ {
+ //set file to abc
+ {
+ fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
+ if (fd == -1) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ } else {
+ if (write(fd, in, nbytes) != nbytes ) {
+ perror("write");
+ exit(EXIT_FAILURE);
+ }
+ if (close(fd) == -1 ) {
+ perror("close");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ //open and write to it without truncating
+ {
+ fd = open(fname, O_RDWR);
+ if (fd == -1) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ }
+ else {
+ if (write(fd, "01", 2) != 2 ) {
+ perror("write");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ //check the new result
+ {
+ if (lseek(fd, 0, SEEK_SET) != 0 ) {
+ perror("lseek");
+ exit(EXIT_FAILURE);
+ }
+ if (read(fd, out, nbytes) != nbytes ) {
+ perror("read");
+ exit(EXIT_FAILURE);
+ }
+ else {
+ //the first two bytes were overwriten
+ assert(memcmp(out, "01cd", nbytes) == 0 );
+ }
+ if (close(fd) == -1 ) {
+ perror("close");
+ exit(EXIT_FAILURE);
+ }
+ close(fd);
+ }
+ }
+
+ /*
+ lseek after eof
+ */
+ {
+ int fd;
+ char out[2];
+
+ fd = open("lseek.tmp", O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);
+ if (fd == -1) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ }
+ else {
+ if (lseek(fd, 1, SEEK_SET) != 1 ) {
+ perror("lseek");
+ exit(EXIT_FAILURE);
+ }
+ if (write(fd, "a", 1) != 1 ) {
+ perror("write");
+ exit(EXIT_FAILURE);
+ }
+ //read after eof, return 0 and read nothing
+ if (read(fd, out, 1) != 0 ) {
+ assert(false);
+ }
+ if (lseek(fd, 0, SEEK_SET) != 0 ) {
+ perror("lseek");
+ exit(EXIT_FAILURE);
+ }
+ //byte 0 was never writen, so reading it returns (int)0
+ if (read(fd, out, 2) != 2 ) {
+ perror("read");
+ exit(EXIT_FAILURE);
+ } else {
+ assert(memcmp(out, "\0a", 2) == 0 );
+ }
+ if (close(fd) == -1 ) {
+ perror("close");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ /*
+ File descriptors open by default to all processes.
+
+ Analogous to ANSI C `stdout`, `stdin` and `stderr`, except that the ANSI C `FILE*` objects.
+
+ #STDIN_FILENO
+
+ stdin
+
+ #STDOUT_FILENO
+
+ stdout
+
+ #STDERR_FILENO
+
+ stderr
+ */
+ {
+ printf("STDIN_FILENO = %d\n", STDIN_FILENO);
+ printf("STDOUT_FILENO = %d\n", STDOUT_FILENO);
+ printf("STDERR_FILENO = %d\n", STDERR_FILENO);
+ }
+ }
+
+ /*
+ #link
+
+ Create hardlink to file.
+
+ If newfile exists, the link is not created, returns -1 and sets `errno = EEXIST`
+
+ #unlink
+
+ Delete file.
+
+ Is called unlink because what you are doing is not to directly remove a file from disk
+ but instead remove one hardlink for the data.
+
+ if the number of hardlinks to a data equals 0, it the data gets deleted.
+
+ If the given path does not exist `errno = ENOENT`.
+ */
+ {
+ int fd;
+ char in[] = "a";
+ char in_new[] = "b";
+ int nbytes = strlen(in);
+ char *out = malloc(nbytes);
+ char *oldpath = "link_old.tmp";
+ char *newpath = "link_new.tmp";
+
+ //create old
+ fd = open(oldpath, O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
+ if (fd == -1) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ } else {
+ if (write(fd, in, nbytes) != nbytes ) {
+ perror("write");
+ exit(EXIT_FAILURE);
+ }
+ if (close(fd) == -1 ) {
+ perror("close");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* ensure that the new path does not exist
+ ENOENT is ok since the path may not exist */
+ if (unlink(newpath) == -1 && errno != ENOENT ) {
+ perror("link");
+ exit(EXIT_FAILURE);
+ }
+
+ //make the hardlink
+ if (link(oldpath, newpath) == -1 ) {
+ perror("link");
+ exit(EXIT_FAILURE);
+ }
+
+ //write to new
+ fd = open(newpath, O_WRONLY);
+ if (fd == -1) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ } else {
+
+ if (write(fd, in_new, nbytes) != nbytes ) {
+ perror("write");
+ exit(EXIT_FAILURE);
+ }
+
+ if (close(fd) == -1 ) {
+ perror("close");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ //assert that it reflected on old
+ fd = open(oldpath, O_RDONLY);
+ if (fd == -1) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ } else {
+ if (read(fd, out, nbytes) != nbytes ) {
+ perror("read");
+ exit(EXIT_FAILURE);
+ }
+
+ assert(memcmp(out, in_new, nbytes) == 0 );
+
+ if (close(fd) == -1 ) {
+ perror("close");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ free(out);
+ }
+
+ /*
+ #symlink
+
+ create symbolic link
+
+ TODO0 example
+ */
+ {
+ }
+
+ /*
+ #select
+
+ Wait for one of multiple file descriptors to become available for some operation.
+
+ Sounds like server implementation!
+ */
+
+ /*
+ #mmap
+
+ Good man page:
+
+ man mmap
+
+ Initial code source:
+
+ Map RAM memory to a file.
+
+ TODO application?
+ */
+ {
+ char *filepath = "mmap.tmp";
+ int numints = 4;
+ int filesize = numints * sizeof(int);
+
+ int i;
+ int fd;
+ int result;
+ int *map; /* mmapped array of int's */
+
+ //write to file with mmap
+ {
+ /* `O_WRONLY` is not sufficient when mmaping, need `O_RDWR`.*/
+ fd = open(filepath, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
+ if (fd == -1) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ }
+
+ /* set fd position and write to it to strech the file */
+ result = lseek(fd, filesize - 1, SEEK_SET);
+ if (result == -1) {
+ close(fd);
+ perror("lseek");
+ exit(EXIT_FAILURE);
+ }
+
+ /* write something to the file to actually strech it */
+ result = write(fd, "", 1);
+ if (result != 1) {
+ close(fd);
+ perror("write");
+ exit(EXIT_FAILURE);
+ }
+
+ /* do the actual mapping call */
+ map = mmap(NULL, filesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (map == MAP_FAILED) {
+ close(fd);
+ perror("mmap");
+ exit(EXIT_FAILURE);
+ }
+
+ /* write int's to the file as if it were memory because MAP_SHARED was used */
+ for (i = 0; i < numints; ++i) {
+ map[i] = i;
+ }
+
+ /* free mmapped memory */
+ if (munmap(map, filesize) == -1) {
+ perror("munmap");
+ exit(EXIT_FAILURE);
+ }
+
+ /* un-mmaping doesn't close the file, so we still need to do that. */
+ if (close(fd) == -1) {
+ perror("close");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ //read result back in
+ {
+ fd = open(filepath, O_RDONLY, 0);
+ if (fd == -1) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ }
+
+ map = mmap(0, filesize, PROT_READ, MAP_SHARED, fd, 0);
+ if (map == MAP_FAILED) {
+ close(fd);
+ perror("mmap");
+ exit(EXIT_FAILURE);
+ }
+
+ assert(map[1] == 1);
+
+ //segmentation fault because no PROT_WRITE:
+ {
+ //map[1] = 2;
+ }
+
+ if (munmap(map, filesize) == -1) {
+ perror("munmap");
+ exit(EXIT_FAILURE);
+ }
+
+ if (close(fd) == -1) {
+ perror("close");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /*
+ MAP_PRIVATE
+
+ Creates a copy-on-write version of file in memory.
+
+ Changes do not reflect on the file.
+
+ TODO0 application?
+ */
+ {
+ //private write
+ {
+ fd = open(filepath, O_RDONLY, 0);
+ if (fd == -1) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ }
+
+ map = mmap(NULL, filesize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+ if (map == MAP_FAILED) {
+ close(fd);
+ perror("mmap");
+ exit(EXIT_FAILURE);
+ }
+
+ map[0] = 1;
+
+ if (munmap(map, filesize) == -1) {
+ perror("munmap");
+ exit(EXIT_FAILURE);
+ }
+
+ if (close(fd) == -1) {
+ perror("close");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ //read
+ {
+ fd = open(filepath, O_RDONLY, 0);
+ if (fd == -1) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ }
+
+ map = mmap(0, filesize, PROT_READ, MAP_SHARED, fd, 0);
+ if (map == MAP_FAILED) {
+ close(fd);
+ perror("mmap");
+ exit(EXIT_FAILURE);
+ }
+
+ //did not change!
+ assert(map[0] == 0);
+
+ if (munmap(map, filesize) == -1) {
+ perror("munmap");
+ exit(EXIT_FAILURE);
+ }
+
+ if (close(fd) == -1) {
+ perror("close");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ }
+
+ //#pathname operations
+ {
+ /*
+ #realpath
+
+ Return:
+
+ - absolute path
+ - cannonical: does not contain `.` nor `..`.
+
+ Interface:
+
+ char *realpath(const char *restrict file_name,
+ char *restrict resolved_name);
+
+ The function does completelly different things depending if resolved_name is NULL or not:
+
+ - `resolved_name == NULL`: realpath mallocs the path for you and returns it.
+
+ You have to free it in the future.
+
+ This is a good options if you don't already have a buffer of the right size, since calculating the required buffer size
+ would be annoying (would require calling `pathconf`).
+
+ - `resolved_name == NULL`: the pathname is copied to `resolved_name`.
+
+ You must make sure that you have enough space there.
+
+ This is a good option if you already have a large enough buffer laying around,
+ since it does not require dynamic allocation.
+
+ Of course, ensuring that your buffer is large enough means doing messy `pathconfs` at some point.
+ */
+ {
+ char *rp = realpath(".", NULL);
+ if (rp) {
+ printf("realpath(\".\") = %s\n", rp);
+ } else {
+ perror("realpath");
+ exit(EXIT_FAILURE);
+ }
+ free(rp);
+ }
+
+ /*
+ #dirname #basename
+
+ p may be modified memory is statically allocated
+ and may change on next dirname/basename call.
+ TODO what is p
+
+ behaviour:
+
+ path dirname basename
+ ---------- -------- ---------
+ "/usr/lib" "/usr" "lib"
+ "/usr/" "/" "usr"
+ "usr" "." "usr"
+ "/" "/" "/"
+ "." "." "."
+ ".." "." ".."
+ */
+ {
+
+ char p[1024];
+ char* res;
+
+ strcpy(p, "a/b");
+ res = dirname(p);
+ assert(strcmp(res, "a") == 0 );
+
+ strcpy(p, "a/b");
+ res = basename(p);
+ assert(strcmp(res, "b") == 0 );
+ }
+ }
+
+ /*
+ #file and directory operations
+
+ there is no standard portable way of doing most them:
+
+
+ posix alternatives:
+
+ - portable semi heavyweight: booost: #include
+ - portable lightweight: dirent.h
+ */
+ {
+
+ /*
+ #stat family
+
+ get info on paths
+
+ return value: 0 on success, other constants on errors.
+
+ If you get a 0, you know the file exists!
+ This is not however the best way to check if a file exists since it incurs the large overhead
+ of getting the parameters. Use `access` for that instead.
+
+ This fills in the `struct stat` given by pointer.
+
+ The family contains the following variants:
+
+ - stat: takes string path. Grouped under fstatat.
+ - lstat: does not dereference symbolic links. Grouped under fstatat.
+ - fstat: takes fd.
+ - fstatat: can't understand, does not seem important.
+
+ #struct stat
+
+ fields:
+
+ - dev_t st_dev Device ID of device containing file.
+ - ino_t st_ino File serial number.
+ - mode_t st_mode Mode of file
+ - nlink_t st_nlink Number of hard links to the file.
+ - uid_t st_uid User ID of file.
+ - gid_t st_gid Group ID of file.
+ - dev_t st_rdev Device ID (if file is character or block special).
+ - off_t st_size For regular files, the file size in bytes.
+
+ For symbolic links, the length in bytes of the
+ pathname contained in the symbolic link.
+
+ For a typed memory object, the length in bytes.
+
+ For other file types, the use of this field is
+ unspecified.
+
+ - struct timespec st_atim Last data access timestamp.
+ - struct timespec st_mtim Last data modification timestamp.
+ - struct timespec st_ctim Last file status change timestamp.
+
+ - blksize_t st_blksize A file system-specific preferred I/O block size
+ for this object. In some file system types, this
+ may vary from file to file.
+ - blkcnt_t st_blocks Number of blocks allocated for this object.
+
+ #find if a file exists
+
+ in *nix, you often cannot be sure if a file or directory exists,
+ because to do that you must have permission to list all of its parent dirs.
+
+ the only thing you can say is that a path is accessible or not.
+
+ using stat is a good way to do that.
+ */
+ {
+ char in[] = "123\n";
+ char fname[] = "stat.tmp";
+ struct stat s;
+
+ //create the file
+ int fd = open(fname, O_WRONLY | O_CREAT, S_IRWXU);
+ int nbytes = strlen(in);
+ if (fd != -1) {
+ if (write(fd, in, nbytes) != nbytes )
+ assert(false);
+ else {
+ //assert that file exists:
+ assert(stat(fname, &s) == 0 );
+
+ //view/assert the fields of the stat struct:
+ assert(s.st_size == nbytes);
+ }
+ close(fd);
+ }
+ else {
+ assert(false);
+ }
+ }
+
+ /*
+ #access
+
+ Check if file or directory exists and or has a given permission (rwx):
+
+ - R_OK
+ - W_OK
+ - X_OK
+ - F_OK: file exists
+
+ If the access is not permitted, errno is still set even if this call did not give an error.
+ */
+ {
+ char *exist = realpath(".", NULL);
+ if(access(exist, F_OK) == -1 ) {
+ perror("access");
+ assert(false);
+ }
+ free(exist);
+
+ char *dont_exist = "/i/dont/canot/must/not/exist.asdf";
+ if(access(dont_exist, F_OK) == -1 ) {
+ perror("access(dont_exist, F_OK)" );
+ } else {
+ assert(false);
+ }
+ }
+
+ //#mkdir
+ {
+ struct stat s;
+ char fname[] = "mkdir";
+
+ //remove the file if it exists:
+ if(stat(fname, &s) == 0 )
+ rmdir(fname);
+
+ //make the dir and check for error:
+ if(mkdir(fname, 0777) == -1 )
+ assert(false);
+ }
+
+ //#rmdir
+ {
+ mkdir("rmdir", 0777);
+ if(rmdir("rmdir") == -1 )
+ assert(false);
+ }
+
+ /*
+ #ls
+
+ Opendir is the basis for `ls`.
+
+ #opendir
+
+ Open a directory for reading.
+
+ #readdir
+
+ Get next directory entry, or NULL if over.
+
+ #dirent
+
+
+ */
+ {
+ DIR* dp;
+ struct dirent* entry;
+
+ dp = opendir(".");
+ if (dp == NULL) {
+ perror("opendir");
+ } else {
+ printf("opendir:\n");
+ while ((entry = readdir(dp) ) != NULL ) {
+ printf(" %s\n", entry->d_name);
+ }
+ }
+ }
+ }
+
+ /*
+ #getrusage
+
+ rusage stands for Resource usage
+
+ the kernel allows processes to use a certain ammount of ressources such as
+ memory or processor time
+
+ exceeding those limits may lead the kernel to kill a processes
+
+ returns the total time usage of current process (non sleeping/waiting)
+
+ int getrusage(int who, struct rusage *r_usage);
+
+ - who:
+
+ - RUSAGE_SELF: only get information about current process
+ - RUSAGE_CHILDREN: information includes both current process and chidren who are dead
+ and are just waiting for the parent to call `wait()` on them.
+
+ This makes sense here because the only thing that keeps their memory
+ used up is the existance of the parent process.
+
+ - r_usage: is the main return valu, and is set to contain a struct:
+
+ struct rusage {
+ struct timeval ru_utime; // user time used
+ struct timeval ru_stime; // system time used
+ };
+
+ and `timeval` is of type:
+
+ struct timeval {
+ time_t tv_sec Seconds.
+ suseconds_t tv_usec Microseconds.
+ };
+
+ - return: 0 on success, -1 on error + errno set to exact error
+ */
+ {
+ struct rusage usage;
+ if (getrusage(RUSAGE_SELF, &usage) == -1 ) {
+ perror("getrusage failed");
+ exit(EXIT_FAILURE);
+ } else {
+ printf(
+ "user time = %llu s %llu micro secs\n",
+ (uintmax_t)usage.ru_utime.tv_sec,
+ (uintmax_t)usage.ru_utime.tv_usec
+ );
+ printf(
+ "system time = %llu s %llu micro secs\n",
+ (uintmax_t)usage.ru_stime.tv_sec,
+ (uintmax_t)usage.ru_stime.tv_usec
+ );
+ }
+ }
+
+ /*
+ #getrlimit
+
+ returns the maximum value for a given resource
+
+ there are two types of limits:
+
+ - soft: can be increased by any process to any value lower than the hard maximum
+ - hard: only processes with special privileges may increase the hard limit
+
+ if a resource goes over the soft limit, the kernel may choose to kill the process
+
+ interfaces:
+
+ int getrlimit(int resource, struct rlimit *rlp);
+ int setrlimit(int resource, const struct rlimit *rlp);
+
+ - resource: see the docs for a description of each possible value
+
+ - rlp: struct of type:
+
+ struct rlimit {
+ rlim_t rlim_cur // The current (soft) limit.
+ rlim_t rlim_max // The hard limit.
+ }
+
+ where `rlim_t` is an unsigned integer
+ */
+ {
+ struct rlimit limit;
+ if (getrlimit(RLIMIT_DATA, &limit) == -1 ) {
+ perror("getrlimit(RLIMIT_DATA, ...) failed" );
+ exit(EXIT_FAILURE);
+ } else {
+ //maximum process memory in bytes
+ if (limit.rlim_max == RLIM_INFINITY) {
+ //RLIM_INFINITY means that no limit is imposed on the resource
+ puts("RLIMIT_DATA: no limit imposed");
+ } else {
+ printf(
+ "RLIMIT_DATA\n soft = %llu\n hard = %llu\n",
+ (uintmax_t)limit.rlim_cur,
+ (uintmax_t)limit.rlim_max
+ );
+ }
+ }
+
+ //ok, enough of error checking from now on
+
+ printf("RLIM_INFINITY = %llu\n", (uintmax_t)RLIM_INFINITY);
+
+ //maximum total cpu usage in seconds
+ getrlimit(RLIMIT_CPU, &limit);
+ printf(
+ "RLIMIT_CPU\n soft = %llu\n hard = %llu\n",
+ (uintmax_t)limit.rlim_cur,
+ (uintmax_t)limit.rlim_max
+ );
+
+ //maximum file size in bytes
+ getrlimit(RLIMIT_FSIZE, &limit);
+ printf(
+ "RLIMIT_FSIZE\n soft = %llu\n hard = %llu\n",
+ (uintmax_t)limit.rlim_cur,
+ (uintmax_t)limit.rlim_max
+ );
+
+ //number of file descriptors:
+ getrlimit(RLIMIT_NOFILE, &limit);
+ printf(
+ "RLIMIT_NOFILE\n soft = %llu\n hard = %llu\n",
+ (uintmax_t)limit.rlim_cur,
+ (uintmax_t)limit.rlim_max
+ );
+ }
+
+ /*
+ #limits.h
+
+ This header exists in ANSI C, and POSIX extends it with several values.
+
+ Defines current and possible maximuns, minimums for several resources.
+
+ Some resources cannot cannot be evaluated statically.
+
+ For example the maximum path length depends on which directory we are talking about,
+ since diferent directories can be on differnet mount points.
+
+ Also some resources can change maximum values at anytime while the program is executing.
+
+ In those cases, limits defines a KEY value which can be passed to a function that gets
+ the actual values for a given key, for example pathconf or sysconf.
+
+ For resources that have fixed values, this header furnishes them directly.
+ */
+ {
+ //static macros
+ {
+ //TODO0
+ printf("NL_ARGMAX = %d\n", NL_ARGMAX);
+
+ //maximum value that fits into a `size_t`.
+ printf("SSIZE_MAX (Mib) = %ju\n", (uintmax_t)SSIZE_MAX / (1 << 20) );
+
+ }
+
+ /*
+ #sysconf
+
+ Get lots of info on the system configuration
+
+ Meanings for the constants can be found under
+ the `limits.h` and `unistd.h` corresponding variables
+
+ If the value can be negative, it is necessary to check errno changes for error.
+
+ It seems
+
+ #maximum path length
+
+ This is needed often when you need to deal with paths names.
+
+ Sources:
+
+ -
+
+ Keep in mind that this value can vary even while a program is running,
+ and depends of the underlying filesystem, and therefore of the direcotory you are in.
+
+ As a consequence of this, it does not make sense to have a macro constant and use it to create
+ fixed variable arrays: a function is needed, and memory must be allocated with malloc.
+ */
+ {
+ //number of processors:
+
+ printf("_SC_NPROCESSORS_ONLN = %ld\n", sysconf(_SC_NPROCESSORS_ONLN) );
+
+ //maximum lengh of command line arguments + environment variables:
+
+ printf("_SC_ARG_MAX (MiB) = %ld\n", sysconf(_SC_ARG_MAX) / (1 << 20 ) );
+
+ //TODO0 find the number of processors / cpus / cores: not possible without glibc extension?
+ //
+ }
+
+ /*
+ #pathconf
+
+ Similar to sysconf, but for parameters that depend on a path, such as maxium filename lengths.
+ */
+ {
+ //max basename in given dir including trailling null:
+
+ printf("pathconf(\".\", _PC_NAME_MAX) = %ld\n", pathconf(".", _PC_NAME_MAX));
+
+ //max pathname in (TODO this is per device?)
+
+ printf("pathconf(\".\", _PC_PATH_MAX) = %ld\n", pathconf(".", _PC_PATH_MAX));
+ }
+ }
+
+ /*
+ #user information
+
+ once use have uids for processes, you can querry standard user information
+ which was traditionally stored in the `/etc/passwd` file.
+ */
+ {
+ /*
+ #getpwuid
+
+ you can get those information either by username or by uid:
+
+ #include
+
+ struct passwd *getpwuid(uid_t uid);
+ struct passwd *getpwnam(const char *name);
+
+ the struct returned is:
+
+ struct passwd {
+ passwd Member // Description
+ char *pw_name // The user’s login name
+ uid_t pw_uid // The UID number
+ gid_t pw_gid // The GID number
+ char *pw_dir // The user’s home directory
+ char *pw_gecos // The user’s full name
+ char *pw_shell // The user’s default shell
+ }
+
+ which contains all the required user metadata specified by POSIX
+ */
+ {
+ uid_t uid = getuid();
+ struct passwd* info = getpwuid(uid);
+ if (info == NULL) {
+ perror("getpwuid failed");
+ exit(EXIT_FAILURE);
+ } else {
+ puts("getpwuid");
+ printf(" pw_name = %s\n", info->pw_name );
+ printf(" pw_uid = %d\n", info->pw_uid );
+ printf(" pw_gid = %d\n", info->pw_gid );
+ printf(" pw_dir = %s\n", info->pw_dir );
+ printf(" pw_gecos = %s\n", info->pw_gecos);
+ printf(" pw_shell = %s\n", info->pw_shell);
+ }
+ }
+
+ /*
+ #getpwuid
+
+ iterate a list of all passwd structures
+
+ first call gets the first, every call gets the next
+
+ to start from beginning again do:
+
+ void setpwent(void);
+
+ when you are done, free any associated resources with:
+
+ endpwent()
+ */
+ {
+ puts("all users:");
+ struct passwd* info;
+
+ info = getpwent();
+ while (info != NULL) {
+ printf(" %s\n", info->pw_name );
+ info = getpwent();
+ }
+ endpwent();
+ }
+ }
+
+ /*
+ #uname
+
+ you can get information about the current computer using `uname`
+
+ unsurprisingly, it is the same information given by the POSIX utility `uname`
+ */
+ {
+ struct utsname info;
+ if (uname(&info) == -1 ) {
+ perror("uname failed");
+ exit(EXIT_FAILURE);
+ } else {
+ puts("uname");
+ printf(" sysname = %s\n", info.sysname );
+ printf(" nodename = %s\n", info.nodename );
+ printf(" release = %s\n", info.release );
+ printf(" version = %s\n", info.version );
+ printf(" machine = %s\n", info.machine );
+ }
+ }
+
+ /*
+ #process info
+
+ #getpid
+
+ each process has an unique identifying integer called pid
+
+ #getuid
+
+ each process has user information associated to it
+ which determine what the process can or not
+
+ there are two types of uid and gid: real and effective:
+
+ - real is always of who executes the program
+
+ - effective may be different depending on the suid and sgid bits
+
+ #getguid
+
+ like getuid but for user group
+
+ #setuid
+
+ sets the user id if you have the priviledges
+
+ #getppid
+
+ Gets paren't pid.
+
+ It seems that it is not possible to list all children of a process in POSIX:
+
+ */
+ {
+ uid_t uid = getuid();
+ uid_t euid = geteuid();
+ gid_t gid = getgid();
+ gid_t egid = getegid();
+ pid_t pid = getpid();
+ printf("getpid() = %llu\n", (uintmax_t)pid );
+ printf("getuid() = %llu\n", (uintmax_t)uid );
+ printf("geteuid() = %llu\n", (uintmax_t)euid );
+ printf("getgid() = %llu\n", (uintmax_t)gid );
+ printf("getegid() = %llu\n", (uintmax_t)egid );
+ printf("getppid() = %llu\n", (uintmax_t)getppid());
+
+ /*
+ #getcwd
+
+ pwd
+
+ #root directory
+
+ As of POSIX 7, this concept is not available.
+
+ It is implemented as a Glibc extension under `_BSD_SOURCE`.
+ */
+ {
+ const int n = 1 << 10;
+ char buf[n];
+ if (getcwd(buf, n) == NULL ) {
+ perror("getcwd");
+ } else {
+ printf("getcwd() = %s\n", buf);
+ }
+ }
+ }
+
+ /*
+ #getpriority
+
+ Each process, user and group has a priority associated to it.
+
+ Those priorities are commonly called *nice* values on UNIX, since
+ the higher the nice, the less time it takes (it is nicer to other processes)
+
+ Any process can reduce its priority.
+ Only a priviledged process can increaes its priority,
+ even if the process just decreased it himself.
+
+ POSIX does not fix the nice range, but it does specify that:
+
+ - lower values are more favorable
+ - the values must be between `-{NZERO}` and x{NZERO}-1`.
+
+ where `NZERO` is implementation defined parameter.
+
+ The minimum value for NZERO if 20, it is also the most common.
+
+ The actual effect of priority is undefined.
+
+ Nice:
+
+ int nice(int incr)
+
+ - incr: how much to increase the nice value
+ - return: the new nice value after the increase
+
+ getpriority:
+
+ int getpriority(int which, id_t who);
+
+ - which:
+
+ - PRIO_PROCESS:
+ - PRIO_PGRP: TODO
+ - PRIO_USER: TODO
+
+ - who: pid, uid or gid depending on which. `0` means current.
+
+ #error checking
+
+ On error, returns `-1` and errno set to indicate the error.
+
+ Therefore simply checking the return value is not enough to detect an error
+ since `-1` is a valid return value.
+
+ Therefore, to do error checking one *must* check `errno != 0`:
+
+ - set `errno = 0`
+ - make the call
+ - there is an error iff ret = -1 errno != 0.
+
+ #setpriority
+
+ Return value is the same as getpriority after the modification.
+
+ #nice
+
+ Same as setpriority, but only for `PRIO_PROCESS` but increments
+ (or decrements) the value instead of setting it to an absolute value.
+
+ Return value is the same as getpriority after the modification.
+
+ Can only be negative if we have `sudo`, since a negative value means to increase the priority.
+ */
+ {
+ int prio;
+
+ printf("NZERO = %d\n", NZERO);
+
+ errno = 0;
+ prio = getpriority(PRIO_PROCESS, 0);
+ if (prio == -1 && errno != 0) {
+ perror("getpriority(PRIO_PROCESS, 0)" );
+ } else {
+ printf("getpriority(PRIO_PROCESS, 0) = %d\n", prio );
+ }
+
+ errno = 0;
+ prio = getpriority(PRIO_PGRP, 0);
+ if (prio == -1 && errno != 0) {
+ perror("getpriority(PRIO_PGRP, 0)" );
+ } else {
+ printf("getpriority(PRIO_PGRP, 0) = %d\n", prio );
+ }
+
+ errno = 0;
+ prio = getpriority(PRIO_USER, 0);
+ if (prio == -1 && errno != 0) {
+ perror("getpriority(PRIO_USER, 0)" );
+ } else {
+ printf("getpriority(PRIO_USER, 0) = %d\n", prio );
+ }
+
+ errno = 0;
+ prio = nice(0);
+ if (prio == -1 && errno != 0) {
+ perror("nice(0)" );
+ } else {
+ printf("nice(0) = %d\n", nice(0 ) );
+ }
+
+ //ok, tired of errno checking:
+ printf("nice(0) = %d\n", nice(0 ) );
+ printf("nice(1) = %d\n", nice(1 ) );
+ printf("nice(0) = %d\n", nice(0 ) );
+
+ errno = 0;
+ prio = nice(-1);
+ if (prio == -1 && errno != 0) {
+ //if not root we end up here
+ perror("nice(-1)" );
+ } else {
+ printf("nice(-1) = %d\n", prio );
+ }
+ }
+
+ /*
+ #sched.h
+
+ Get or set scheduler information
+
+ POSIX 7 specifies four scheduling policies, more can be defined by the implementation
+
+ The precise decription of those policies can be found under System interfaces > Scheduling policies.
+
+ TODO0 what happens if there is both a FIFO and a RR process running?
+
+ - SCHED_FIFO:
+
+ First in first out. Process runs untill it finishes, blocking the CPU while it runs.
+
+ An infinite loop could be catastrophic.
+
+ In linux, applied to realtime process.
+
+ - SCHED_RR:
+
+ Round robin.
+
+ Assign time slices proportional to prio and turn around the pie.
+
+ In linux, applied to realtime process.
+
+ - SCHED_SPORADIC:
+
+ TODO0
+
+ Optional, so don't rely on it.
+
+ - SCHED_OTHER:
+
+ Any other type of scheduling.
+
+ In linux, includes SCHED_NORMAL, the normal scheduling policy.
+
+ POSIX explicitly says that it is implementation defined what happens
+ when there are both process of `SCHED_OTHER` and another type at the same time.
+
+ #sched_getscheduler
+
+ pid_t for given pid, 0 for current process
+
+ #sched_get_priority_max
+
+ Get maximum possible priority for a given policy.
+
+ #sched_get_priority_min
+ */
+ {
+ printf("SCHED_FIFO = %d\n", SCHED_FIFO );
+ printf("sched_get_priority_max(SCHED_FIFO) = %d\n", sched_get_priority_max(SCHED_FIFO));
+ printf("sched_get_priority_min(SCHED_FIFO) = %d\n", sched_get_priority_min(SCHED_FIFO));
+
+ printf("SCHED_RR = %d\n", SCHED_RR );
+ printf("sched_get_priority_max(SCHED_RR) = %d\n", sched_get_priority_max(SCHED_RR));
+ printf("sched_get_priority_min(SCHED_RR) = %d\n", sched_get_priority_min(SCHED_RR));
+
+ //printf("SCHED_SPORADIC = %d\n", SCHED_SPORADIC );
+
+ printf("SCHED_OTHER = %d\n", SCHED_OTHER );
+ printf("sched_get_priority_max(SCHED_OTHER) = %d\n", sched_get_priority_max(SCHED_OTHER));
+ printf("sched_get_priority_min(SCHED_OTHER) = %d\n", sched_get_priority_min(SCHED_OTHER));
+
+ printf("sched_getscheduler(0) = %d\n", sched_getscheduler(0 ) );
+
+ /*
+ #sched_setscheduler()
+
+ You need root permissions to change to higher priority modes such as from SCHED_NORMAL to SCHED_FIFO or SCHED_RR.
+ */
+ {
+ int policy = SCHED_FIFO;
+ struct sched_param sched_param = {
+ .sched_priority = 99
+ };
+
+ if (sched_setscheduler(0, policy, &sched_param) == -1 ) {
+ perror("sched_setscheduler");
+ //exit(EXIT_FAILURE);
+ } else {
+ assert(sched_getscheduler(0) == policy );
+ }
+ }
+
+ /*
+ Bogging the computer down.
+
+ Time to have some destructive fun by making your computer bog down because of infinite loops on real time processes.
+
+ This will generate an infinite number of `SCHED_FIFO` classes with maximum priority.
+
+ *SAVE ALL DATA BEFORE DOING THIS!!!*
+
+ Needs root to work.
+
+ I recommend running this with music on the background to see the music break up.
+ The mouse also becomes very irresponsive.
+
+ However userspace system does not halt. It seems that POSIX does not specify how operating systems
+ should deal with different simultaneous scheduling policies, and my Linux does not let SCHED_FIFO run 100% of the time.
+ maybe as explained here: .
+
+ I could therefore kill the parent and all children with a C-C on the starting terminal.
+ */
+ if (0) {
+ int policy = SCHED_FIFO;
+ struct sched_param sched_param = {
+ .sched_priority = sched_get_priority_max(policy)
+ };
+
+ if (sched_setscheduler(0, policy, &sched_param) == -1 ) {
+ perror("sched_setscheduler");
+ } else {
+ while (1) {
+ pid_t pid = fork();
+ if (pid == -1) {
+ perror("fork");
+ assert(false);
+ } else {
+ if (pid == 0)
+ break;
+ }
+ }
+ while (1);
+ }
+ }
+ }
+
+ //#execl, execlp, execsle, execv, execvp, execvpe
+ {
+ /*
+ interfaces for ``execve`` system call
+
+ execute and *leave*, ends current process!!
+
+ common combo: fork + execl
+
+ this is effective because of COW implemented on some systems:
+ memory will only be copied to new process if needed, and in this case it is no needed.
+
+ takes variable number or args
+
+ must end null terminated
+
+ versions:
+
+ - char 'p': path, uses PATH var to find executable
+ - TODO: char 'v', char 'e'? what's the difference?
+
+ sample calls:
+
+ execl("/bin/ls", "-l", "-h", NULL);
+
+ execlp("ls", "-l", "-h", NULL);
+
+ execlp("cprogram", "cprogram", "arg0", NULL);
+
+ don't forget that in a c program the first arg is the program name
+ */
+ }
+
+ /*
+ #fork
+
+ Makes a copy of this process.
+
+ This includes open file descriptors.
+
+ Global memory space (`.DATA` and `.BSD`) is copied to current value but separated
+ (unlike threads, which share memory space).
+
+ #fork and buffering
+
+
+
+ There are three buffering methods:
+
+ - unbuffered
+ - fully buffered
+ - line buffered
+
+ When you fork, the streams get forked too,
+ with unflushed data still inside
+
+ stdout and stderr flush at newlines
+ if you don't put newlines, if does not flush,
+ and fork copies the buffers
+
+ This will print everything twice.
+
+ #vfork
+
+ fork but keep same address space. POSIX 7 discourages its use,
+ and says that it may be deprecated in the future
+
+ #wait
+
+ Wait for any child to terminate and then wake up.
+
+ Same as:
+
+ waitpid(-1, &status, 0);
+
+ #waitpid
+
+ Wait for child with given PID to terminate.
+
+ #copy on write #COW
+
+ often the fork is followed by an operation which does not use the old memory
+ such as `exec`, making copying the data useless.
+
+ some operating systems may at first not copy memory from old process,
+ but wait util memory is written to do that.
+
+ this has page granularity (physical RAM parameter, larger than most variables)
+ */
+ {
+ //this variable will be duplicated on the parent and on the child
+ int i = 0;
+ int ppid; /* parent pid */
+
+ ppid = getpid();
+ if (ppid == -1) {
+ perror("getpid");
+ exit(EXIT_FAILURE);
+ }
+
+ //this variable is visible only by the parent
+ //TODO0 is the compiler smart enough not to duplicate this to the child?
+ {
+ //int i = 0;
+ }
+
+ puts("fork parent only before child");
+
+ //flush before fork so that existing output won't be repeated twice:
+ fflush(stdout);
+
+ //in case of success, pid is set differently on parent and child
+ //so you can distinguish between them. For the child, `pid = 0`.
+ pid_t pid = fork();
+ if (pid == -1) {
+ perror("fork");
+ assert(false);
+ } else {
+ puts("fork child and parent");
+
+ //happens on child only:
+ if (pid == 0) {
+ /*
+ this puts is assynchonous with the process stdout
+
+ so it might not be in the line program order
+
+ but they both go to the same terminal
+ */
+ puts("fork child only");
+
+ //child has a different pid from its parent
+ int pid = getpid();
+ if (pid == -1) {
+ perror("getpid");
+ exit(EXIT_FAILURE);
+ }
+ assert(pid != ppid);
+
+ //this shall only change the child's `i` because memory was cloned (unlike threads)
+ i++;
+
+ //the child exits here:
+ exit(EXIT_SUCCESS);
+ }
+
+ //happens on parent only, before or after child.
+ puts("fork parent only");
+
+ //parent waits for the child to end.
+ //only the parent reaches this point because of the exit call
+ //done on the child above
+ int status;
+ wait(&status);
+ assert(status == EXIT_SUCCESS);
+
+ //happens on parent and only after child:
+ puts("fork parent only after child");
+
+ //memory was cloned, parent i was only modified in child memory
+ assert(i == 0);
+ }
+ }
+
+ /*
+ Forking too many processes
+
+ Time to have some destructive fun and test the limits on how many processes the system can have.
+
+ This will generate an infinite number of processes. Each one will sleep and terminate.
+
+ **SAVE ALL DATA BEFORE DOING THIS!!!**
+
+ My computer bogged down and I could do nothing about it except turn off the power.
+ */
+ if (0) {
+ while (1) {
+ pid_t pid = fork();
+ if (pid == -1) {
+ perror("fork");
+ assert(false);
+ }
+ }
+ }
+
+ /*
+ #IPC
+
+ inter process communication
+
+ the basic ways are:
+
+ at startup:
+
+ - command line arguments
+ - environment variables
+
+ during execution:
+
+ - pipes
+ - regular files
+ - signals
+ - shared memory
+ - sockets
+ */
+
+ /*
+ #pipe
+
+ Must read man page:
+
+ man 7 pipe
+
+ Unidirectional data transfer from the write side to the read side.
+
+ The write side in on one process (producer)
+ and the read side on another one (consumer).
+
+ The consumer and producer can be the same process, but in that case pipes are useless.
+
+ There are two types of pipes:
+
+ - unnamed pipes
+ - FIFOs
+
+ Both pipe sides are represented by either a file descriptor int or by a `FILE*`,
+ and many operations on them are the same as operation on disk files using
+ functions such as `write` and `read`.
+
+ #pipes are RAM only
+
+ Pipes are meant to be implementable on RAM only without using the filesystem.
+
+ This means that reading and writing to pipes is potentially much faster
+ than reading and writing to files.
+
+ The price to pay is that some operations
+ such as `ftell` and `fseek` are not possible.
+ There are also fcntl flags which cannot be applied to pipes.
+
+ #read and write operations on pipes
+
+ Read and write operations may be slightly differnt depending on the file descriptor type.
+
+ This is specially the case for pipes vs regular files with respect to blocking.
+
+ For example, pipes usually block if there is no more data available, and wait for data to become available.
+
+ See the documentation of read and write for the details.
+
+ #forbidden pipe operations
+
+ Since pipes are meant to be implemented on RAM only, pipes cannot be rewinded.
+
+ For example, using `ftell` on a pipe generate `errno=ESPIPE`.
+
+ Therefore, once the data has been read from the read side it can be discareded by the OS,
+ preventing all its RAM from being used up easily.
+
+ It seems that the only way to ask for forgiveness rather than permission and just do a `ftell`.
+
+
+ #advantages over using files
+
+ Potentially faster because possible to be RAM only.
+
+ If the read happens before the write it blocks untill the write is done.
+ This lets the OS manage all the synchronization.
+
+ #pipe capacity
+
+ There is a maximum data ammount that can be writen to a pipe.
+
+ If a process tries to write more data than the maximum to the pipe, it blocks until
+ part of the data is read.
+
+ This is a good source of deadlocks!
+
+ It seems that it is not possible to get the maximum pipe capacity:
+ .
+ */
+ {
+ /*
+ #unnamed pipe
+
+ Can be created either via `popen` or `pipe`
+
+ A single process must start two children process:
+ the data source and the data consumer and connect them
+
+ Advantages over named pipes:
+
+ - simple: no need to agree on a filename to communicate over
+ - secure: other process cannot se the data as they could in a file
+
+ i think it is not possible to know if a file pointer
+ is open for reading or writtin besides looking at how
+ it was created
+
+ workflow:
+
+ - child fills the buffer, then parent takes control
+ - child fills ...
+ */
+
+ /*
+ #popen
+
+ man popen
+
+ Creates unnamed pipes.
+
+ Consider using ansi c `system` instead of this.
+
+ Opens a new process running the given command.
+
+ Given string runs inside `sh` in a separated process and therefore it is:
+
+ - slow
+ - does magic stuff like pathname expansion (`*.txt`)
+ - harder to port to non posix systems if one day you decide to do that
+
+ It seems from the POSIX docs that the interpreter `sh` cannot be changed.
+
+ Unlike the ANSI C `system` function,
+ does not automatically wait for the command to return: see `pclose` for that.
+
+ For that reason, `popen` is slightly more general than `system`,
+ as you can do more work in the current program begore getting the shell output.
+
+ This is not however extremely useful since you usually need the shell output
+ to continue working anyways.
+
+ It is possible to either:
+
+ - read output from stdout of a command
+ - write to stdin of a command
+
+ TODO0 possible to both set stdin *and* get stdout?
+
+ The output of popen is put on an unnamed pipe, which is accessible via
+ ANSI C FILE type returned by the function, instead of POSIX file descriptor (integers)
+ Therefore you must use ANSI C file io functions like `fopen` or `fclose` with it,
+ instead of the more low level POSIX `open` or `write` family.
+
+ errno may or not be set depending on what caused the error.
+
+ #pclose
+
+ waits for child generated process.
+
+ returns child exit status.
+
+ if child was already waited for, returns -1 to indicate an error.
+
+ errno may or not be set depending on what caused the error.
+ */
+ {
+ //read from stdout of command and get its exit status
+ {
+ //popen uses ANSI C fread which uses ANSI C FILE type:
+ FILE* read_fp;
+ char buffer[BUFSIZ + 1];
+ char cmd[1024];
+ int chars_read;
+ int exit_status;
+ int read_cycles = 0;
+ int desired_read_cycles = 3;
+ int desired_last_char_read = 1;
+ assert(desired_last_char_read < BUFSIZ);
+
+ sprintf(
+ cmd,
+ "for i in `seq %ju`; do echo -n a; done",
+ (uintmax_t)(desired_read_cycles-1) * BUFSIZ + desired_last_char_read
+ );
+ read_fp = popen(cmd, "r");
+ if (read_fp == NULL) {
+ printf("popen failed\n");
+ exit(EXIT_FAILURE);
+ } else {
+ do
+ {
+ chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp );
+ buffer[chars_read] = '\0';
+ printf("======== n bytes read: %d\n", chars_read);
+ //printf("%s\n", buffer); //if you want to see a bunch of 'a's...
+ read_cycles++;
+ } while (chars_read == BUFSIZ);
+
+ exit_status = pclose(read_fp);
+ if (exit_status == -1) {
+ printf("pclose failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ assert(read_cycles == desired_read_cycles);
+ assert(chars_read == desired_last_char_read);
+ assert(exit_status == 0);
+ }
+ }
+
+ //write to stdin of command, its stdout goes to current stdout
+ {
+ FILE *write_fp;
+ char buf[BUFSIZ];
+ int exit_status;
+
+ memset(buf, 'b', BUFSIZ);
+
+ //counts how many charaters were given to stdin
+ //using many sh features to make it clear that a sh interpreter is called
+ write_fp = popen("read A; printf 'popen write = '; printf $A | wc -c", "w");
+
+ if (write_fp == NULL) {
+ assert(false);
+ } else {
+ //TODO0 is safe to write twice BUFSIZ without a newline in the middle?
+ fwrite(buf, sizeof(char), BUFSIZ, write_fp );
+ fwrite(buf, sizeof(char), BUFSIZ, write_fp );
+
+ //prints 2 * BUFSIZ
+ //command waits until pipe close
+ exit_status = pclose(write_fp);
+ assert(exit_status == 0);
+ }
+ }
+ }
+
+ /*
+ #pipe
+
+ Create unnamed pipes.
+
+ Very close to the linux pipe system call.
+
+ Differences from popen:
+
+ - does not use a shell, avoiding many of its problems
+
+ - uses two integer file descriptors, one for each end of the pipe,
+ instead of ANSI C `FILE` typedef.
+
+ Therefore you manipulate pipes with file descriptor functions
+ like `open` and `write` instead of ANSI C `fopen` family.
+
+ This potentially gives you more control over the operations.
+
+ It may however be a bit harder to setup.
+
+ Typically used with fork + exec.
+ */
+ {
+ /*
+ Producer and consumer are the same process.
+
+ Useuless but simple illustration of a pipe call.
+ */
+ {
+ int nbytes;
+ int pipes[2];
+ char data[] = "123";
+ char buf[BUFSIZ + 1];
+
+ if (pipe(pipes) == -1 ) {
+ perror("pipe");
+ exit(EXIT_FAILURE);
+ } else {
+ nbytes = write(pipes[1], data, strlen(data));
+ assert(nbytes == strlen(data));
+ nbytes = read(pipes[0], buf, BUFSIZ);
+ assert(nbytes == strlen(data));
+ buf[nbytes] = '\0';
+ assert(strcmp(buf, data) == 0 );
+ }
+ }
+
+ /*
+ Parent writes to child.
+
+ This works because if ever read happens before, it blocks.
+ */
+ {
+ int nbytes;
+ int file_pipes[2];
+ const char data[] = "123";
+ char buf[BUFSIZ + 1];
+ pid_t pid;
+
+ if (pipe(file_pipes) == -1 ) {
+ perror("pipe");
+ exit(EXIT_FAILURE);
+ } else {
+ fflush(stdout);
+ pid = fork();
+ if (pid == -1) {
+ perror("fork");
+ exit(EXIT_FAILURE);
+ } else {
+ if (pid == 0) {
+ //child only
+
+ //if read happens before write, it blocks because there is no data
+ nbytes = read(file_pipes[0], buf, BUFSIZ);
+
+ printf("pipe child. data: %s\n", buf);
+ exit(EXIT_SUCCESS);
+ }
+
+ //parent only
+ nbytes = write(file_pipes[1], data, strlen(data));
+ assert(nbytes == strlen(data));
+
+ int status;
+ wait(&status);
+
+ //parent only after child
+ assert(status == EXIT_SUCCESS);
+ }
+ }
+ }
+ }
+
+ /*
+ #FIFO
+
+ aka named pipes
+
+ appear on the filesystem
+
+ therefore can be accessed as by any process who sees it
+ and has enough priviledge level
+
+ are however faster than writting to files,
+ since everything happens on RAM
+
+ cannot open for rw
+
+ application: simple client/servers!
+
+ created with mkfifo
+
+ #mkfifo
+
+ Create a FIFO.
+
+ #mknod
+
+ Create a FIFO, character device, block device, directory or file.
+
+ int mknod(const char *path, mode_t mode, dev_t dev);
+
+ POSIX only demands that this work for FIFOs, all other are optional suggested interfaces.
+
+ The type is ored into mode and can be:
+
+ S_IFIFO
+ S_IFCHR
+ S_IFDIR
+ S_IFBLK
+ S_IFREG //regular file
+
+ however implementation need only support `S_IFIFO`.
+
+ `dev` represents the device corresponding to the file, and must be `0` in the case of `S_IFIFO`,
+ since FIFOs have no associated devices.
+
+ Linux implements all the options except `S_IFDIR` via the `sys_mknod` syscall.
+ It also adds `S_IFSOCK` for UNIX domain sockets. Glibc provides a wrapper for all the extended linux
+ functionality.
+ */
+ {
+ }
+
+ /*
+ #PIPE_BUF
+
+ Maximum size that guarantees that a pipe write operation will be atomic.
+
+ PIPE_BUF is guaranteed to be at least 512.
+
+ See `man 7 pipe` for an explanation.
+
+ Quoting POSIX 7:
+
+ If path refers to a FIFO, or fildes refers to a pipe or FIFO,
+ the value returned shall apply to the referenced object.
+
+ If path or fildes refers to a directory, the value returned shall apply to any FIFO that exists
+ or can be created within the directory.
+
+ If path or fildes refers to any other type of file,
+ it is unspecified whether an implementation supports an association of the variable name with the specified file.
+
+ Conclusions:
+
+ - the PIPE_BUF of an unnamed pipe can only be determined after it is created via `fpathconf`.
+ - the PIPE_BUF of FIFOs is the same for all fifos on given directory.
+
+ It can be retreived with `pathconf`.
+ */
+ {
+ long pipe_buf;
+ int pipes[2];
+
+ //unnamed pipe
+ if (pipe(pipes) == -1 ) {
+ perror("pipe");
+ exit(EXIT_FAILURE);
+ } else {
+ pipe_buf = fpathconf(pipes[0], _PC_PIPE_BUF);
+ printf("PIPE_BUF pipes[0] = %ld\n", pipe_buf);
+ assert(pipe_buf >= 512);
+
+ pipe_buf = fpathconf(pipes[1], _PC_PIPE_BUF);
+ printf("PIPE_BUF pipes[1] = %ld\n", pipe_buf);
+ assert(pipe_buf >= 512);
+ }
+
+ //directory
+ pipe_buf = pathconf(".", _PC_PIPE_BUF);
+ printf("PIPE_BUF \".\" = %ld\n", pipe_buf);
+ assert(pipe_buf >= 512);
+ }
+ }
+
+ /*
+ #shared memory
+
+ Memory that can be accessed by multiple separate processes.
+
+ In this example, both parent and child processes access the same shared memory.
+ */
+ {
+ int shmid;
+ int *shmem;
+
+ /*
+ #shmget
+
+ Allocate shared memory
+
+ int shmget(key_t key, size_t size, int shmflg);
+
+ - key: TODO0 what is this
+ - size: num of bytes
+ - shmflg: permission flags like for files + other specific flags
+ IPC_CREAT is required to allocate the memory
+
+ Return:
+
+ - negative if error
+ - unique identifier of memory if positive
+ */
+ {
+ shmid = shmget((key_t)1234, sizeof(int) * 2, IPC_CREAT | S_IRWXU | S_IRWXO );
+ assert(shmid >= 0);
+ }
+
+ /*
+ #shmat
+
+ Attach shared memory to current process so it can be used afterwards
+
+ void *shmat(int shm_id, const void *shm_addr, int shmflg);
+
+ - shm_id: memory id given to shmget
+ - shm_id: where to map to. Usual choice: `NULL` so the system choses on its own.
+ - shm_flg:
+ */
+ {
+ shmem = shmat(shmid, NULL, 0);
+ if (shmem == (void*)-1) {
+ assert(false);
+ } else {
+ shmem[0] = 1;
+ fflush(stdout);
+ pid_t pid = fork();
+ if (pid < 0) {
+ assert(false);
+ }
+ else {
+
+ //child only
+ if (pid == 0) {
+
+ //child inherits attached memory
+ shmem[0]++;
+
+ //detach from child:
+ assert(shmdt(shmem) == 0 );
+
+ exit(EXIT_SUCCESS);
+ }
+
+ int status;
+ wait(&status);
+ //parent after child
+ assert(status == EXIT_SUCCESS);
+ assert(shmem[0] == 2);
+
+ /*
+ #shmdt
+
+ detach shared memory from current process
+
+ int shmdt(void* shmem);
+
+ each process should detach it separatelly before deleting the memory
+ */
+ {
+ //detach from parent:
+ assert(shmdt(shmem) == 0 );
+ }
+
+ /*
+ #shmctl
+
+ controls the shared memory, doing amongst other things its deletion
+
+ int shmctl(int shm_id, int command, struct shmid_ds *buf);
+
+ - shm_id returned by shmget
+ - command one of the following:
+
+ - IPC_STAT: get parameters of memory into buf
+ - IPC_SET: set parameters of memory to match buf
+ - IPC_RMID: delete memory.
+
+ It must be detached from all processes, or you get unspecified behaviour.
+
+ - buf the following struct:
+
+ struct shmid_ds {
+ uid_t shm_perm.uid;
+ uid_t shm_perm.gid;
+ mode_t shm_perm.mode;
+ }
+
+ it can be `NULL` for `PIC_RMID`
+
+ - return: 0 on success, -1 on failure
+ */
+ {
+ assert(shmctl(shmid, IPC_RMID, NULL) == 0 );
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ #threads
+
+ See pthread.
+
+ #pthread
+
+ Sources:
+
+ -
+
+ Posix threads.
+
+ Complex model with around 100 functions prefixed by `pthread`.
+
+ c11 will introduce a standard threading model,
+ so in time this may become less important
+
+ Each thread has its own stack, but unlike process, global memory is shared.
+
+ Quicker to start than a process because less resource copy is needed.
+
+ In Linux, based on the `clone` system call.
+
+ In gcc you must compile with `-pthread`.
+
+ #thread synchronization mechanisms
+
+ - mutexes - Mutual exclusion lock: Block access to variables by other threads.
+ This enforces exclusive access by a thread to a variable or set of variables.
+
+ - joins - Make a thread wait till others are complete (terminated).
+
+ - condition variables - data type pthread_cond_t
+
+ Good tutorial:
+
+ #pthread_create
+
+ Create a new thread.
+
+ int pthread_create(
+ pthread_t *restrict thread,
+ const pthread_attr_t *restrict attr,
+ void *(*start_routine)(void*),
+ void *restrict arg
+ )
+
+ - thread:
+
+ unique id of the created thread
+
+ can be retreived from the thread with `pthread_self()`
+
+ In POSIX all threads of a process have the same PID. TODO confirm with reference
+
+ - attr:
+ - start_routine: function that runs the thread code
+ - arg: argument to start_routine
+
+ #pthread_join
+
+ Wait for a given thread to terminated.
+
+ If it has already terminated, does not wait.
+
+ #pthread_self
+
+ Get thread id of current running thread.
+
+ vs linux gettid:
+
+ #pthread_mutex methods
+
+ Allows a single thread to enter some code region.
+
+ #PTHREAD_MUTEX_INITIALIZER
+
+ New pthread_mutex_t should be initialized to it.
+
+ #pthread_mutex_lock
+
+ Acquire mutex: from now one no one else can enter.
+
+ #pthread_mutex_unlock
+
+ Release mutex: from now one others can enter.
+ */
+ {
+ pthread_t threads[NUM_THREADS];
+ int thread_args[NUM_THREADS];
+ int rc, i;
+
+ /* create all threads */
+ for (i = 0; i < NUM_THREADS; ++i)
+ {
+ thread_args[i] = i;
+ rc = pthread_create(&threads[i], NULL, main_thread, (void *) &thread_args[i]);
+ assert(rc == 0);
+ printf("created thread: %ju\n", (uintmax_t)threads[i]);
+ }
+
+ /* wait for all threads to complete */
+ for (i = 0; i < NUM_THREADS; ++i)
+ {
+ rc = pthread_join(threads[i], NULL);
+ if(rc != 0) {
+ printf("%s\n", strerror(rc) );
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ /*
+ #thread synchronization
+
+ Threads can be synchronized via:
+
+ - semaphores
+
+ - mutexes
+
+ Threads have the specific synchronization mechanisms:
+
+ #semaphore
+
+ #mutex
+ */
+ {
+ /*
+ #sched_yield
+
+ excplicitly tell scheduler to schedule another process
+ */
+ {
+ sched_yield();
+ }
+ }
+
+ /*
+ #netdb.h
+
+ network information (NETwork DataBase)
+
+ */
+ {
+ /*
+ #gethostname
+
+ Copies name of current host on given string:
+
+ int gethostname(char* hostname, int namelength);
+
+ Name is truncated to namelength if too large
+ */
+ {
+ const int namelength = 256;
+ char hostname[namelength];
+ gethostname(hostname, namelength);
+ printf("gethostname = %s\n", hostname);
+ }
+
+ /*
+ #gethostbyname
+
+ Give a hostname string ("localhost", "john") and get info on that host,
+ including its IP.
+
+ struct hostent *gethostbyname(const char *name);
+
+ Return value:
+
+ struct hostent {
+ char *h_name; // name of the host
+ char **h_aliases; // list of aliases (nicknames)
+ int h_addrtype; // address type
+ int h_length; // length in bytes of the address
+ char **h_addr_list // list of address (network order)
+ };
+
+ `NULL` on error
+
+ #gethostbyaddr
+
+ Same as gethostbyname but by address.
+ */
+ {
+ const int namelength = 256;
+ char **names;
+ char **addrs;
+ struct hostent* hostent;
+ char hostnames[2][namelength];
+
+ if (gethostname(hostnames[0], namelength) == -1 ) {
+ perror("gethostname");
+ exit(EXIT_FAILURE);
+ }
+
+ strcpy(hostnames[1], "www.google.com");
+
+ for (int i = 0; i < 2; i++) {
+ hostent = gethostbyname(hostnames[i]);
+ //hostent = gethostbyname("www.google.com");
+ if (hostent == NULL) {
+ fprintf(stderr, "gethostbyname failed for hostname = %s\n", hostnames[i]);
+ } else {
+ printf("gethostbyname\n");
+ printf(" name: %s\n", hostent -> h_name);
+ printf(" aliases:\n");
+ names = hostent -> h_aliases;
+ while (*names) {
+ printf(" %s\n", *names);
+ names++;
+ }
+ //assert that it is an inet address
+ if (hostent -> h_addrtype != AF_INET) {
+ printf("host is not AF_INET\n");
+ exit(EXIT_FAILURE);
+ }
+
+ //show addresses
+ printf(" IPs:\n");
+ addrs = hostent->h_addr_list;
+ while (*addrs) {
+ /*
+ #inet_ntoa
+
+ Converts integer representation of ip (4 bytes) to a string.
+
+ Takes network byte ordering into consideration.
+ */
+ printf(" %s", inet_ntoa(*(struct in_addr*)*addrs) );
+ addrs++;
+ }
+ printf("\n");
+ }
+ }
+ }
+
+ /*
+ POSIX requires that systems must keep a database that given a protocol links:
+ service name (strings) to their ports and vice versa.
+
+ #servent struct
+
+ Represents a service.
+
+ Must contain at least the following fields:
+
+ char *s_name Official name of the service.
+ char **s_aliases A pointer to an array of pointers to
+ alternative service names, terminated by
+ a null pointer.
+ int s_port The port number at which the service
+ resides, in network byte order.
+ char *s_proto The name of the protocol to use when
+ contacting the service.
+
+ #getservbyport
+
+ struct servent *getservbyport(int port, const char *proto);
+
+ - protocol: protocol name to look for.
+
+ If `NULL`, match the first service found on that port for any protocol.
+
+ #getservbyname
+
+ Same as getservbyport but using the service name itself
+
+ struct servent *getservbyname(const char *name, const char *proto);
+ */
+ {
+ struct servent *servent;
+ int port;
+ char proto[] = "tcp";
+ char name[] = "http";
+
+ /*
+ TODO what is a service? PING, HTTP, etc. application layer stuff?
+ How to gete those working then?
+ */
+
+ port = 80;
+ servent = getservbyport(port, proto);
+ if (servent == NULL) {
+ fprintf(stderr, "getservbyport(%d, %s) failed\n", port, proto);
+ } else {
+ printf("getservbyport\n");
+ printf(" s_name = %s\n", servent->s_name);
+ printf(" s_proto = %s\n", servent->s_proto);
+ }
+
+ servent = getservbyname(name, proto);
+ if (servent == NULL) {
+ fprintf(stderr, "getservbyname(%s, %s) failed\n", name, proto);
+ } else {
+ printf("getservbyname\n");
+ printf(" s_name = %s\n", servent->s_name);
+ printf(" s_port = %d\n", servent->s_port);
+ printf(" s_proto = %s\n", servent->s_proto);
+ }
+ }
+
+ /*
+ #getprotobyname
+
+ The system must implement a database that links protocol names to protocol identifier numbers.
+
+ Interface:
+
+ struct protoent *getprotobyname(const char *name);
+
+ `protoent` must contain at least the following fields:
+
+ char *p_name Official name of the protocol.
+ char **p_aliases A pointer to an array of pointers to
+ alternative protocol names, terminated by
+ a null pointer.
+ int p_proto The protocol number.
+ */
+ }
+
+ /*
+ #sync
+
+ Makes all cached writes to all filesystems.
+
+ OSes may keep disk writes for later for efficienty, grouping several writes into one.
+
+ This explicitly tells the OS to write everything down.
+
+ TODO what is an application for this, except before shutting down the system?
+
+ #fsync
+
+ Same as sync, but only for filesystem containing given fd.
+ */
+ {
+ sync();
+
+ int fd = open(__FILE__, O_RDONLY);
+ fsync(fd);
+ close(fd);
+ }
+
+ /*
+ #termios.h
+
+ Terminal management
+ */
+ {
+ //TODO0
+ }
+
+ /*
+ #terminal
+
+ some POSIX functions deal with the controlling terminal which called the program if any
+
+ #getlogin
+
+ gets login name of controlling terminal
+
+ note that this is different from getuid, since it looks at the controlling terminal,
+ and not at processes specific information.
+ */
+ {
+ char* login = getlogin();
+ if (login == NULL) {
+ perror("getlogin failed");
+ } else {
+ printf("getlogin() = %s\n", getlogin());
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/posix/signal.c b/posix/signal.c
new file mode 100644
index 0000000..7bf93ac
--- /dev/null
+++ b/posix/signal.c
@@ -0,0 +1,237 @@
+/*
+In short:
+
+- signals are a simple way for processes to communicate
+- signals are limited to passing a single byte between two processes
+
+To send arbitrary signals to a process from a terminal, consider using the `kill` utility
+(it does more than killing via a SIGTERM, as in general it sends any signal to a process)
+
+Also consider the convenient non POSIX stadardized VT100 control characters such as ``
+which generate certain signals such as `SIGTERM`.
+
+#ANSI
+
+ANSI C supports the concept of signals, and only POSIX specific features shall be discussed here.
+
+Please look for ANSI C info for any feature used but not explained here.
+
+Linux extends POSIX by adding new signals, Those shall not be discussed here.
+
+#POSIX
+
+Docs here: .
+
+POSIX defines several signals in addition to the ANSI C signals.
+
+As in ANSI C, each signal has the following attributes:
+
+- general description of which conditions generate the signal
+
+- the signal can or cannot be handled.
+
+ Most signals can be handled, but there are a few exceptions such as:
+
+ - `SIGKILL`: always kill processes, cannot be handled.
+ - `SIGKSTOP` and `SIGCONT`
+
+- default action to take
+
+ For signals that can be handled, you can change those behavious by creating your own handlers.
+
+ The possible default behaviours are:
+
+ - T:
+
+ Abnormal termination of the process.
+
+ The process is terminated with all the consequences of `_exit()` except that the status made available to
+ `wait()` and `waitpid()` indicates abnormal termination by the specified signal.
+
+ Default action for most signals.
+
+ - A :Abnormal termination of the process.
+
+ [XSI] [Option Start] Additionally, implementation-defined abnormal termination actions,
+ such as creation of a core file, may occur. [Option End]
+
+ Linux implements concept of core dumps on those cases.
+ Note however that those may be turned on or off depending on the system configuration.
+
+ - I: Ignore the signal.
+
+ An important example is `SIGCHLD`, which is generated when a child terminates,
+ but has no effect by default, since in general killing the parent is not what
+ should happen on most programs.
+
+ - S: Stop the process.
+
+ Mainly `SIGSTOP`.
+
+ - C: Continue the process, if it is stopped; otherwise, ignore the signal.
+
+ Mainly `SIGCONT`.
+
+POSIX specific signals include:
+
+- SIGKILL
+
+ Kills program.
+
+ Cannot be handled unlike to `SIGINT` and `SIGTERM`.
+
+- SIGQUIT
+
+ Quit program.
+
+ Used in case of abnormal termination (`A`), unlike `SIGINT` and `SIGTERM`.
+
+ May therefore generate a core dump on certain systems.
+
+ On Linux, you must first enable core dumps on the current terminal via `ulimit -c unlimited`,
+ since coredumps may be turned off by default.
+
+ Can be generated on VT100 with ``.
+
+- SIGSTOP
+
+ Freezes program.
+
+ `ctrl+z`, in linux terminals.
+
+ Cannot be handle.
+
+- SIGCONT
+
+ Continues a process that
+
+- SIGHUP
+
+ Controlling terminal was killed.
+
+ This is why killing the terminal kills most process by default unless those process implement a handler.
+
+- SIGPIPE
+
+ Process write to a pipe with no readers on other side
+
+- SIGCHLD
+
+ Child terminated, stopped or continued.
+
+ Ignored by default.
+
+- SIGALRM
+
+ Received after the alarm call after given no of secs.
+
+- SIGUSR1 and SIGUSR2: left to users to do whatever they want with
+
+#parent death signal
+
+ In POSIX, no signal needs to be sent to the child if the parent exits:
+
+ In Linux, this can be achieved via the `prctl` syscall.
+ This may seem surprising considering that:
+
+ - parents can wait for children
+
+ - children get a NOHUP when controling process is killed
+ This is mentioned at:
+
+ TODO what is a controlling process?
+
+#sources
+
+-
+
+ good intro
+
+- man 7 signal
+
+ man pages
+
+# TODO
+
+- determine where specification barriers betwwen ANSIC / POSIX / linux
+*/
+
+#define _XOPEN_SOURCE 700
+
+#include
+#include
+#include
+#include
+
+#include
+
+void signal_handler( int sig )
+{
+ //sig arg allows us to use a single function for several different signals:
+ //just look at it and decide which action to take based on the signal number
+
+ printf( "sig: %d\n", sig );
+
+ //after the signal is dealt with, the handler is then changed to its default action
+ //if you want to continue using this handler for future signals, you have to reregister
+ //it here: TODO confirm. If I remove this it does not work.
+
+ signal( sig, signal_handler );
+
+ //you can change the action handler at any time
+ //for example, if you uncomment this line, only the first signal will be ignored
+ //and but the second will be dealt with the default action:
+
+ //(void) signal( sig, SIG_DFL );
+}
+
+int main()
+{
+
+ /*
+ #signal
+
+ POSIX recommends `sigaction` instead of `signal`
+ */
+
+ signal( SIGALRM, signal_handler );
+
+ /*
+ #send signal
+
+ this is done via the kill function:
+
+ int kill(pid_t pid, int sig);
+
+ returns 0 on success
+
+ you must have permissions to send a signal to a program
+
+ TODO 0 which? same uid?
+ */
+
+ //send a SIGINT to ourselves:
+ assert( kill( getpid(), SIGALRM ) == 0 );
+
+ int i = 0;
+ while ( i < 3 )
+ {
+ /*
+ #alarm
+
+ sends a SIGALRM to ourselves in n seconds
+ */
+ assert( alarm( 1 ) == 0 );
+ printf( "%d\n", i );
+ i++;
+ sleep( 2 );
+ }
+
+ /*
+ #pause
+
+ Stop program until it receives a signal.
+ */
+
+ return EXIT_SUCCESS;
+}
diff --git a/posix/socket/Makefile b/posix/socket/Makefile
new file mode 120000
index 0000000..0c2ecfc
--- /dev/null
+++ b/posix/socket/Makefile
@@ -0,0 +1 @@
+../../Makefile_many
\ No newline at end of file
diff --git a/posix/socket/README.md b/posix/socket/README.md
new file mode 100644
index 0000000..2e3ea2b
--- /dev/null
+++ b/posix/socket/README.md
@@ -0,0 +1,92 @@
+Sockets are similar to pipes but:
+
+- allow communication across different systems and are thus a base for networks local sockets also exist.
+
+- allow bidirection communication
+
+- allow multiple clients to connet to a single server (the concepts of client and server are clearly defined)
+
+#What posix sockets can do
+
+POSIX sockets allows to implement any Application layer program, and thus to implement things like web browsers, crawlers or wget like utilities.
+
+It seems however that POSIX does not support lower level layer control, for exapmle making an ICMP echo
+
+For those functionalities it seems that Linux specific functionalities must be used for example raw sockets:
+
+#Socket params
+
+sockets are characterized by three parameters:
+
+- domain
+- type
+- protocol
+
+this are exactly the 3 parameters that the `socket` call receives.
+
+##Domain
+
+- `AF_UNIX`: local sockets for single machine usage
+
+ UNIX domain sockets are uniquelly identified on the filesystem like pipes or other special files
+
+- `AF_INET`: internet IP protocol, regular local networks or the internet
+
+ this is one of the few stream like resources that are not put into the filesystem because TODO
+
+- `AF_INET6`: IPv6
+
+##Type
+
+- `SOCK_STREAM`: connexion works like a file stream to the program
+
+ in `AF_INET` this is automatically done via TCP/IP
+
+ delivery and ordering is guaranteed by TCP/IP
+
+ a connection is maintained while data is being sent
+
+- `SOCK_DGRAM`: datagram
+
+ lower level protocol
+
+ does not establish connection
+
+ no automatic delivery guarantee
+
+ data must be manually split into packages of a maximum width
+
+ in `AF_INET` this is UDP
+
+certain domains may have differnt types
+
+`AF_UNIX` has a single type: `SOCK_STREAM`
+
+`AF_INET` has the following types:
+
+##Protocol
+
+sometimes it is possible to choose different protocols for a given type
+
+`0` uses the default protocol
+
+#Testing
+
+- run the server:
+
+ ./server &
+
+- run as many clients as you want:
+
+ ./client && ./client
+ ./client
+
+- kill the server:
+
+ fg
+
+ and then hit Ctrl-C
+
+#Local socket
+
+Is inserted into the filesystem.
diff --git a/posix/socket/inet/Makefile b/posix/socket/inet/Makefile
new file mode 100644
index 0000000..e47822b
--- /dev/null
+++ b/posix/socket/inet/Makefile
@@ -0,0 +1,34 @@
+# make all files with one of the given extensions by IN_EXTS
+# separatelly, possibly using a different rule per extension
+
+IN_DIR ?= ./
+IN_EXTS ?= .c
+OUT_DIR ?= _out/
+OUT_EXT ?=
+RUN ?= main
+
+INS := $(foreach IN_EXT, $(IN_EXTS), $(wildcard $(IN_DIR)*$(IN_EXT)) )
+INS_NODIR := $(notdir $(INS))
+OUTS_NODIR := $(basename $(INS_NODIR))
+OUTS_NODIR := $(addsuffix $(OUT_EXT), $(OUTS_NODIR))
+OUTS := $(addprefix $(OUT_DIR), $(OUTS_NODIR))
+
+.PHONY: all clean mkdir run
+
+all: mkdir $(OUTS)
+
+# rule to make the c files
+$(OUT_DIR)%$(OUT_EXT): %.c
+ gcc -Wall -std=c99 -pedantic-errors -o "$@" "$<"
+
+clean:
+ rm -rf $(OUT_DIR)
+
+mkdir:
+ mkdir -p $(OUT_DIR)
+
+run: all
+ cd $(OUT_DIR) && ./server$(OUT_EXT) &
+ cd $(OUT_DIR) && ./client$(OUT_EXT) &
+ cd $(OUT_DIR) && ./client$(OUT_EXT) &
+ cd $(OUT_DIR) && ./client$(OUT_EXT) &
diff --git a/posix/socket/inet/README.md b/posix/socket/inet/README.md
new file mode 100644
index 0000000..b24f18e
--- /dev/null
+++ b/posix/socket/inet/README.md
@@ -0,0 +1,6 @@
+inet sockets, can be used across different computers for networking
+
+will only comment on differences from the unix sockets
+
+once a connection has been made to a port, the port stays blocked,
+so be sure to take a port that is not being used
diff --git a/posix/socket/inet/client.c b/posix/socket/inet/client.c
new file mode 100644
index 0000000..9241f23
--- /dev/null
+++ b/posix/socket/inet/client.c
@@ -0,0 +1,106 @@
+/**
+@file
+
+Inet server example
+*/
+
+#define _XOPEN_SOURCE 700
+
+#include "assert.h"
+#include "stdbool.h"
+#include "stdio.h"
+#include "stdlib.h"
+
+#include
+#include //getprotobyname
+#include
+#include
+#include "unistd.h"
+
+int main( int argc, char** argv )
+{
+ char server_ip[] = "127.0.0.1";
+ unsigned short server_port = 12345;
+ in_addr_t server_addr;
+ int sockfd;
+ //this is the struct used by INet addresses:
+ struct sockaddr_in address;
+ char ch_init = 'a';
+ char ch = ch_init;
+ struct protoent *protoent;
+ char protoname[] = "tcp";
+ //char protoname[] = "udp";
+
+ protoent = getprotobyname( protoname );
+ if ( protoent == NULL ) {
+ perror( "getprotobyname" );
+ exit(EXIT_FAILURE);
+ }
+
+ sockfd = socket( AF_INET, SOCK_STREAM, protoent->p_proto );
+ if ( sockfd == -1 ) {
+ perror( "socket" );
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ #inet_addr
+
+ converts the text representation to a representation that can be used on the network
+
+ #s_addr
+
+ server address
+ */
+ server_addr = inet_addr( server_ip );
+ if ( server_addr == (in_addr_t)-1 ) {
+ fprintf( stderr, "inet_addr(\"%s\") failed\n", server_ip );
+ return EXIT_FAILURE;
+ }
+ address.sin_addr.s_addr = server_addr;
+
+ address.sin_family = AF_INET;
+
+ /*
+ #htons
+
+ Host TO Network Short
+
+ takes a short (2 bytes), and converts it to the correct byte ordering expected by the network
+
+ you must do this, or else the network won't look at the right port
+
+ versions:
+
+ - htons
+ - htonl (long, 4 bytes)
+ - ntohs (inverse)
+ - ntohl
+
+ #sin_port
+
+ port at which to contact server
+ */
+
+ address.sin_port = htons( server_port );
+
+ if ( connect( sockfd, ( struct sockaddr* )&address, sizeof( address ) ) == -1 ) {
+ perror( "connect" );
+ return EXIT_FAILURE;
+ }
+
+ if ( write( sockfd, &ch, 1 ) == -1 ) {
+ perror( "write" );
+ exit(EXIT_FAILURE);
+ }
+
+ /* according to wireshark, the response is received from some random port that the OS assigns to us */
+
+ if ( read( sockfd, &ch, 1 ) == -1 ) {
+ perror( "read" );
+ exit(EXIT_FAILURE);
+ }
+ close( sockfd );
+ assert( ch == ch_init + 1 );
+ exit( EXIT_SUCCESS );
+}
diff --git a/posix/socket/inet/server.c b/posix/socket/inet/server.c
new file mode 100644
index 0000000..376b60f
--- /dev/null
+++ b/posix/socket/inet/server.c
@@ -0,0 +1,81 @@
+#define _XOPEN_SOURCE 700
+
+#include "stdbool.h"
+#include "stdio.h"
+#include "stdlib.h"
+#include "string.h"
+
+#include
+#include //getprotobyname
+#include
+#include
+#include "unistd.h"
+
+int main(int argc, char** argv) {
+ unsigned short server_port = 12345;
+ char ch;
+ size_t client_len;
+ int server_sockfd, client_sockfd;
+ struct sockaddr_in client_address, server_address;
+ struct protoent *protoent;
+ char protoname[] = "tcp";
+ //char protoname[] = "udp";
+
+ protoent = getprotobyname(protoname);
+ if (protoent == NULL) {
+ perror("getprotobyname");
+ exit(EXIT_FAILURE);
+ }
+
+ server_sockfd = socket(
+ AF_INET,
+ SOCK_STREAM,
+ protoent->p_proto
+ //0
+ );
+ if (server_sockfd == -1) {
+ perror("socket");
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ #s_addr server
+
+ on server, this is which addresses it will accept connections from
+
+ #INADDR_ANY
+
+ special value that tells server to accept connections from anyone
+ */
+ server_address.sin_family = AF_INET;
+ server_address.sin_addr.s_addr = htonl(INADDR_ANY);
+ server_address.sin_port = htons(server_port);
+ if (bind(
+ server_sockfd,
+ (struct sockaddr*)&server_address,
+ sizeof(server_address)
+ ) == -1
+ ) {
+ perror("bind");
+ exit(EXIT_FAILURE);
+ }
+
+ if (listen(server_sockfd, 5) == -1) {
+ perror("listen");
+ exit(EXIT_FAILURE);
+ }
+
+ while (1) {
+ client_len = sizeof(client_address);
+ client_sockfd = accept(
+ server_sockfd,
+ (struct sockaddr*)&client_address,
+ &client_len
+ );
+ read(client_sockfd, &ch, 1);
+ ch++;
+ write(client_sockfd, &ch, 1);
+ close(client_sockfd);
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/posix/socket/unix/Makefile b/posix/socket/unix/Makefile
new file mode 100644
index 0000000..e47822b
--- /dev/null
+++ b/posix/socket/unix/Makefile
@@ -0,0 +1,34 @@
+# make all files with one of the given extensions by IN_EXTS
+# separatelly, possibly using a different rule per extension
+
+IN_DIR ?= ./
+IN_EXTS ?= .c
+OUT_DIR ?= _out/
+OUT_EXT ?=
+RUN ?= main
+
+INS := $(foreach IN_EXT, $(IN_EXTS), $(wildcard $(IN_DIR)*$(IN_EXT)) )
+INS_NODIR := $(notdir $(INS))
+OUTS_NODIR := $(basename $(INS_NODIR))
+OUTS_NODIR := $(addsuffix $(OUT_EXT), $(OUTS_NODIR))
+OUTS := $(addprefix $(OUT_DIR), $(OUTS_NODIR))
+
+.PHONY: all clean mkdir run
+
+all: mkdir $(OUTS)
+
+# rule to make the c files
+$(OUT_DIR)%$(OUT_EXT): %.c
+ gcc -Wall -std=c99 -pedantic-errors -o "$@" "$<"
+
+clean:
+ rm -rf $(OUT_DIR)
+
+mkdir:
+ mkdir -p $(OUT_DIR)
+
+run: all
+ cd $(OUT_DIR) && ./server$(OUT_EXT) &
+ cd $(OUT_DIR) && ./client$(OUT_EXT) &
+ cd $(OUT_DIR) && ./client$(OUT_EXT) &
+ cd $(OUT_DIR) && ./client$(OUT_EXT) &
diff --git a/posix/socket/unix/README.md b/posix/socket/unix/README.md
new file mode 100644
index 0000000..88cb9b7
--- /dev/null
+++ b/posix/socket/unix/README.md
@@ -0,0 +1 @@
+unix sockets, meant to be used locally on a single computer
diff --git a/posix/socket/unix/client.c b/posix/socket/unix/client.c
new file mode 100644
index 0000000..f40e212
--- /dev/null
+++ b/posix/socket/unix/client.c
@@ -0,0 +1,95 @@
+#define _XOPEN_SOURCE 700
+
+#include "assert.h"
+#include "stdbool.h"
+#include "stdio.h"
+#include "stdlib.h"
+#include "string.h"
+
+#include
+#include //sockaddr_un
+#include "unistd.h"
+
+int main( int argc, char** argv )
+{
+ //name of the socket file
+ //server and client must agree on it
+ char name[] = "server_socket";
+
+ //sockets are accessible via file descriptors
+ int sockfd;
+ int len;
+
+ //this is the struct used by UNix addresses
+ struct sockaddr_un address;
+ char ch_init = 'a';
+ char ch = ch_init;
+
+ /*
+ #socket
+
+ Create the socket, and get a file descrpitor to it.
+
+ This must be done by both clients and servers.
+
+ int socket(int domain, int type, int protocol);
+
+ - protocol:
+
+ For a given domain, select which protocol id to use.
+
+ `0` uses a default protocol for the domain.
+
+ Many domains have a single protocol.
+
+ Other do not, for example `AF_INET` has both `tcp` adn `udp`.
+
+ To get a protocol id, use `struct protoent *protoent = getprotobyname('tcp')`,
+ and then extract `protoent->p_proto`.
+ */
+
+ sockfd = socket( AF_UNIX, SOCK_STREAM, 0 );
+ if ( sockfd == -1 ) {
+ perror( "socket" );
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ #connect
+
+ request connection to the socket on the given address
+
+ if the socket file does not exist fails
+ */
+
+ //type of socket
+ address.sun_family = AF_UNIX;
+
+ //give a name to the socket
+ strcpy( address.sun_path, name );
+
+ len = sizeof( address );
+
+ if ( connect( sockfd, ( struct sockaddr* )&address, len ) == -1 ) {
+ perror( "connect" );
+ exit( EXIT_FAILURE );
+ }
+
+ if ( write( sockfd, &ch, 1 ) == -1 ) {
+ perror( "write" );
+ exit(EXIT_FAILURE);
+ }
+ if ( read( sockfd, &ch, 1 ) == -1 ) {
+ perror( "read" );
+ exit(EXIT_FAILURE);
+ }
+
+ //you should close the connection on both client and server
+
+ close( sockfd );
+
+ //assert that the server did its job of increasing the char we gave it
+ assert( ch == ch_init + 1 );
+
+ exit( EXIT_SUCCESS );
+}
diff --git a/posix/socket/unix/server.c b/posix/socket/unix/server.c
new file mode 100644
index 0000000..cdf5f9d
--- /dev/null
+++ b/posix/socket/unix/server.c
@@ -0,0 +1,170 @@
+#define _XOPEN_SOURCE 700
+
+#include "assert.h"
+#include "stdbool.h"
+#include "stdio.h"
+#include "stdlib.h"
+#include "string.h"
+
+#include
+#include
+#include //sockaddr_un
+#include "unistd.h"
+
+/**
+Simple server that takes one char per connection,
+increases it, and returns it to the client.
+*/
+int main( int argc, char** argv )
+{
+ //name of the socket file
+ //server and client must agree on it
+ char ch;
+ char name[] = "server_socket";
+ size_t client_len;
+ int server_sockfd, client_sockfd;
+ struct sockaddr_un client_address, server_address;
+
+ //remove any existing socket file and create a new one
+ if ( unlink( name ) == -1 && errno != ENOENT ) {
+ perror( "unlink" );
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ #setsockopt
+
+ Set several socket options
+
+ int setsockopt(int sockfd, int level, int optname,
+ const void *optval, socklen_t optlen)
+
+ TODO it seems that setting SO_REUSEADDR allows to immediately reuse this socket.
+ Otherwise, bind fails because the address is already used.
+
+ Discussion on the python interface:
+ */
+
+ /*
+ if ( setsockopt(
+ int sockfd,
+ int level,
+ int optname,
+ const void *optval,
+ socklen_t optlen
+ ) == -1 )
+ {
+ }
+ */
+
+ server_sockfd = socket( AF_UNIX, SOCK_STREAM, 0 );
+ if ( server_sockfd == -1 ) {
+ perror( "socket" );
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ #bind
+
+ Bind an address to the socket file.
+
+ May take different address formats depending on the socket parameters.
+
+ #AF_INET
+
+ In the AF_INET domain, the address is specified using a structure called sockaddr_in, defined in
+ netinet/in.h, which contains at least these members:
+
+ struct sockaddr_in {
+ short int; //AF_INET
+ unsigned short int; //port number
+ struct in_addr; //IP address
+ };
+
+ The IP address structure, in_addr, is defined as follows:
+
+ struct in_addr {
+ unsigned long int
+ };
+
+ The four bytes of an IP address constitute a single 32-bit value
+ */
+ server_address.sun_family = AF_UNIX;
+ strcpy( server_address.sun_path, name );
+ if ( bind(
+ server_sockfd,
+ (struct sockaddr*)&server_address,
+ sizeof( server_address )
+ ) == -1 )
+ {
+ perror( "bind" );
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ #listen
+
+ Create a connection queue
+
+ int listen(int socket, int backlog);
+
+ Backlog is the max queue size.
+
+ If overflows TODO.
+ */
+
+ if ( listen( server_sockfd, 5 ) == -1 ) {
+ perror( "listen" );
+ exit(EXIT_FAILURE);
+ }
+
+ //run server
+ while ( 1 )
+ {
+ /*
+ #accept
+
+ Accept connect from client.
+
+ For each accept a new file descriptor is created to communicate with the client.
+
+ Blocks until a connexion is requested by a client via `connect`
+ */
+ client_len = sizeof( client_address );
+ client_sockfd = accept(
+ server_sockfd,
+ (struct sockaddr*)&client_address,
+ &client_len
+ );
+ if ( client_sockfd == -1 ) {
+ perror( "accept" );
+ exit(EXIT_FAILURE);
+ }
+
+ if ( read( client_sockfd, &ch, 1 ) == -1 ) {
+ perror( "read" );
+ exit(EXIT_FAILURE);
+ }
+
+ ch++;
+
+ if ( write( client_sockfd, &ch, 1 ) == -1 ) {
+ perror( "write" );
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ #send
+
+ Could be used instead of write for sockets.
+
+ `write` is the same as a send without flags, so send has more options.
+ */
+
+ //you should close the connection on both client and server
+
+ close( client_sockfd );
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/posix/socket/wget.c b/posix/socket/wget.c
new file mode 100644
index 0000000..9cb777f
--- /dev/null
+++ b/posix/socket/wget.c
@@ -0,0 +1,90 @@
+/**
+@file
+
+Minimalistic error checked program that fetches a web page and prints it to stdout.
+*/
+
+#define _XOPEN_SOURCE 700
+
+#include "assert.h"
+#include "stdbool.h"
+#include "stdio.h"
+#include "stdlib.h"
+#include "string.h"
+
+#include
+#include //getprotobyname
+#include
+#include
+#include "unistd.h"
+
+int main(int argc, char** argv) {
+ char hostname[] = "www.google.com";
+ unsigned short server_port = 80;
+ char protoname[] = "tcp";
+ char request[] = "GET / HTTP/1.0\n\n";
+ int request_len = strlen(request);
+ in_addr_t server_addr;
+ int sockfd;
+ struct sockaddr_in address;
+ struct protoent *protoent;
+ char buff[BUFSIZ];
+ struct hostent* hostent;
+ int nbytes_total, nbytes_last;
+
+ // Build the socket.
+ protoent = getprotobyname(protoname);
+ if (protoent == NULL) {
+ perror("getprotobyname");
+ exit(EXIT_FAILURE);
+ }
+
+ sockfd = socket(AF_INET, SOCK_STREAM, protoent->p_proto);
+ if (sockfd == -1) {
+ perror("socket");
+ exit(EXIT_FAILURE);
+ }
+
+ // Build the address.
+ hostent = gethostbyname(hostname);
+ if (hostent == NULL) {
+ fprintf(stderr, "error: gethostbyname(\"%s\")\n", hostname);
+ exit(EXIT_FAILURE);
+ }
+
+ server_addr = inet_addr(inet_ntoa(*(struct in_addr*)*(hostent->h_addr_list)));
+ if (server_addr == (in_addr_t)-1) {
+ fprintf(stderr, "error: inet_addr(\"%s\")\n", *(hostent->h_addr_list));
+ exit(EXIT_FAILURE);
+ }
+ address.sin_addr.s_addr = server_addr;
+ address.sin_family = AF_INET;
+ address.sin_port = htons(server_port);
+
+ if (connect(sockfd, (struct sockaddr*)&address, sizeof(address)) == -1) {
+ perror("connect");
+ exit(EXIT_FAILURE);
+ }
+
+ nbytes_total = 0;
+ while (nbytes_total < request_len) {
+ nbytes_last = write(sockfd, request + nbytes_total, request_len - nbytes_total);
+ if (nbytes_last == -1) {
+ perror("write");
+ exit(EXIT_FAILURE);
+ }
+ nbytes_total += nbytes_last;
+ }
+
+ while ((nbytes_total = read(sockfd, buff, BUFSIZ)) > 0) {
+ write(STDOUT_FILENO, buff, nbytes_total);
+ }
+
+ if (nbytes_total == -1) {
+ perror("read");
+ exit(EXIT_FAILURE);
+ }
+
+ close(sockfd);
+ exit(EXIT_SUCCESS);
+}