Demo entry 6644578

1

   

Submitted by anonymous on Oct 04, 2017 at 14:39
Language: C. Code size: 12.6 kB.

/*
 * OS Assignment #1
 */

#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/signalfd.h>

#define MSG(x...) fprintf (stderr, x)
#define STRERROR  strerror (errno)
#define handle_error(msg) do{perror(msg); exit(EXIT_FAILURE);}while(0)

#define ID_MIN 2
#define ID_MAX 8
#define COMMAND_LEN 256

typedef enum
{
  ACTION_ONCE,
  ACTION_RESPAWN,

} Action;

typedef struct _Task Task;
struct _Task
{
  Task          *next;

  volatile pid_t pid;
  int            piped;
  int            pipe_a[2];
  int            pipe_b[2];

  char           id[ID_MAX + 1];
  char           pipe_id[ID_MAX + 1];
  char           order[1];
  Action         action;
  char           command[COMMAND_LEN];
};

static Task *tasks;

static volatile int running;

static char *
strstrip (char *str)
{
  char  *start;
  size_t len;

  len = strlen (str);
  while (len--)
    {
      if (!isspace (str[len]))
        break;
      str[len] = '\0';
    }

  for (start = str; *start && isspace (*start); start++)
    ;
  memmove (str, start, strlen (start) + 1);

  return str;
}

static int
check_valid_id (const char *str)
{
  size_t len;
  int    i;

  len = strlen (str);
  if (len < ID_MIN || ID_MAX < len)
    return -1;

  for (i = 0; i < len; i++)
    if (!(islower (str[i]) || isdigit (str[i])))
      return -1;

  return 0;
}

static int 
check_valid_order(const char *str) //order의 자릿수 체크 하는 함수
{
  int i;
  int count = 0;
  int len=0, num=0;
  num = atoi(str);
  len = num;

  size_t size = strlen(str);
  if(size == 0){ //0바이트 문자열은 숫자가 아니다
    MSG("not valid order\n");
    return 0;
  }
  
  for (int j = 0; j < (int) size; j++) {
    if (str[j] == '.' || str[j] == '-' || str[j] == '+')
      continue;
    if (str[j] < '0' || str[j] > '9'){ 
      MSG("not valid order\n"); // 알파벳 등이 있으면 숫자 아니다.
      return 0;
    }
  }
  
  do{
    len = (int)(len/10);
    count++;
  }while(len>0);

   if(0<=count&&count<=4) //order가 4자리 이하의 숫자인지 확인
      return 1;
   else 
      return 0;
   return 0;
}



static Task *
lookup_task (const char *id)
{
  Task *task;

  for (task = tasks; task != NULL; task = task->next)
    if (!strcmp (task->id, id))
      return task;

  return NULL;
}

static Task *
lookup_task_by_pid (pid_t pid)
{
  Task *task;

  for (task = tasks; task != NULL; task = task->next)
    if (task->pid == pid)
      return task;

  return NULL;
}

static void
append_task (Task *task)
{
  Task *new_task;
  new_task = malloc (sizeof (Task));
  Task* head = tasks;
  if (!new_task)
    {
      MSG ("failed to allocate a task: %s\n", STRERROR);
      return;
    }

  *new_task = *task;
  new_task->next = NULL;

  if (!tasks){
    tasks = new_task;
  }
  else
    { 
      Task *t;
      int num; //Task 노드의 개수를 저장하는 변수
      Task *tmp, *prev;
      tmp = malloc(sizeof(Task));
      prev = malloc(sizeof(Task));
      strcpy(tmp->order, new_task->order);
      tmp->next = NULL;
      prev->next = NULL;
 
      if(atoi(head->order) == 0){ //연결리스트가 공백일 때 
        num = 0;
      }
      else{ //연결리스트가 공백이 아닐 때
        for (num = 1, t =head; t->next != NULL; t = t->next, num++);
      }
      
      if(num == 0){ //연결리스트가 공백일 때 새로 들어온 task를 head로 지정
        head = new_task;
        head->next = NULL;
        
      }
      else if(num == 1){ //연결리스트에 head만 존재할 때 order값 비교
        if(atoi(head->order) <= atoi(new_task->order)){
          head->next = new_task;
          head->next->next = NULL;
        }
        else{
          head->next = NULL;
          new_task -> next = head;
          head = new_task;
        }

      }
      else { //연결리스트에 Task 노드가 2개 이상 존재할 때
        t = head;
        while(1){
          if(atoi(t->order)<= atoi(new_task->order)){ //새로 들어온 노드의 order를 head에서부터 하나씩 옮겨가며 비교
            if(t->next == NULL){
              t->next = new_task;
              break;
            }
            else{
              prev = t;
              t = t->next;
            }
          }
          else if(atoi(t->order)>atoi(new_task->order)){
            new_task -> next = t;
            prev->next = new_task;
            break;
          }
        }
      }
    }
    
}

