2016-05-06 15 views
9

Ich versuche, Prozesse mit meinem Daemon verzweigen und versuchen, sie im Falle meiner Daemon-Abstürze zu verleugnen. Regelmäßige os/exec ist auf hohem Niveau, deshalb ging ich für syscall.ForkExec und produzierte den folgenden Code:Golang Exec-Prozess und es zu verleugnen

package main 

import (
    "fmt" 
    "os" 
    "os/exec" 
    "syscall" 
    "time" 
) 

func main() { 

    cmd := "myproc" 
    binary, lookErr := exec.LookPath(cmd) 
    if lookErr != nil { 
     panic(lookErr) 
    } 
    fmt.Println(binary) 

    os.Remove("/tmp/stdin") 
    os.Remove("/tmp/stdout") 
    os.Remove("/tmp/stderr") 

    fstdin, err1 := os.Create("/tmp/stdin") 
    fstdout, err2 := os.Create("/tmp/stdout") 
    fstderr, err3 := os.Create("/tmp/stderr") 
    if err1 != nil || err2 != nil || err3 != nil { 
     fmt.Println(err1, err2, err3) 
     panic("WOW") 
    } 

    argv := []string{"hi"} 
    procAttr := syscall.ProcAttr{ 
     Dir: "/tmp", 
     Files: []uintptr{fstdin.Fd(), fstdout.Fd(), fstderr.Fd()}, 
     Env: []string{"VAR1=ABC123"}, 
     Sys: &syscall.SysProcAttr{ 
      Foreground: false, 
     }, 
    } 

    pid, err := syscall.ForkExec(binary, argv, &procAttr) 
    fmt.Println("Spawned proc", pid, err) 

    time.Sleep(time.Second * 100) 
} 

Ich habe auch eine einfache Anwendung aus, die schläft und druckt Hallo Welt und legte es auf Weg.

#include <stdio.h> 

int main(){ 
    while(1){ 
     printf("hello world"); 
     fflush(stdout); 
     usleep(300000); 
     } 
} 

Es funktioniert, aber der Prozess wird nicht an den Hintergrund gesendet, wie ich erwartet habe, mein Go-Prozess besitzt noch das Kind. Die SysProcAttr hat folgende Werte in Linux:

type SysProcAttr struct { 
    Chroot  string   // Chroot. 
    Credential *Credential // Credential. 
    Ptrace  bool   // Enable tracing. 
    Setsid  bool   // Create session. 
    Setpgid  bool   // Set process group ID to Pgid, or, if Pgid == 0, to new pid. 
    Setctty  bool   // Set controlling terminal to fd Ctty (only meaningful if Setsid is set) 
    Noctty  bool   // Detach fd 0 from controlling terminal 
    Ctty  int   // Controlling TTY fd 
    Foreground bool   // Place child's process group in foreground. (Implies Setpgid. Uses Ctty as fd of controlling TTY) 
    Pgid  int   // Child's process group ID if Setpgid. 
    Pdeathsig Signal   // Signal that the process will get when its parent dies (Linux only) 
    Cloneflags uintptr  // Flags for clone calls (Linux only) 
    UidMappings []SysProcIDMap // User ID mappings for user namespaces. 
    GidMappings []SysProcIDMap // Group ID mappings for user namespaces. 
    // GidMappingsEnableSetgroups enabling setgroups syscall. 
    // If false, then setgroups syscall will be disabled for the child process. 
    // This parameter is no-op if GidMappings == nil. Otherwise for unprivileged 
    // users this should be set to false for mappings work. 
    GidMappingsEnableSetgroups bool 
} 

Ich habe versucht, auch die folgenden, aber es einen Fehler verursacht:

Sys: &syscall.SysProcAttr{ 
    Setsid:  true, 
    Setctty: true, 
    Foreground: false, 
}, 

Spawned proc 0 inappropriate ioctl for device 

Auch die folgenden:

Sys: &syscall.SysProcAttr{ 
    Setsid:  true, 
    Setctty: true, 
    Foreground: false, 
    Noctty:  true, 
    Setpgid: true, 
}, 

Spawned proc 0 operation not permitted (with root privilleges) 

Was mache ich/falsch annehmen? Hinweis: Trotz der Aussage, os/exec ist High-Level, habe ich auch Folgendes versucht, aber es produzierte die gleichen Ergebnisse.

cs := exec.Command(binary) 
cs.SysProcAttr = &syscall.SysProcAttr{ 
    Setctty: true, 
} 
err := cs.Run() 
fmt.Println(err) 
+0

Warum denken Sie 'exec.Cmd' zu hoch Ebene? Sie haben von dort aus auch Zugriff auf SysProcAttr. Was meinst du mit deinem Prozess? Liegt es in derselben Prozessgruppe? Wird es nicht zu PID 1 neu zugewiesen, wenn das Elternteil beendet wird? – JimB

