основной пакет
import (
«fmt»
«os»
«syscall»
«время»
«github.com/kardianos/osext»
)
// Права доступа к файлам по умолчанию для файлов журнала и pid.
const FILE_PERM = os.FileMode(0640)
// Context описывает контекст демона.
type Context struct {
// Если WorkDir не пуст, дочерний элемент переходит в каталог перед
// созданием процесса.
WorkDir string
// Если Chroot не пуст, дочерний элемент меняет корневой каталог
Chroot string
// Если Env не равен нулю, он предоставляет переменные среды для
// процесса-демона в форме, возвращаемой os.Environ.
// Если это значение равно нулю, результат os. Будет использоваться Environ.
Env []string
// Если Args не равен нулю, он предоставляет аргументы командной строки для
// процесса-демона. Если он равен нулю, будет использоваться результат os.Args
// (без имени программы).
Args []string
// Учетные данные содержат идентификаторы пользователя и группы, которые должны быть приняты демоном-процессом.
Учетные данные *syscall.Credential
// Если Umask не равен нулю, демон-процесс вызывает функцию Umask() с заданным значение.
Umask int
// Структура содержит только сериализуемые общедоступные поля (!!!)
abspath string
logFile *os.File
nullFile *os.File
}
// Reborn запускает вторую копию текущего процесса в заданном контексте.
// функция выполняет отдельные части кода в дочернем и родительском процессах
// и обеспечивает демонизацию дочернего процесса. Выглядит аналогично
// fork-daemonization, но безопасен для горутин.
// В случае успеха возвращает *os.Process в родительском процессе и nil в дочернем процессе.
// В противном случае возвращает error.
func (d *Context) Reborn() (child *os.Process, err error) {
return d.reborn()
}
// Release обеспечивает корректный выпуск pid-файла в демоне.
func (d *Context) Release() (err error) {
return d.release()
}
func (d *Context) reborn() (child *os.Process, err error) {
child, err = d.parent()
return
}
func (d *Context) parent() (дочерний процесс *os.Process, ошибка ошибки) {
if err = d.prepareEnv(); ошибка != ноль {
возврат
}
отложить d.closeFiles()
, если err = d.openFiles(); ошибка != ноль {
возврат
}
attr := &os.ProcAttr{
Dir: d.WorkDir,
Env: d.Env,
Files: d.files(),
Sys: &syscall.SysProcAttr{< br /> //Chroot: d.Chroot,
Credential: d.Credential,
Setsid: true,
},
}
если дочерний, err = os.StartProcess(d.abspath, d.Args, attr); ошибка != ноль {
возврат
}
возврат
}
func (d *Context) openFiles() (ошибка ошибки) {
if d.nullFile, err = os.Open(os.DevNull); ошибка != ноль {
возврат
}
возврат
}
func (d *Context) closeFiles() (err error) {
cl := func(file **os.File) {
if *file != nil {
(*file) .Close()
*file = nil
}
}
cl(&d.nullFile)
возврат
}
func (d *Context) prepareEnv() (ошибка ошибки) {
if d.abspath, err = osext.Executable(); err != nil {
return
}
if len(d.Args) == 0 {
d.Args = os.Args
}
если len(d.Env) == 0 {
d.Env = os.Environ()
}
возврат
}
func (d *Context) files() (f []*os.File) {
log := d.nullFile
if d.logFile != nil {
log = d.logFile
}
f = []*os.File{
log, // (0) stdin
log, // (1) stdout
log, // (2) stderr
d .nullFile, // (3) дублирование fd 0 после инициализации
}
возврат
}
переменная инициализирована = ложь
func (d *Context) child() (err error) {
if initialized {
return os.ErrInvalid
}
initialized = true
если ошибка = syscall.Close(0); err != nil {
return
}
if err = syscall.Dup2(3, 0); ошибка != ноль {
возврат
}
if d.Umask != 0 {
syscall.Umask(int(d.Umask))
}
if len(d.Chroot) › 0 {
err = syscall. Chroot(d.Chroot)
}
возврат
}
func (d *Context) release() (ошибка ошибки) {
if !initialized {
return
}
возврат
}
func main() {
context := new(Context)
child, er := context.Reborn()
fmt.Println(er)
if child != nil {
fmt.Println("c")
time.Sleep(100 * time.Second)
} else {
fmt.Println("2" )
отложить context.Release()
}
}