delve/proc/exec_darwin.c
Evgeny L 4064d6acc0 Flag to set working directory (#650)
* proc: Add `wd` to Launch

This change adds the `wd` arg which specify working directory of the
program.

Fixes #295

* service/debugger: Add `Wd` field to debugger.Config

This change adds the `Wd` field which specify working directory of the
program launched by debugger.

Fixes #295

* service: Add `Wd` to service.Config

This change adds the `Wd` field which specify working directory of the
program debugger will launch.

Fixes #295

* cmd/dlv: Add `Wd` flag

This change adds `Wd` flag which specify working directory of the
program which launched by debugger.

Fixes #295

* only set the Linux working directory if it is set,
stub out param in darwin and windows

* set working directory for Windows
https://godoc.org/golang.org/x/sys/windows#CreateProcess
https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx

* Windows workingDir must be an *uint16

* attempt to chdir on darwin via @yuntan

* proc/exec_darwin.c: fix working directory for darwin

* Add tests to check if working directory works.
* Fix darwin implementation of fork/exec, which paniced if
  child fork returned.

* cmd, service: rename Wd to WorkingDir
2016-11-01 12:58:42 -07:00

111 lines
2.3 KiB
C

#include "exec_darwin.h"
#include "stdio.h"
extern char** environ;
int
close_exec_pipe(int fd[2]) {
if (pipe(fd) < 0) return -1;
if (fcntl(fd[0], F_SETFD, FD_CLOEXEC) < 0) return -1;
if (fcntl(fd[1], F_SETFD, FD_CLOEXEC) < 0) return -1;
return 0;
}
int
fork_exec(char *argv0, char **argv, int size,
char *wd,
task_t *task,
mach_port_t *port_set,
mach_port_t *exception_port,
mach_port_t *notification_port)
{
// Since we're using mach exceptions instead of signals,
// we need to coordinate between parent and child via pipes
// to ensure that the parent has set the exception ports on
// the child task before it execs.
int fd[2];
if (close_exec_pipe(fd) < 0) return -1;
// Create another pipe to signal the parent on exec.
int efd[2];
if (close_exec_pipe(efd) < 0) return -1;
kern_return_t kret;
pid_t pid = fork();
if (pid > 0) {
// In parent.
close(fd[0]);
close(efd[1]);
kret = acquire_mach_task(pid, task, port_set, exception_port, notification_port);
if (kret != KERN_SUCCESS) return -1;
char msg = 'c';
write(fd[1], &msg, 1);
close(fd[1]);
char w;
size_t n = read(efd[0], &w, 1);
close(efd[0]);
if (n != 0) {
// Child died, reap it.
waitpid(pid, NULL, 0);
return -1;
}
return pid;
}
// Fork succeeded, we are in the child.
int pret, cret;
char sig;
close(fd[1]);
read(fd[0], &sig, 1);
close(fd[0]);
// Create a new process group.
if (setpgid(0, 0) < 0) {
perror("setpgid");
exit(1);
}
// Set errno to zero before a call to ptrace.
// It is documented that ptrace can return -1 even
// for successful calls.
errno = 0;
pret = ptrace(PT_TRACE_ME, 0, 0, 0);
if (pret != 0 && errno != 0) {
perror("ptrace");
exit(1);
}
// Change working directory if wd is not empty.
if (wd && wd[0]) {
errno = 0;
cret = chdir(wd);
if (cret != 0 && errno != 0) {
char *error_msg;
asprintf(&error_msg, "%s '%s'", "chdir", wd);
perror(error_msg);
exit(1);
}
}
errno = 0;
pret = ptrace(PT_SIGEXC, 0, 0, 0);
if (pret != 0 && errno != 0) {
perror("ptrace");
exit(1);
}
// Create the child process.
execve(argv0, argv, environ);
// We should never reach here, but if we did something went wrong.
// Write a message to parent to alert that exec failed.
char msg = 'd';
write(efd[1], &msg, 1);
close(efd[1]);
exit(1);
}