X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=run.go;h=589889e3db6edbf62f51f626e5f9ed04394323fd;hb=3a29962a4bfcb0cf6cdfd112fb87f68751b0a0dc;hp=bb455e845df4b20abc91be0a10c584367b43a301;hpb=e14100898b08c312919f58f712f19f623f602748;p=goredo.git diff --git a/run.go b/run.go index bb455e8..589889e 100644 --- a/run.go +++ b/run.go @@ -21,7 +21,6 @@ package main import ( "bufio" - "encoding/hex" "errors" "flag" "fmt" @@ -29,13 +28,14 @@ import ( "os" "os/exec" "path" - "strconv" + "path/filepath" "strings" "sync" "syscall" "time" "go.cypherpunks.ru/recfile" + "go.cypherpunks.ru/tai64n" "golang.org/x/sys/unix" ) @@ -63,9 +63,10 @@ var ( StderrPrefix string Jobs sync.WaitGroup - flagTrace = flag.Bool("x", false, "trace current target (sh -x) (set REDO_TRACE=1 for others too)") - flagStderrKeep = flag.Bool("logs", false, "keep job's stderr (REDO_LOGS=1)") - flagStderrSilent = flag.Bool("silent", false, "do not print job's stderr (REDO_SILENT=1)") + flagTrace = flag.Bool("x", false, "trace (sh -x) current targets") + flagTraceAll = flag.Bool("xx", false, fmt.Sprintf("trace (sh -x) all targets (%s=1)", EnvTrace)) + flagStderrKeep = flag.Bool("logs", false, fmt.Sprintf("keep job's stderr (%s=1)", EnvStderrKeep)) + flagStderrSilent = flag.Bool("silent", false, fmt.Sprintf("do not print job's stderr (%s=1)", EnvStderrSilent)) ) type RunErr struct { @@ -102,16 +103,6 @@ func mkdirs(pth string) error { return os.MkdirAll(pth, os.FileMode(0777)) } -func tempsuffix() string { - return strconv.FormatInt((time.Now().UnixNano()+int64(os.Getpid()))&0xFFFFFFFF, 16) -} - -func tempfile(dir, prefix string) (*os.File, error) { - // It respects umask, unlike ioutil.TempFile - name := path.Join(dir, TmpPrefix+prefix+"."+tempsuffix()) - return os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, os.FileMode(0666)) -} - func isModified(cwd, redoDir, tgt string) (bool, string, error) { fdDep, err := os.Open(path.Join(redoDir, tgt+DepSuffix)) if err != nil { @@ -193,14 +184,20 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { fdLock.Close() return TgtErr{tgtOrig, err} } - trace(CDebug, "waiting: %s", tgtOrig) Jobs.Add(1) + trace(CDebug, "waiting: %s", tgtOrig) + if FdStatus != nil { + FdStatus.Write([]byte{StatusWait}) + } go func() { defer Jobs.Done() trace(CLock, "LOCK_EX: %s", fdLock.Name()) unix.Flock(int(fdLock.Fd()), unix.LOCK_EX) lockRelease() trace(CDebug, "waiting done: %s", tgtOrig) + if FdStatus != nil { + FdStatus.Write([]byte{StatusWaited}) + } var depInfo *DepInfo fdDep, err := os.Open(path.Join(redoDir, tgt+DepSuffix)) if err != nil { @@ -269,10 +266,6 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { cleanup() return TgtErr{tgtOrig, errors.New("no .do found")} } - if err = writeDep(fdDep, cwd, doFile); err != nil { - cleanup() - return TgtErr{tgtOrig, err} - } // Determine basename and DIRPREFIX ents := strings.Split(cwd, "/") @@ -283,64 +276,53 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { cwd = path.Join(cwd, "..") } cwd = path.Clean(cwd) + doFilePath := path.Join(cwd, doFile) basename := tgt runErr := RunErr{Tgt: tgtOrig} if strings.HasPrefix(doFile, "default.") { basename = tgt[:len(tgt)-(len(doFile)-len("default.")-len(".do"))-1] runErr.DoFile = doFile } + + if err = writeDep(fdDep, cwd, doFile); err != nil { + cleanup() + return TgtErr{tgtOrig, err} + } trace(CWait, "%s", runErr.Name()) - doFile = path.Base(doFile) // Prepare command line var cmdName string var args []string - if err = unix.Access(path.Join(cwd, doFile), unix.X_OK); err == nil { - // Ordinary executable file - cmdName = doFile + if err = unix.Access(doFilePath, unix.X_OK); err == nil { + cmdName = doFilePath args = make([]string, 0, 3) } else { - fd, err := os.Open(path.Join(cwd, doFile)) - if err != nil { - cleanup() - return TgtErr{tgtOrig, err} - } - buf := make([]byte, 512) - n, err := fd.Read(buf) - if err != nil { - cleanup() - return TgtErr{tgtOrig, err} - } - if n > 3 && string(buf[:3]) == "#!/" { - // Shebanged - t := string(buf[2:n]) - nlIdx := strings.Index(t, "\n") - if nlIdx == -1 { - cleanup() - return TgtErr{tgtOrig, errors.New("not fully read shebang")} - } - args = strings.Split(t[:nlIdx], " ") - cmdName, args = args[0], args[1:] + cmdName = "/bin/sh" + if traced { + args = append(args, "-ex") } else { - // Shell - cmdName = "/bin/sh" - if traced { - args = append(args, "-ex") - } else { - args = append(args, "-e") - } + args = append(args, "-e") } args = append(args, doFile) } // Temporary file for stdout - fdStdout, err := tempfile(cwd, tgt) + fdStdout, err := tempfile(cwdOrig, tgt) if err != nil { cleanup() return TgtErr{tgtOrig, err} } tmpPath := fdStdout.Name() + ".3" // and for $3 - args = append(args, tgt, basename, path.Base(tmpPath)) + tmpPathRel, err := filepath.Rel(cwd, tmpPath) + if err != nil { + panic(err) + } + args = append( + args, + path.Join(dirPrefix, tgt), + path.Join(dirPrefix, basename), + tmpPathRel, + ) cmd := exec.Command(cmdName, args...) cmd.Dir = cwd @@ -354,10 +336,19 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { "%s=%s", EnvStderrPrefix, childStderrPrefix, )) - cmd.ExtraFiles = append(cmd.ExtraFiles, fdDep) fdNum := 0 + cmd.ExtraFiles = append(cmd.ExtraFiles, fdDep) cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", EnvDepFd, 3+fdNum)) fdNum++ + + if FdStatus == nil { + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=NO", EnvStatusFd)) + } else { + cmd.ExtraFiles = append(cmd.ExtraFiles, FdStatus) + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", EnvStatusFd, 3+fdNum)) + fdNum++ + } + if JSR == nil { // infinite jobs cmd.Env = append(cmd.Env, fmt.Sprintf("%s=NO", EnvJSFd)) @@ -394,6 +385,9 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { Jobs.Add(1) go func() { jsAcquire(shCtx) + if FdStatus != nil { + FdStatus.Write([]byte{StatusRun}) + } defer func() { jsRelease(shCtx) lockRelease() @@ -406,6 +400,9 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { os.Remove(fdStdout.Name()) os.Remove(tmpPath) os.Remove(fdLock.Name()) + if FdStatus != nil { + FdStatus.Write([]byte{StatusDone}) + } Jobs.Done() }() started := time.Now() @@ -423,7 +420,7 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { go func() { scanner := bufio.NewScanner(stderr) var line string - ts := new(TAI64N) + ts := new(tai64n.TAI64N) for scanner.Scan() { line = scanner.Text() if strings.HasPrefix(line, childStderrPrefix) { @@ -432,8 +429,10 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { continue } if fdStderr != nil { - tai64nNow(ts) - fmt.Fprintf(fdStderr, "@%s %s\n", hex.EncodeToString(ts[:]), line) + tai64n.FromTime(time.Now(), ts) + LogMutex.Lock() + fmt.Fprintf(fdStderr, "%s %s\n", ts.Encode(), line) + LogMutex.Unlock() } if StderrSilent { continue