Lancer bash en utilisant posix au lieu de fork / execv

J’ai une CLI, une des commandes est d’entrer dans le shell bash Linux. C’est le code qui le fait en utilisant fork & execv:

if ((pid = fork()) < 0) { syslog_debug(LOG_ERR, "Could not fork"); } if (pid == 0) { /* In child, open the child's side of the tty. */ int i; for(i = 0; i <= maxfd; i++) { close(i); } /* make new process group */ setsid(); if ((fd[0] = open(tty_name, O_RDWR /*| O_NOCTTY*/)) < 0) { syslog_debug(LOG_ERR, "Could not open tty"); exit(1); } fd[1] = dup(0); fd[2] = dup(0); /* exec shell, with correct argv and env */ execv("/bin/sh", (char *const *)argv_init); exit(1); } 

Je veux remplacer le fork / execv et utiliser posix_spawn à la place:

 ret = posix_spawn_file_actions_init(&action); pipe(fd); for(i = 0; i <= maxfd; i++) { ret = posix_spawn_file_actions_addclose (&action, i); } ret = posix_spawn_file_actions_adddup2 (&action, fd[1], 0); ret = posix_spawn_file_actions_adddup2 (&action, fd[2], 0); ret = posix_spawn_file_actions_addopen (&action, STDOUT_FILENO, tty_name, O_RDWR, 0); char* argv[] = { "/bin/sh", "-c", "bash", NULL }; int status; extern char **environ; posix_spawnattr_t attr = { 0 }; snprintf( cmd, sizeof(cmd), "bash"); status = posix_spawn( &pid, argv[0], action /*__file_actions*/, &attr, argv, environ ); posix_spawn_file_actions_destroy(&action); 

Mais ça ne marche pas. De l’aide?

Regardons ce que le code dans le fork / exec original fait:

  • fermer tous les descripteurs de fichier
  • ouvrez le tty, qui obtiendra par hasard un fd de 0 , c’est-à-dire qu’il est stdin
  • dupliquer ce fd deux fois ( dup(0) ), ce qui les met dans stdout et stderr
  • exec la commande shell

Votre nouveau code ne suit pas du tout le même schéma. Ce que vous voulez faire, c’est répéter le processus, mais soyez plus explicite:

fermez tous les FD:

 for(i = 0; i <= maxfd; i++) { ret = posix_spawn_file_actions_addclose (&action, i); } 

ouvrez le tty dans STDIN_FILENO :

 ret = posix_spawn_file_actions_addopen (&action, STDIN_FILENO, tty_name, O_RDWR, 0); 

dupliquer le STDIN_FILENO dans STDOUT_FILENO et STDERR_FILENO :

 ret = posix_spawn_file_actions_adddup2 (&action, STDIN_FILENO, STDOUT_FILENO); ret = posix_spawn_file_actions_adddup2 (&action, STDIN_FILENO, STDERR_FILENO); 

Ensuite, posix_spawn doit avoir lieu dans le bon contexte.

Pour l'ancien processus fork / exec, vous devriez avoir fait quelque chose comme:

 int fd = open(tty_name, O_RDWR /*| O_NOCTTY*/); if (fd != STDIN_FILENO) dup2(fd, STDIN_FILENO); if (fd != STDOUT_FILENO) dup2(fd, STDOUT_FILENO); if (fd != STDERR_FILENO) dup2(fd, STDERR_FILENO); 

C'est plus explicite dans l'intention.

Les ifs ont pour but d'empêcher la dup2 accidentelle du fd origine dans le nouveau numéro de fd. Le seul qui devrait vraiment avoir ce problème est le premier parce que fd == STDIN_FILENO car aucun autre descripteur de fichier n’est ouvert à ce stade.

Pour combiner cela dans un petit morceau de code, avec echo something plutôt que d'invocation de bash nous avons:

 #include  #include  #include  #include  #include  #include  void do_spawn() { int ret; posix_spawn_file_actions_t action; int i; pid_t pid; int maxfd = 1024; char *tty_name = ttyname (0); ret = posix_spawn_file_actions_init (&action); for (i = 0; i <= maxfd; i++) { ret = posix_spawn_file_actions_addclose (&action, i); } ret = posix_spawn_file_actions_addopen (&action, STDIN_FILENO, tty_name, O_RDWR, 0); ret = posix_spawn_file_actions_adddup2 (&action, STDIN_FILENO, STDOUT_FILENO); ret = posix_spawn_file_actions_adddup2 (&action, STDIN_FILENO, STDERR_FILENO); char *argv[] = { "/bin/sh", "-c", "echo something", NULL }; int status; extern char **environ; posix_spawnattr_t attr = { 0 }; posix_spawnattr_init(&attr); posix_spawnattr_setflags(&attr, POSIX_SPAWN_USEVFORK); status = posix_spawn(&pid, argv[0], &action /*__file_actions*/ , &attr, argv, environ); printf ("%d %ld\n", status, pid); wait (0); posix_spawn_file_actions_destroy (&action); } int main(int argc, char **argv) { do_spawn(); } 

Cela doit être compilé avec -D_GNU_SOURCE , sinon POSIX_SPAWN_USEVFORK n'est pas défini.