domingo, 23 de enero de 2011

C bajo linux

Buenas a todos os voy a enseñar una aplicación escrita en C para sistemas UNIX. Espero que pueda servir como referencia a la hora de manejar procesos hijos y tuberías. 

Lo que hace fundamentalmente es:
El proceso padre crea dos procesos hijos. El proceso padre recoge lo que le pasemos por el teclado y se lo envía a los hijos, los cuales uno de ellos visualiza la información por teclado y el otro la almacena en un fichero. Si el padre recibe un "exit" o la cadena está vacía, mata ambos procesos y la aplicación finaliza.

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/wait.h>

pid_t pidHijo1;
pid_t pidHijo2;
int tuberiaH1[2]; /* usamos tuberiaH1 para enviar información del Padre al Hijo1 */
int tuberiaH2[2]; /* usamos tuberiaH2 para enviar información del Padre al Hijo2 */

void procesoHijo1();/* Proceso que ejecuta Hijo1 */
void procesoHijo2();/*Proceso que ejecuta Hijo2 */
void procesoPadre();/* Proceso Padre */
void procesoCrearHijo2();/* Proceso que ejecuta Padre para crear Hijo2 */

/* --------------- Main ------------------ */
int main(int argc, char * argv[]){
  pipe(tuberiaH1);
  pidHijo1=fork();
  if(pidHijo1==0){
    procesoHijo1();
  }else if(pidHijo1 >0 ){
    procesoCrearHijo2();/* esto lo ejecuta el proceso Padre, es necesario para crear un segundo Hijo */
  }else{
    printf("Error, no hay procesoHijo1\n");
  }
}
/* --------------- Funciones ------------------ */
void procesoHijo1(){
  char buffer[255];
  close(tuberiaH1[1]); /* no vamos a escribir en la tubería */
  close(0); /* no vamos a leer nada por teclado */
  dup(tuberiaH1[0]); /* rederigir entrada estandar a la tuberia */
  printf("[H1] dice: Escribe texto que te lo visualizo por pantalla y [H2] lo almacenará en un fichero\nPara Salir solo pulsa Intro o escribe exit\n");
  for(;;){
    fgets(buffer,sizeof(buffer),stdin); /* leer del padre */
    while(getc(stdin) != '\n'); /* descartar hasta fin de línea */
    printf("[H1] %s",buffer);/* sacar por pantalla */
  }
}
void procesoHijo2(){
  char buffer[255];
  char* cadena="[H2] ";
  int f1;
/* Si el archivo no existe lo crea y si existe, lo habe en modo escritura y se pone al final */
  f1=open("H2archivoCreado",O_CREAT | O_APPEND | O_WRONLY,0644);
  if (f1==-1){/* control de error al abrir el fichero */
    perror("Error al abrir fichero:");
    exit(1);
  }
  close(tuberiaH2[1]); /* no vamos a escribir en la tubería */
  close(0); /* no vamos a leer nada por teclado */
  dup(tuberiaH2[0]); /* rederigir entrada estandar a la tuberia */
  for(;;){
    fgets(buffer,sizeof(buffer),stdin); /* leer del padre */
    while(getc(stdin) != '\n'); /* descartar hasta fin de línea */
    write(f1, cadena, strlen(cadena) ); /* escribir en fichero mensaje CADENA */
    write(f1, buffer, strlen(buffer) ); /* escribir en fichero mensaje enviado por el proceso Padre */
  }
  close(f1);
}
void procesoPadre(){
  char buffer[255];
  char buffer2[257];
  int estado;
  close(tuberiaH1[0]); /* no la vamos a leer datos de la tubería */
  close(2); /* cerrar la salida estándar de errores */
  dup2(tuberiaH1[1],2); /* Redirigirla salida estándar de ERRORES a la tubería */
  fgets(buffer,sizeof(buffer)-1,stdin);
  while (strlen(buffer) !=0 && buffer[0] != '\n' && strcmp(buffer,"exit\n")){
    sprintf(buffer2,"%s\n ",buffer);
    fprintf(stderr,"%s",buffer2);
    write(tuberiaH2[1],buffer2,strlen(buffer2));
    fgets(buffer,sizeof(buffer)-1,stdin);
  }
  printf("[P] dice: INTRO cerrando aplicación\n");
  kill(pidHijo1, SIGTERM); /* terminia con procesoHijo1 */
  kill(pidHijo2, SIGTERM); /* terminia con procesoHijo2 */
  int pidH1off = wait(&estado);
  int pidH2off = wait(&estado);
  fprintf(stdout,"Hasta otra!\n");
  exit(0);
}
void procesoCrearHijo2(){
pipe(tuberiaH2);
pidHijo2=fork();
  if(pidHijo2==0){
      procesoHijo2();
  }else if(pidHijo2 >0 ){
    procesoPadre(); /* Se ejecuta el procesoPadre */
  }else{
    printf("Error, no hay procesoHijo2\n");
  }

}