static int
read_config (const char *filename)
{
  FILE *fp;
  char  line[COMMAND_LEN * 2];
  int   line_nr;

  fp = fopen (filename, "r");
  if (!fp)
    return -1;

  tasks = NULL;

  line_nr = 0;
  while (fgets (line, sizeof (line), fp))
    {
      Task   task;
      char  *p;
      char  *s;
      size_t len;

      line_nr++;
      memset (&task, 0x00, sizeof (task));

      len = strlen (line);
      if (line[len - 1] == '\n')
        line[len - 1] = '\0';

      if (0)
        MSG ("config[%3d] %s\n", line_nr, line);

      strstrip (line);

      /* comment or empty line */
      if (line[0] == '#' || line[0] == '\0')
        continue;

      /* id */
      s = line;
      p = strchr (s, ':');
      if (!p)
        goto invalid_line;
      *p = '\0';
      strstrip (s);
      if (check_valid_id (s))
        {
          MSG ("invalid id '%s' in line %d, ignored\n", s, line_nr);
          continue;
        }
      if (lookup_task (s))
        {
          MSG ("duplicate id '%s' in line %d, ignored\n", s, line_nr);
          continue;
        }
      strcpy (task.id, s);

      /* action */
      s = p + 1;
      p = strchr (s, ':');
      if (!p)
        goto invalid_line;
      *p = '\0';
      strstrip (s);
      if (!strcasecmp (s, "once"))
        task.action = ACTION_ONCE;
      else if (!strcasecmp (s, "respawn"))
        task.action = ACTION_RESPAWN;
      else
        {
          MSG ("invalid action '%s' in line %d, ignored\n", s, line_nr);
          continue;
        }
      
      /* order */
      s = p + 1;
      p = strchr(s, ':');
      if(!p)
        goto invalid_line;
      *p = '\0';
      strstrip(s);
      
      if(check_valid_order(s) == 0){ //order의 자릿수 체크 하는 함수
        MSG("invalid order '%s', in line %d, ignored\n",s, line_nr);
        continue;
      }
      strcpy(task.order, s);

      /* pipe-id */
      s = p + 1;
      p = strchr (s, ':');
      if (!p)
        goto invalid_line;
      *p = '\0';
      strstrip (s);
      if (s[0] != '\0')
        {
          Task *t;

          if (check_valid_id (s))
            {
              MSG ("invalid pipe-id '%s' in line %d, ignored\n", s, line_nr);
              continue;
            }

          t = lookup_task (s);
          if (!t)
            {
              MSG ("unknown pipe-id '%s' in line %d, ignored\n", s, line_nr);
              continue;
            }
          if (task.action == ACTION_RESPAWN || t->action == ACTION_RESPAWN)
            {
              MSG ("pipe not allowed for 'respawn' tasks in line %d, ignored\n", line_nr);
              continue;
            }
          if (t->piped)
            {
              MSG ("pipe not allowed for already piped tasks in line %d, ignored\n", line_nr);
              continue;
            }

          strcpy (task.pipe_id, s);
          task.piped = 1;
          t->piped = 1;
        }

      /* command */
      s = p + 1;
      strstrip (s);
      if (s[0] == '\0')
        {
          MSG ("empty command in line %d, ignored\n", line_nr);
          continue;
        }
      strncpy (task.command, s, sizeof (task.command) - 1);
      task.command[sizeof (task.command) - 1] = '\0';

      if (0)
        MSG ("id:%s pipe-id:%s action:%d command:%s\n",
             task.id, task.pipe_id, task.action, task.command);

      append_task (&task);
      continue;

    invalid_line:
      MSG ("invalid format in line %d, ignored\n", line_nr);
    }
  
  fclose (fp);

  return 0;
}

static char **
make_command_argv (const char *str)
{
  char      **argv;
  const char *p;
  int         n;

  for (n = 0, p = str; p != NULL; n++)
    {
      char *s;

      s = strchr (p, ' ');
      if (!s)
        break;
      p = s + 1;
    }
  n++;

  argv = calloc (sizeof (char *), n + 1);
  if (!argv)
    {
      MSG ("failed to allocate a command vector: %s\n", STRERROR);
      return NULL;
    }

  for (n = 0, p = str; p != NULL; n++)
    {
      char *s;

      s = strchr (p, ' ');
      if (!s)
        break;
      argv[n] = strndup (p, s - p);
      p = s + 1;
    }
  argv[n] = strdup (p);

  if (0)
    {

      MSG ("command:%s\n", str);
      for (n = 0; argv[n] != NULL; n++)
        MSG ("  argv[%d]:%s\n", n, argv[n]);
    }

  return argv;
}

