Move posix C API form linux repo.

This commit is contained in:
Ciro Santilli 2014-06-03 10:50:21 +02:00
parent 10b9c2e9d3
commit 99ad8ab9ae
17 changed files with 4149 additions and 2 deletions

View File

@ -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.

1
posix/Makefile Symbolic link
View File

@ -0,0 +1 @@
../Makefile_many

1
posix/Makefile_params Normal file
View File

@ -0,0 +1 @@
LIBS := -lm -pthread

1
posix/README.md Normal file
View File

@ -0,0 +1 @@
POSIX C API.

3197
posix/main.c Normal file

File diff suppressed because it is too large Load Diff

237
posix/signal.c Normal file
View File

@ -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 `<C-C>`
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: <http://pubs.opengroup.org/onlinepubs/009696699/basedefs/signal.h.html>.
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 `<C-\>`.
- 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:
<http://stackoverflow.com/questions/284325/how-to-make-child-process-die-after-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: <http://pubs.opengroup.org/onlinepubs/009695399/functions/exit.html>
TODO what is a controlling process?
#sources
- <http://www.alexonlinux.com/signal-handling-in-linux>
good intro
- man 7 signal
man pages
# TODO
- determine where specification barriers betwwen ANSIC / POSIX / linux
*/
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
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;
}

1
posix/socket/Makefile Symbolic link
View File

@ -0,0 +1 @@
../../Makefile_many

92
posix/socket/README.md Normal file
View File

@ -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 <http://www.microhowto.info/howto/send_an_arbitrary_ipv4_datagram_using_a_raw_socket_in_c.html>
For those functionalities it seems that Linux specific functionalities must be used for example raw sockets: <http://www.pdbuchan.com/rawsock/rawsock.html>
#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.

View File

@ -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) &

View File

@ -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

106
posix/socket/inet/client.c Normal file
View File

@ -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 <arpa/inet.h>
#include <netdb.h> //getprotobyname
#include <netinet/in.h>
#include <sys/socket.h>
#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 );
}

View File

@ -0,0 +1,81 @@
#define _XOPEN_SOURCE 700
#include "stdbool.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include <arpa/inet.h>
#include <netdb.h> //getprotobyname
#include <netinet/in.h>
#include <sys/socket.h>
#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;
}

View File

@ -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) &

View File

@ -0,0 +1 @@
unix sockets, meant to be used locally on a single computer

View File

@ -0,0 +1,95 @@
#define _XOPEN_SOURCE 700
#include "assert.h"
#include "stdbool.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include <sys/socket.h>
#include <sys/un.h> //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 );
}

170
posix/socket/unix/server.c Normal file
View File

@ -0,0 +1,170 @@
#define _XOPEN_SOURCE 700
#include "assert.h"
#include "stdbool.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h> //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: <http://www.gossamer-threads.com/lists/python/bugs/714277>
*/
/*
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;
}

90
posix/socket/wget.c Normal file
View File

@ -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 <arpa/inet.h>
#include <netdb.h> //getprotobyname
#include <netinet/in.h>
#include <sys/socket.h>
#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);
}