Esercizi con System Call
Questi programmi contengono esempi di uso delle System-Call.
Il consiglio e' di compilarli, provarli e capirne passo-passo il funzionamento. (o spiegare perche' non funzionino).
Ovviamente potete proporre ulteriori esempi. Renzo (talk) 08:03, 2 November 2015 (CET)
programma 0
Il gatto di Schrödinger: Fork e' vero o falso?
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
if (fork())
printf("fork is true\n");
else
printf("fork is false\n");
usleep(100000);
return 0;
}
il programma stampa entrambi i risultati perché la system call fork() crea un processo figlio identico al padre, questa system call ha come valore di ritorno al thread padre il pid del processo figlio (che essendo diverso da 0 è true) e al thread figlio ritorna 0, quindi il processo padre riceve un valore diverso da 0 (pid del figlio) e stampa true, mentre il processo figlio riceve 0, non entra nel if e stampa, ovviamente false.
(Simone)
programma 1
Open() Write() Read()
/*IL PROGRAMMA APRE UN FILE SUL QUALE FA DELLE OPERAZIONI DI LETTURA E SCRITTURA*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_CHAR 100000
int main(int argc, char *argv[]){
int fd, c, n_read;
int fine = EOF;
int index = 0;
char arr[MAX_CHAR];
char c_arr_read[MAX_CHAR];
const char *path = //inserisci qui il percorso del file che vuoi aprire, se il file specificato non esiste, la open fallisce e il
//programma termina con un codice di errore
fd = open(path, O_RDWR);
if(fd == -1){
printf("fd: %d\n", fd);
printf("Open failed, Err: %d\n", errno);
exit(1);
}
if( (n_read = read(fd, &c_arr_read, MAX_CHAR)) > 0 ) {
printf("file non vuoto\nbytes letti: %d\n", n_read);
write(fd, &fine, 0);
} else printf("file vuoto\nbytes letti: %d\n", n_read);
//printf("fd: %d\n", fd);
printf("Open().\n");
while((c = getchar()) != EOF && index < MAX_CHAR){
arr[index] = c;
index++;
}
//printf("sizeof arr: %d\n", sizeof(arr));
write(fd, &arr, index);
return 0;
}
(Alessio)
programma 2
segnali + fork
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <pty.h>
#include <utmp.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUFSIZE 1024
sigset_t usr12mask;
sigset_t usr1mask;
int status;
int fd, fdout;
char name[80];
static int bit;
char *ar[2] = {"bash", NULL};
void usr12action(int num, siginfo_t *info, void *useless) {
if (num == SIGCHLD)
bit=-1;
else {
bit = (num == SIGUSR2);
kill(info->si_pid, SIGUSR1);
}
}
void usr1action(int num, siginfo_t *info, void *useless) {
}
void char_to_bin(unsigned char x, void *rec){
int i;
pid_t *r = rec;
for(i=7; i>=0; i--){
char f = x >> i & 1;
kill(*r,f?SIGUSR2:SIGUSR1);
sigsuspend(&usr1mask);
}
}
int readbit(void *v)
{
sigsuspend(&usr12mask);
return bit;
}
int bin2char(int (*f)(void *opaque), void *opaque) {
int i;
int c;
for (i=c=0; i<8; i++) {
int n=f(opaque);
if (n<0)
return -1;
c = c<<1 | n;
}
}
void main(int argc, char *argv[]){
if (argc == 1) {
pid_t pid = fork ();
if(pid > 0){
int c;
static struct sigaction sa={
.sa_sigaction=usr12action,
.sa_flags=SA_SIGINFO
};
printf("%d\n",getpid());
sigfillset(&usr12mask);
sigdelset(&usr12mask,SIGINT);
sigprocmask(SIG_SETMASK,&usr12mask,NULL);
sigdelset(&usr12mask,SIGCHLD);
sigdelset(&usr12mask,SIGUSR1);
sigdelset(&usr12mask,SIGUSR2);
sigaction(SIGUSR1,&sa,NULL);
sigaction(SIGUSR2,&sa,NULL);
sigaction(SIGCHLD,&sa,NULL);
while ((c=bin2char(readbit,stdin)) != EOF)
putchar(c);
if (waitpid(pid, &status, 0) != pid) status = -1;
}
else {
char *path;
char *sppid;
pid_t ppid=getppid();
asprintf(&path,"/proc/%d/exe",ppid);
asprintf(&sppid,"%d",ppid);
execl("/usr/bin/xterm", "xterm", "-e", path, sppid, (void *) 0);
exit(1);
}
} else {
unsigned char buf[BUFSIZE];
unsigned char *s;
pid_t receiver=atoi(argv[1]);
static struct sigaction sa={.sa_sigaction=usr1action};
sigaction(SIGUSR1,&sa,NULL);
printf("%d->%d\n",getpid(),receiver);
sigfillset(&usr1mask);
sigdelset(&usr1mask,SIGINT);
sigprocmask(SIG_SETMASK,&usr1mask,NULL);
sigdelset(&usr1mask,SIGUSR1);
if (argc == 2){
while ((s=fgets(buf,BUFSIZE,stdin))!=NULL){
while (*s){
char_to_bin(*s++, &receiver);
}
}
}
printf("\n");
}
}
il programma in questione "unisce" di fatto i due programmi sigsend e sigrec. l'idea è quella che venga lanciato un unico processo e aprire un nuovo terminale dove contiunare l'esecuzione del figlio creato attraverso la fork. Possiamo quindi sfruttare il fatto che i due processi (essendo padre e figlio) conoscono i rispettivi pid e quindi automatizzare questa parte senza fornire alcun pid in input. è possibile anche fornire un pid come argomento per comunicare con processi diversi. (Simone)
programma 3 (Fork del programma 2 di (Simone) )
Sono state effettuate delle piccole modifiche rispetto al programma 2 e inoltre sono stati aggiunti dei commenti per rendere più intuitivo e leggibile il codice.
#define _GNU_SOURCE
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#define BUFSIZE 1024
sigset_t receiverMask;
sigset_t senderMask;
static int bit;
void receiverAction(int num, siginfo_t *info, void *useless) {
if (num == SIGCHLD) // Se il processo figlio del ricevente è terminato
bit = EOF;
else{
bit = (num == SIGUSR2); // Codifico 1 con il segnale SIGUSR2 e 0 con SIGUSR1
kill(info->si_pid, SIGUSR1); // Invio il segnale SIGUSR1 al mittente
}
}
void senderAction(int num, siginfo_t *info, void *useless) {
return;
}
void write_bin(unsigned char x, pid_t *recv){ /* Funzione che riguarda il mittente */
unsigned int i;
for(i=0x80; i>0; i>>=1){ // Parto da 10000000, man mano shifto a destra, 01000000, 00100000 ecc ecc, fino a 00000000
kill(*recv, (x & i) ? SIGUSR2 : SIGUSR1); // Invio segnale al ricevente
sigsuspend(&senderMask); // Attendo segnale di ritorno
}
}
int readbit(void){
sigsuspend(&receiverMask); // Sospende il processo e attende un segnale
return bit;
}
int read_char(){ /* Funzione che riguarda il ricevente */
int i, c;
for (i = c = 0; i < 8; i++) {
int n = readbit();
if (n<0)
return EOF;
c = c<<1 | n;
}
}
void main(int argc, char *argv[]){
if (argc == 1) {
pid_t pid = fork ();
if(pid > 0){ /* PROCESSO PADRE (PROCESSO RICEVENTE) */
int c;
/*
* Il valore di sa_flag permette di specificare vari aspetti del comportamento di sigaction:
* SA_SIGINFO specifica che si vuole usare un gestore in forma estesa usando sa_sigaction al
* posto di sa_handler. Il valore di sa_sigaction specifica la funzione da chiamare, permettendo
* di usare un gestore piu complesso, in grado di ricevere informazioni piu dettagliate dal
* sistema, attraverso la struttura siginfo_t
*/
struct sigaction sa = {
.sa_sigaction = receiverAction,
.sa_flags = SA_SIGINFO
};
printf("%d\n", getpid());
/* Inserisci tutti i segnali nell'insieme receiverMask */
sigfillset(&receiverMask);
/* Elimina il segnale SIGINT dall'insieme receiverMask */
sigdelset(&receiverMask, SIGINT);
/* Setta la maschera dei segnali del processo con l'insieme receiverMask.
* Il valore NULL indica che la maschera attuale del processo non verrà salvata. */
sigprocmask(SIG_SETMASK, &receiverMask, NULL);
/* Elimina il segnale SIGCHLD, SIGUSR1 e SIGUSR2 dall'insieme receiverMask */
sigdelset(&receiverMask, SIGCHLD);
sigdelset(&receiverMask, SIGUSR1);
sigdelset(&receiverMask, SIGUSR2);
/* Setta l'azione sa per i segnali SIGUSR1, SIGUSR2 e SIGCHLD.
* Il valore NULL indica che l’azione corrente non viene restituita indietro. */
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGUSR2, &sa, NULL);
sigaction(SIGCHLD, &sa, NULL);
while ((c = read_char()) != EOF)
putchar(c);
}
else if(pid == 0){ // PROCESSO FIGLIO
char *cmd;
pid_t ppid = getppid(); /* PID del processo padre (ricevente) */
/* Avvio un altro terminale eseguendo lo stesso programma con parametri di input */
asprintf(&cmd, "xterm -e /proc/%d/exe %d", ppid, ppid);
system(cmd);
exit(1);
}
else{ // Errore Fork
fprintf(stderr, "Errore fork");
exit(1);
}
} else { /* PROCESSO MITTENTE */
unsigned char buf[BUFSIZE], *read;
pid_t receiver = atoi(argv[1]); // Pid del ricevente
/* Setto una banale azione (usr1action) alla ricezione del segnale SIGUSR1*/
struct sigaction sa = {.sa_sigaction = senderAction};
sigaction(SIGUSR1, &sa, NULL);
printf("%d->%d\n",getpid(),receiver);
sigfillset(&senderMask); // Inserisco tutti i segnali nell'insieme senderMask
sigdelset(&senderMask, SIGINT); // e cancello dall'insieme il segnale SIGINT.
sigprocmask(SIG_SETMASK,&senderMask,NULL); // Setto la maschera creata per il processo corrente, senza salvare la precedente.
sigdelset(&senderMask,SIGUSR1); // Elimino il segnale SIGUSR1 dall'insieme.
if (argc == 2){ // Se i paramentri in input sono 2
while ((read = fgets(buf,BUFSIZE,stdin)) != NULL){ // Leggo dallo stdin
while (*read) // Leggo tutta la stringa
write_bin(*read++, &receiver); // Invio ogni singolo carattere della stringa al ricevente
}
}
printf("\n");
}
}
(S.G)
programma 5
Secret channel with inotify
Stavo leggendo sul libro che e' possibile creare un canale di comunicazione segreto praticamente con ogni cosa all'interno di un sistema UNIX. In particolare mi ha interessato il caso di creare un canale mono direzionale tramite l'apertura dei file in lettura o scrittura e ho voluto provare a implementarne uno. Questi canali sono quasi impossibili da scovare per un amministratore di sistema e potrebbero permettere lo scambio di informazioni illecite. Una cosa interessante e' che a quanto pare il kernel ha bisogno di qualche millisecondo per eseguire la inotify e che una precedente implementazione senza delay portava all'arrivo di messaggi sbagliati.
Server
#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <string.h>
void printChar(int n){
int a;
char c;
unsigned char buffer[8];
memset(buffer, '0', sizeof(buffer));
int i = 7;
while (n){
if(n & 1)
buffer[i]= '1';
i--;
n >>= 1;
}
c = strtol(buffer, 0, 2);
printf("%c", c);
}
static void handle_events (int fd, int wd, char *bin_message,int *current_bit){
char buf [4096]
__attribute__ ((aligned(__alignof__(struct inotify_event))));
const struct inotify_event *event;
int i;
ssize_t len;
char *ptr;
//loop while inotify events can be read from the fd
for (;;){
len = read (fd, buf, sizeof (buf));
if (len == -1 && errno != EAGAIN){
perror ("read");
exit (EXIT_FAILURE);
}
//if the read returns with the errono EAGAIN exit from the loop
if (len <= 0)
break;
//loop between all events in the buffer
for (ptr = buf; ptr < buf + len;
ptr += sizeof(struct inotify_event) + event->len){
event = (const struct inotify_event *) ptr;
if (event->mask & IN_CLOSE_NOWRITE){
//received 1
*bin_message |= 0x01;
}
if (event->mask & IN_CLOSE_WRITE){
//received 0
//do nothing to leave 0
}
}
}
}
void main () {
char buf;
int fd;
int wd;
struct pollfd fds [2];
int nfds;
int poll_num;
/*char message = 0;*/
unsigned char message_str [4096] = "";
int count = 0;
int bit_wrote = 0;
char path [100] = "/inofify/watched";
int t;
//create the file descriptor where inotify events has to be read
fd = inotify_init1(IN_NONBLOCK);
if (fd == -1) {
perror("inotify_init1");
exit(EXIT_FAILURE);
}
wd = inotify_add_watch (fd, path, IN_CLOSE_NOWRITE | IN_CLOSE_WRITE);
if (wd == -1 ){
fprintf (stderr, "Cannot watch '%s' \n", path);
perror ("inotify_add_watch");
exit(EXIT_FAILURE);
}
//prepare for the polling on the fd
nfds = 2;
fds[0].fd = STDIN_FILENO;
fds[0].events = POLLIN;
fds[1].fd = fd;
fds[1].events = POLLIN;
printf ("Listening for events... Press ENTER to terminate and show the message\n");
while (1){
poll_num = poll(fds,nfds, -1);
if (poll_num == -1){
if (errno == EINTR)
continue;
perror ("poll");
exit (EXIT_FAILURE);
}
if (poll_num > 0){
if (fds[0].revents & POLLIN){
//exit on ENTER key pressed emptying the stdin
while (read (STDIN_FILENO,&buf,1) > 0 && buf != '\n') continue;
break;
}
//notify an event occurred
if (fds[1].revents & POLLIN ){
if (bit_wrote < 8){
printf("..");
fflush(stdout);
handle_events (fd,wd,&message_str[count],&bit_wrote);
if (bit_wrote < 7){
message_str[count] = message_str[count] << 0x01;
}
}
else {
bit_wrote = 0;
count += 1;
handle_events (fd,wd,&message_str[count],&bit_wrote);
}
bit_wrote += 1;
}
}
}
printf("\n**********MESSAGE***********\n");
t = 0;
while (t <= count ){
printChar(message_str[t]);
t += 1;
}
printf("\n**************************\n");
printf("\nClosing...\n");
exit(EXIT_SUCCESS);
}
Client
#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
void chat(int n){
int a;
int fd;
char path [] = "watched";
unsigned char buffer[8];
memset(buffer, '0', sizeof(buffer));
int i = 7;
const struct timespec timer = {
.tv_sec = 0,
.tv_nsec = 500000
};
struct timespec s;
while (n){
if(n & 1){
buffer[i] = '1';
}
i--;
n >>= 1;
}
for (a = 0; a < 8; a++) {
if (buffer[a] == '0'){
fd = open(path,O_WRONLY);
close(fd);
}
else{
fd = open(path,O_RDONLY);
close(fd);
}
/*sleep (1);*/
nanosleep(&timer,&s);
}
}
void main (){
char message[1024] = "";
int bit_message;
int len = 0;
int i = 0;
printf("Type in \n");
fgets(message,100,stdin);
printf("You typed: %s", message);
len = (unsigned)strlen(message);
while (i < len -1){
chat(message[i]);
i++;
}
}
Alessio Trivisonno