static void
spawn_task (Task *task)
{
  if (0) MSG ("spawn program '%s'...\n", task->id);

  if (task->piped && task->pipe_id[0] == '\0')
    {
      if (pipe (task->pipe_a))
        {
          task->piped = 0;
          MSG ("failed to pipe() for prgoram '%s': %s\n", task->id, STRERROR);
        }
      if (pipe (task->pipe_b))
        {
          task->piped = 0;
          MSG ("failed to pipe() for prgoram '%s': %s\n", task->id, STRERROR);
        }
    }
  
  task->pid = fork ();
  if (task->pid < 0)
    {
      MSG ("failed to fork() for program '%s': %s\n", task->id, STRERROR);
      return;
    }

  /* child process */
  if (task->pid == 0)
    {
      char **argv;

      argv = make_command_argv (task->command);
      if (!argv || !argv[0])
        {
          MSG ("failed to parse command '%s'\n", task->command);
          exit (-1);
        }

      if (task->piped)
        {
          if (task->pipe_id[0] == '\0')
            {
              dup2 (task->pipe_a[1], 1);
              dup2 (task->pipe_b[0], 0);
              close (task->pipe_a[0]);
              close (task->pipe_a[1]);
              close (task->pipe_b[0]);
              close (task->pipe_b[1]);
            }
          else
            {
              Task *sibling;

              sibling = lookup_task (task->pipe_id);
              if (sibling && sibling->piped)
                {
                  dup2 (sibling->pipe_a[0], 0);
                  dup2 (sibling->pipe_b[1], 1);
                  close (sibling->pipe_a[0]);
                  close (sibling->pipe_a[1]);
                  close (sibling->pipe_b[0]);
                  close (sibling->pipe_b[1]);
                }
            }
        }

      execvp (argv[0], argv);
      MSG ("failed to execute command '%s': %s\n", task->command, STRERROR);
      exit (-1);
    }
}

static void
spawn_tasks (void)
{
  Task *task;

  for (task = tasks; task != NULL && running; task = task->next)
    spawn_task (task);

}

static void
wait_for_children (int signo)
{
  Task *task;
  pid_t pid;

 rewait:
  pid = waitpid (-1, NULL, WNOHANG);
  if (pid <= 0)
    return;
  task = lookup_task_by_pid (pid);
  if (!task)
    {
      MSG ("unknown pid %d", pid);
      return;
    }
  if (0) MSG ("program[%s] terminated\n", task->id);
  if (running && task->action == ACTION_RESPAWN)
    spawn_task (task);
  else
    task->pid = 0;
  /* some SIGCHLD signals is lost... */
  goto rewait;
}

static void
terminate_children (int signo)
{
  Task *task;

  if (1) MSG ("terminated by SIGNAL(%d)\n", signo);

  running = 0;

  for (task = tasks; task != NULL; task = task->next)
    if (task->pid > 0)
      {
        if (0) MSG ("kill program[%s] by SIGNAL(%d)\n", task->id, signo);
        kill (task->pid, signo);
      }

  exit (1);
}

int
main (int    argc,
      char **argv)
{
  struct sigaction sa;
  int terminated;

  if (argc <= 1)
    {
      MSG ("usage: %s config-file\n", argv[0]);
      return -1;
    }

  if (read_config (argv[1]))
    {
      MSG ("failed to load config file '%s': %s\n", argv[1], STRERROR);
      return -1;
    }

  running = 1;

  spawn_tasks ();

  
  sigset_t mask;
  int sfd;
  struct signalfd_siginfo fdsi;
  ssize_t s;

  
  sigemptyset(&mask); //시그널 집합 변수의 내용을 모두 제거
  
  /*시그널 집합 변수에 시그널 추가*/
  sigaddset(&mask, SIGINT);
  sigaddset(&mask, SIGCHLD);
  sigaddset(&mask, SIGTERM);


  /* Block signals so that they aren't handled
      according to their default dispositions */
  if(sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
    handle_error("sigprocmask");
  sfd = signalfd(-1, &mask, 0);
  if(sfd == -1)
    handle_error("signalfd");

  for(;;){
    s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo));
    if(s==-1){
      MSG("Error : %s\n",STRERROR);
    }
    if(fdsi.ssi_signo == SIGINT){ //SIGINT 시그널 발생 시 terminate_children 함수로 처리
       terminate_children(SIGINT);
    }
    else if(fdsi.ssi_signo == SIGCHLD){ //SIGCHLD 시그널 발생 시 wait_for_children 함수로 처리
      wait_for_children(SIGCHLD);
    }
    else if(fdsi.ssi_signo == SIGTERM){ //SIGTERM 시그널 발생 시 terminate_children 함수로 처리
      terminate_children(SIGTERM);
    }
  }
  
  return 0;
}

This snippet took 0.03 seconds to highlight.

Back to the Entry List or Home.

Delete this entry (admin only).