Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add detach in scope cli #1110

Merged
merged 7 commits into from
Oct 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/cmd/attach.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
* userconfig X X X X X X X X X X -
*/

// attachCmd represents the run command
// attachCmd represents the attach command
var attachCmd = &cobra.Command{
Use: "attach [flags] PID | <process_name>",
Short: "Scope a currently-running process",
Expand Down
24 changes: 24 additions & 0 deletions cli/cmd/detach.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package cmd

import (
"github.com/criblio/scope/internal"
"github.com/spf13/cobra"
)

// detachCmd represents the detach command
var detachCmd = &cobra.Command{
Use: "detach [flags] PID | <process_name>",
Short: "Unscope a currently-running process",
Long: `Unscopes a currently-running process identified by PID or <process_name>.`,
Example: `scope detach 1000
scope detach firefox`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
internal.InitConfig()
return rc.Detach(args)
},
}

func init() {
RootCmd.AddCommand(detachCmd)
}
2 changes: 1 addition & 1 deletion cli/cmd/ps.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
/* Args Matrix (X disallows)
*/

// psCmd represents the run command
// psCmd represents the ps command
var psCmd = &cobra.Command{
Use: "ps",
Short: "List processes currently being scoped",
Expand Down
12 changes: 12 additions & 0 deletions cli/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,15 @@ func (sL *ScopeLoader) AttachSubProc(args []string, env []string) (string, error
args = append([]string{"--attach"}, args...)
return sL.RunSubProc(args, env)
}

// Detach transforms the calling process into a ldscope detach operation
func (sL *ScopeLoader) Detach(args []string, env []string) error {
args = append([]string{"--detach"}, args...)
return sL.Run(args, env)
}

// DetachSubProc runs a ldscope detach as a seperate process
func (sL *ScopeLoader) DetachSubProc(args []string, env []string) (string, error) {
args = append([]string{"--detach"}, args...)
return sL.RunSubProc(args, env)
}
168 changes: 105 additions & 63 deletions cli/run/attach.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,62 +21,43 @@ var (
errPidInvalid = errors.New("invalid PID")
errPidMissing = errors.New("PID does not exist")
errCreateLdscope = errors.New("error creating ldscope")
errAlreadyScope = errors.New("attach failed. This process is already being scoped")
errNotScoped = errors.New("detach failed. This process is not being scoped")
errLibraryNotExist = errors.New("library Path does not exist")
errInvalidSelection = errors.New("invalid Selection")
)

// Attach scopes an existing PID
func (rc *Config) Attach(args []string) error {
// Validate user has root permissions
if err := util.UserVerifyRootPerm(); err != nil {
return err
}
// Validate PTRACE capability
c, err := capability.NewPid2(0)
pid, err := handleInputArg(args[0])
if err != nil {
return errGetLinuxCap
}
err = c.Load()
if err != nil {
return errLoadLinuxCap
}
if !c.Get(capability.EFFECTIVE, capability.CAP_SYS_PTRACE) {
return errMissingPtrace
return err
}
// Get PID by name if non-numeric, otherwise validate/use args[0]
var pid int
if !util.IsNumeric(args[0]) {
procs, err := util.ProcessesByName(args[0])
if err != nil {
args[0] = fmt.Sprint(pid)
var reattach bool
// Check PID is not already being scoped
if !util.PidScoped(pid) {
// Validate user has root permissions
if err := util.UserVerifyRootPerm(); err != nil {
return err
}
if len(procs) == 1 {
pid = procs[0].Pid
} else if len(procs) > 1 {
fmt.Println("Found multiple processes matching that name...")
pid, err = choosePid(procs)
if err != nil {
return err
}
} else {
return errMissingProc
}
args[0] = fmt.Sprint(pid)
} else {
pid, err = strconv.Atoi(args[0])
// Validate PTRACE capability
c, err := capability.NewPid2(0)
if err != nil {
return errPidInvalid
return errGetLinuxCap
}

if err = c.Load(); err != nil {
return errLoadLinuxCap
}

if !c.Get(capability.EFFECTIVE, capability.CAP_SYS_PTRACE) {
return errMissingPtrace
}
} else {
// Reattach because process contains our library
reattach = true
}
// Check PID exists
if !util.PidExists(pid) {
return errPidMissing
}
// Check PID is not already being scoped
if util.PidScoped(pid) {
return errAlreadyScope
}

// Create ldscope
if err := createLdscope(); err != nil {
return errCreateLdscope
Expand All @@ -101,30 +82,91 @@ func (rc *Config) Attach(args []string) error {
// Prepend "-f" [PATH] to args
args = append([]string{"-f", rc.LibraryPath}, args...)
}
sL := loader.ScopeLoader{Path: ldscopePath()}
if reattach {
env = append(env, "SCOPE_CONF_RELOAD="+filepath.Join(rc.WorkDir, "scope.yml"))
}

ld := loader.ScopeLoader{Path: ldscopePath()}
if !rc.Subprocess {
return ld.Attach(args, env)
}
_, err = ld.AttachSubProc(args, env)
return err
}

// Detach unscopes an existing PID
func (rc *Config) Detach(args []string) error {
pid, err := handleInputArg(args[0])
if err != nil {
return err
}
args[0] = fmt.Sprint(pid)

// Check PID is already being scoped
if !util.PidScoped(pid) {
return errNotScoped
}

env := os.Environ()

// Create ldscope
if err := createLdscope(); err != nil {
return errCreateLdscope
}

ld := loader.ScopeLoader{Path: ldscopePath()}
if !rc.Subprocess {
return sL.Attach(args, env)
return ld.Detach(args, env)
}
_, err = sL.AttachSubProc(args, env)
_, err = ld.DetachSubProc(args, env)
return err
}

// choosePid presents a user interface for selecting a PID
func choosePid(procs util.Processes) (int, error) {
util.PrintObj([]util.ObjField{
{Name: "ID", Field: "id"},
{Name: "Pid", Field: "pid"},
{Name: "User", Field: "user"},
{Name: "Scoped", Field: "scoped"},
{Name: "Command", Field: "command"},
}, procs)
fmt.Println("Select an ID from the list:")
var selection string
fmt.Scanln(&selection)
i, err := strconv.ParseUint(selection, 10, 32)
i--
if err != nil || i >= uint64(len(procs)) {
return -1, errInvalidSelection
// handleInputArg handles the input argument (process id/name)
func handleInputArg(InputArg string) (int, error) {
// Get PID by name if non-numeric, otherwise validate/use InputArg
var pid int
var err error
if !util.IsNumeric(InputArg) {
procs, err := util.ProcessesByName(InputArg)
if err != nil {
return -1, err
}
if len(procs) == 1 {
pid = procs[0].Pid
} else if len(procs) > 1 {
// user interface for selecting a PID
fmt.Println("Found multiple processes matching that name...")
util.PrintObj([]util.ObjField{
{Name: "ID", Field: "id"},
{Name: "Pid", Field: "pid"},
{Name: "User", Field: "user"},
{Name: "Scoped", Field: "scoped"},
{Name: "Command", Field: "command"},
}, procs)
fmt.Println("Select an ID from the list:")
var selection string
fmt.Scanln(&selection)
i, err := strconv.ParseUint(selection, 10, 32)
i--
if err != nil || i >= uint64(len(procs)) {
return -1, errInvalidSelection
}
pid = procs[i].Pid
} else {
return -1, errMissingProc
}
} else {
pid, err = strconv.Atoi(InputArg)
if err != nil {
return -1, errPidInvalid
}
}

// Check PID exists
if !util.PidExists(pid) {
return -1, errPidMissing
}
return procs[i].Pid, nil

return pid, nil
}
29 changes: 22 additions & 7 deletions src/scope.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,15 @@ attach(pid_t pid, char *scopeLibPath)
}

static int
attachCmd(pid_t pid, const char *on_off)
attachCmd(pid_t pid, bool attach)
{
int fd;
char path[PATH_MAX];
char cmd[64];

scope_snprintf(path, sizeof(path), "%s/%s.%d",
DYN_CONFIG_CLI_DIR, DYN_CONFIG_CLI_PREFIX, pid);

fd = scope_open(path, O_RDWR|O_CREAT);
fd = scope_open(path, O_WRONLY|O_CREAT);
if (fd == -1) {
scope_perror("open() of dynamic config file");
return EXIT_FAILURE;
Expand Down Expand Up @@ -143,14 +142,30 @@ attachCmd(pid_t pid, const char *on_off)
return EXIT_FAILURE;
}
}

scope_snprintf(cmd, sizeof(cmd), "SCOPE_CMD_ATTACH=%s", on_off);
const char *cmd = (attach == TRUE) ? "SCOPE_CMD_ATTACH=true" : "SCOPE_CMD_ATTACH=false";
if (scope_write(fd, cmd, scope_strlen(cmd)) <= 0) {
scope_perror("scope_write() failed");
scope_close(fd);
return EXIT_FAILURE;
}

if (attach == TRUE) {
/*
* Reload the configuration during reattach if we want to redirect data
* into other place e.g via cli
*/
char *scopeConfReload = getenv("SCOPE_CONF_RELOAD");
if (scopeConfReload) {
char reloadCmd[PATH_MAX] = {0};
scope_snprintf(reloadCmd, sizeof(reloadCmd), "\nSCOPE_CONF_RELOAD=%s", scopeConfReload);
if (scope_write(fd, reloadCmd, scope_strlen(reloadCmd)) <= 0) {
scope_perror("scope_write() failed");
scope_close(fd);
return EXIT_FAILURE;
}
}
}

scope_close(fd);
return EXIT_SUCCESS;
}
Expand Down Expand Up @@ -262,7 +277,7 @@ main(int argc, char **argv, char **env)
} else {
// libscope exists, a reattach
scope_printf("Reattaching to pid %d\n", pid);
ret = attachCmd(pid, "true");
ret = attachCmd(pid, TRUE);
}

// remove the env var file
Expand All @@ -280,7 +295,7 @@ main(int argc, char **argv, char **env)
return EXIT_FAILURE;
}
scope_printf("Detaching from pid %d\n", pid);
return attachCmd(pid, "false");
return attachCmd(pid, FALSE);
} else {
scope_fprintf(scope_stderr, "error: attach or detach with invalid option\n");
showUsage(scope_basename(argv[0]));
Expand Down
4 changes: 2 additions & 2 deletions src/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -484,8 +484,8 @@ setupExtractFilterFile(void *filterFileMem, size_t filterSize) {
*
* Returns memory address in case of success, NULL otherwise.
*/
char*
setupLoadFileIntoMem(size_t *size, char *path)
char *
setupLoadFileIntoMem(size_t *size, const char *path)
{
// Load file into memory
char *resMem = NULL;
Expand Down
2 changes: 1 addition & 1 deletion src/setup.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@

service_status_t setupService(const char *);
int setupConfigure(void *, size_t);
char *setupLoadFileIntoMem(size_t *, char*);
char *setupLoadFileIntoMem(size_t *, const char *);

#endif // __SETUP_H__
5 changes: 3 additions & 2 deletions src/wrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -664,11 +664,12 @@ dynConfig(void)
// Modify the static config from the command file
cfgProcessCommands(g_staticfg, fs);

scope_fclose(fs);
scope_unlink(path);

// Apply the config
doConfig(g_staticfg);

scope_fclose(fs);
scope_unlink(path);
return 0;
}

Expand Down
Loading