Antwort

2

Ein Prozess mit Start() gegabelt wird auch nach seiner Eltern stirbt.

func forker() { 
    cmd := exec.Command("sleep", "3") 
    cmd.Start() 
    time.Sleep(2 * time.Second) 
    os.Exit(1) 
} 

Hier ist der sleep Prozess wird gerne für 3 Sekunden weiterleben, auch wenn der übergeordnete Prozess nur 2 Sekunden lang lebt:

$ forker &; while true; do ps -f; sleep 1; done 

    UID PID PPID C STIME TTY   TIME CMD 
    501 71423 69892 0 3:01PM ttys003 0:00.07 forker 
    501 71433 71432 0 3:01PM ttys003 0:00.00 sleep 3 

    UID PID PPID C STIME TTY   TIME CMD 
    501 71423 69892 0 3:01PM ttys003 0:00.07 forker 
    501 71433 71432 0 3:01PM ttys003 0:00.00 sleep 3 

    UID PID PPID C STIME TTY   TIME CMD 
    501 71433  1 0 3:01PM ttys003 0:00.00 sleep 3 

Beachten Sie, wie der übergeordnete Prozess-ID (PPID) des sleep Prozess wurde 1, wenn der übergeordnete Prozess 71432 beendet wird. Dies bedeutet, dass der Prozess sleep verwaist wurde.

+0

Ziel ist es, 71432 im obigen Beispiel (sofort) gleich 1 zu machen. – sheerun

+0

Die Frage ist formuliert: "[...] im Falle meines Daemon stürzt ab [sic]". Es ist nicht notwendig, den Prozess sofort zu verwaisen. Mit 'Start()' wird es nur dann verwaist, wenn und wenn der Elternteil stirbt. – augustzf

2

Die Start() method sollten Sie geben, was Sie suchen. Mein Skript eine einfache Textdatei nach meinem Beispiel Go-Programm fortgesetzt beendet anhängen auszuführen:

package main 

import (
    "os/exec" 
) 

func main() { 
    cmd := exec.Command("./appender.sh") 
    cmd.Start() 
} 
+1

Beachten Sie, dass wenn der Befehl das stdin/stdout von dem Eltern verwendet, wenn das Eltern beendet wird, der Prozess ein Zombie werden kann. – AJPennster

2

Es scheint eine gute Strategie für die sofortige Entlarvung ist die Erstellung os.Argv[0] Prozess (Self-Spawn), dann Laich-Ziel-Prozess (mit Setsid: true), und dann Beenden des ersten erzeugten Prozess. Auf diese Weise erhält gradchild process sofort ppid 1. Optional kann der erste generierte Prozess pid von granchild über stdout an parent kommunizieren, bevor es beendet wird.

1

Die Daemonisierung spielt nicht gut mit Go's Goroutine-Planung, so dass die Details ziemlich knorrig werden. godaemon hat eine kleine Implementierung mit guter Diskussion und Links zu mehr guter Diskussion, und es gibt auch die populärere und traditionellere Implementierung go-daemon.

Die ioctl Fehler, die Sie bekommen, sind fast sicher aus der Verwendung von Setctty.

Sys: &syscall.SysProcAttr{ Foreground: false, Setsid: true, },

sollten Sie Semantik wie os/exec ‚s Start, erhalten, wo das Kind Prozess nicht sofort neubeeltert, aber wird sicher hochgezogen werden, wenn/wenn der Elternteil stirbt.

Die operation not permitted Sie sehen, ist, glaube ich, über die Rohre in /tmp/, unter strace laufen: openat(AT_FDCWD, "/tmp/stdin", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 3 epoll_create1(EPOLL_CLOEXEC) = 4 epoll_ctl(4, EPOLL_CTL_ADD, 3, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=471047936, u64=140179613654784}}) = -1 EPERM (Operation not permitted) epoll_ctl(4, EPOLL_CTL_DEL, 3, 0xc420039c24) = -1 EPERM (Operation not permitted) openat(AT_FDCWD, "/tmp/stdout", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 5 epoll_ctl(4, EPOLL_CTL_ADD, 5, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=471047936, u64=140179613654784}}) = -1 EPERM (Operation not permitted) epoll_ctl(4, EPOLL_CTL_DEL, 5, 0xc420039c24) = -1 EPERM (Operation not permitted) openat(AT_FDCWD, "/tmp/stderr", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 6 epoll_ctl(4, EPOLL_CTL_ADD, 6, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=471047936, u64=140179613654784}}) = -1 EPERM (Operation not permitted) epoll_ctl(4, EPOLL_CTL_DEL, 6, 0xc420039c24) = -1 EPERM (Operation not permitted)