Re: [RFC] lttngtrace available to non-root (-o option)

From: Mathieu Desnoyers
Date: Sat Nov 20 2010 - 09:24:15 EST


Hi,

Due to popular demand, I added a "-o" option to let the user specify the trace
file name. If the directory/file specified with -o already exist, it just fails
without unlinking. Now I put the "pid" file under the trace directory.

Thanks,

Mathieu

/*
* lttngtrace.c
*
* lttngtrace starts/stop system wide tracing around program execution.
*
* Copyright (c) 2010 Mathieu Desnoyers <mathieu.desnoyers@xxxxxxxxxxxx>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This file should be setuid root, and belong to a "tracing" group. Only users
* part of the tracing group can trace and view the traces gathered.
*
* TODO: LTTng should support per-session tracepoint activation.
* TODO: use mkstemp() and save last trace name in user's home directory.
*/

#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <dirent.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/types.h>

#if DEBUG
#define printf_dbg(fmt, args...) printf(fmt, args)
#else
#define printf_dbg(fmt, ...)
#endif

static char *trace_path;
static char trace_path_pid[PATH_MAX];
static int autotrace; /*
* Is the trace_path automatically chosen in /tmp ? Can
* we unlink if needed ?
*/

int recunlink(const char *dirname)
{
DIR *dir;
struct dirent *entry;
char path[PATH_MAX];

dir = opendir(dirname);
if (dir == NULL) {
if (errno == ENOENT)
return 0;
perror("Error opendir()");
return -errno;
}

while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) {
snprintf(path, (size_t) PATH_MAX, "%s/%s", dirname,
entry->d_name);
if (entry->d_type == DT_DIR)
recunlink(path);
else
unlink(path);
}
}
closedir(dir);
rmdir(dirname);

return 0;
}

int start_tracing(void)
{
int ret;
char command[PATH_MAX];

if (autotrace) {
ret = recunlink(trace_path);
if (ret)
return ret;
}

/*
* Create the directory in /tmp to deal with races (refuse if fail).
* Only allow user and group to read the trace data (to limit
* information disclosure).
*/
ret = mkdir(trace_path, S_IRWXU|S_IRWXG);
if (ret) {
perror("Trace directory creation error");
return ret;
}
ret = system("ltt-armall > /dev/null");
if (ret)
return ret;

ret = snprintf(command, PATH_MAX - 1,
"lttctl -C -w %s autotrace1 > /dev/null",
trace_path);
ret = ret < 0 ? ret : 0;
if (ret)
return ret;
ret = system(command);
if (ret)
return ret;
}

int stop_tracing(uid_t uid, gid_t egid)
{
int ret;

ret = system("lttctl -D autotrace1 > /dev/null");
if (ret)
return ret;
ret = system("ltt-disarmall > /dev/null");
if (ret)
return ret;
/* Hand the trace back to the user after tracing is over */
ret = chown(trace_path, uid, egid);
if (ret) {
perror("chown error");
return ret;
}
}

int write_child_pid(pid_t pid, uid_t uid, gid_t gid)
{
int fd;
FILE *fp;
int ret;

/* Create the file as exclusive to deal with /tmp file creation races */
fd = open(trace_path_pid, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
fp = fdopen(fd, "w");
if (!fp) {
perror("Error writing child pid");
return -errno;
}

fprintf(fp, "%u", (unsigned int) pid);
ret = fclose(fp);
if (ret)
perror("Error in fclose");
/* Hand pid information file back to user */
ret = chown(trace_path_pid, uid, gid);
if (ret)
perror("chown error");
return ret;
}

int parse_options(int argc, char *argv[], int *arg)
{
int ret = 0;

for (;;) {
if (*arg >= argc
|| argv[*arg][0] != '-'
|| argv[*arg][0] == '\0'
|| argv[*arg][1] == '\0'
|| !strcmp(argv[*arg], "--"))
break;
switch (argv[*arg][1]) {
case 'o': if (*arg + 1 >= argc) {
printf("Missing -o trace name\n");
ret = -EINVAL;
break;
}
trace_path = argv[*arg + 1];
(*arg) += 2;
break;
default: printf("Unknown option -%c\n", argv[*arg][1]);
ret = -EINVAL;
return ret;
}
}
return ret;
}

int set_trace_path(void)
{
int ret;

if (!trace_path) {
trace_path = "/tmp/autotrace1";
autotrace = 1;
}
ret = snprintf(trace_path_pid, PATH_MAX - 1, "%s/%s",
trace_path, "pid");
ret = ret < 0 ? ret : 0;
return ret;
}

int main(int argc, char *argv[])
{
uid_t euid, uid;
gid_t egid, gid;
pid_t pid;
int gret = 0, ret = 0;
int arg = 1;

if (argc < 2)
return -ENOENT;

euid = geteuid();
uid = getuid();
egid = getegid();
gid = geteuid();

if (euid != 0 && uid != 0) {
printf("%s must be setuid root\n", argv[0]);
return -EPERM;
}

printf_dbg("euid: %d\n", euid);
printf_dbg("uid: %d\n", uid);
printf_dbg("egid: %d\n", egid);
printf_dbg("gid: %d\n", gid);

if (arg < argc) {
ret = parse_options(argc, argv, &arg);
if (ret)
return ret;
}

ret = set_trace_path();
gret = (gret == 0) ? ret : gret;

ret = start_tracing();
if (ret)
return ret;

pid = fork();
if (pid > 0) { /* parent */
int status;

pid = wait(&status);
if (pid == -1)
gret = (gret == 0) ? -errno : gret;

ret = stop_tracing(uid, egid);
gret = (gret == 0) ? ret : gret;
ret = write_child_pid(pid, uid, egid);
gret = (gret == 0) ? ret : gret;
} else if (pid == 0) { /* child */
/* Drop root euid before executing child program */
seteuid(uid);
ret = execvp(argv[arg], &argv[arg]);
if (ret)
perror("Execution error");
return ret;
} else { /* error */
perror("Error in fork");
return -errno;
}
return ret;
}

--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/