2016-04-20 10:49:15 +08:00
|
|
|
// Functions for executing the jobs builtin.
|
2016-04-21 14:00:54 +08:00
|
|
|
#include "config.h" // IWYU pragma: keep
|
2006-05-26 19:38:11 +08:00
|
|
|
|
|
|
|
#include <errno.h>
|
2016-04-21 14:00:54 +08:00
|
|
|
#ifdef HAVE__PROC_SELF_STAT
|
|
|
|
#include <sys/time.h>
|
|
|
|
#endif
|
2006-05-26 19:38:11 +08:00
|
|
|
|
|
|
|
#include "builtin.h"
|
|
|
|
#include "common.h"
|
2016-04-20 10:49:15 +08:00
|
|
|
#include "fallback.h" // IWYU pragma: keep
|
2016-04-21 14:00:54 +08:00
|
|
|
#include "io.h"
|
2016-04-20 10:49:15 +08:00
|
|
|
#include "proc.h"
|
|
|
|
#include "wgetopt.h"
|
|
|
|
#include "wutil.h" // IWYU pragma: keep
|
2006-07-20 06:55:49 +08:00
|
|
|
|
2016-04-21 14:00:54 +08:00
|
|
|
class parser_t;
|
2006-05-26 19:38:11 +08:00
|
|
|
|
2016-04-20 10:49:15 +08:00
|
|
|
/// Print modes for the jobs builtin.
|
|
|
|
enum {
|
|
|
|
JOBS_DEFAULT, // print lots of general info
|
|
|
|
JOBS_PRINT_PID, // print pid of each process in job
|
|
|
|
JOBS_PRINT_COMMAND, // print command name of each process in job
|
|
|
|
JOBS_PRINT_GROUP, // print group id of job
|
|
|
|
};
|
2006-05-26 19:38:11 +08:00
|
|
|
|
|
|
|
#ifdef HAVE__PROC_SELF_STAT
|
2016-04-20 10:49:15 +08:00
|
|
|
/// Calculates the cpu usage (in percent) of the specified job.
|
|
|
|
static int cpu_use(const job_t *j) {
|
|
|
|
double u = 0;
|
2012-11-19 08:30:30 +08:00
|
|
|
process_t *p;
|
2006-05-26 19:38:11 +08:00
|
|
|
|
2016-04-20 10:49:15 +08:00
|
|
|
for (p = j->first_process; p; p = p->next) {
|
2012-11-19 08:30:30 +08:00
|
|
|
struct timeval t;
|
|
|
|
int jiffies;
|
|
|
|
gettimeofday(&t, 0);
|
|
|
|
jiffies = proc_get_jiffies(p);
|
2006-05-26 19:38:11 +08:00
|
|
|
|
2016-04-20 10:49:15 +08:00
|
|
|
double t1 = 1000000.0 * p->last_time.tv_sec + p->last_time.tv_usec;
|
|
|
|
double t2 = 1000000.0 * t.tv_sec + t.tv_usec;
|
2006-05-26 19:38:11 +08:00
|
|
|
|
2016-04-20 10:49:15 +08:00
|
|
|
// fwprintf( stderr, L"t1 %f t2 %f p1 %d p2 %d\n", t1, t2, jiffies, p->last_jiffies );
|
|
|
|
u += ((double)(jiffies - p->last_jiffies)) / (t2 - t1);
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2016-04-20 10:49:15 +08:00
|
|
|
return u * 1000000;
|
2006-05-26 19:38:11 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-04-20 10:49:15 +08:00
|
|
|
/// Print information about the specified job.
|
|
|
|
static void builtin_jobs_print(const job_t *j, int mode, int header, io_streams_t &streams) {
|
2012-11-19 08:30:30 +08:00
|
|
|
process_t *p;
|
2016-04-20 10:49:15 +08:00
|
|
|
switch (mode) {
|
|
|
|
case JOBS_DEFAULT: {
|
|
|
|
if (header) {
|
|
|
|
// Print table header before first job.
|
2015-09-22 02:24:49 +08:00
|
|
|
streams.out.append(_(L"Job\tGroup\t"));
|
2006-05-26 19:38:11 +08:00
|
|
|
#ifdef HAVE__PROC_SELF_STAT
|
2015-09-22 02:24:49 +08:00
|
|
|
streams.out.append(_(L"CPU\t"));
|
2006-05-26 19:38:11 +08:00
|
|
|
#endif
|
2015-09-22 02:24:49 +08:00
|
|
|
streams.out.append(_(L"State\tCommand\n"));
|
2012-11-19 16:31:03 +08:00
|
|
|
}
|
2006-05-26 19:38:11 +08:00
|
|
|
|
2016-04-20 10:49:15 +08:00
|
|
|
streams.out.append_format(L"%d\t%d\t", j->job_id, j->pgid);
|
2006-05-26 19:38:11 +08:00
|
|
|
|
|
|
|
#ifdef HAVE__PROC_SELF_STAT
|
2016-04-20 10:49:15 +08:00
|
|
|
streams.out.append_format(L"%d%%\t", cpu_use(j));
|
2006-05-26 19:38:11 +08:00
|
|
|
#endif
|
2016-04-20 10:49:15 +08:00
|
|
|
streams.out.append(job_is_stopped(j) ? _(L"stopped") : _(L"running"));
|
2015-09-22 02:24:49 +08:00
|
|
|
streams.out.append(L"\t");
|
|
|
|
streams.out.append(j->command_wcstr());
|
|
|
|
streams.out.append(L"\n");
|
2012-11-19 16:31:03 +08:00
|
|
|
break;
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2016-04-20 10:49:15 +08:00
|
|
|
case JOBS_PRINT_GROUP: {
|
|
|
|
if (header) {
|
|
|
|
// Print table header before first job.
|
2015-09-22 02:24:49 +08:00
|
|
|
streams.out.append(_(L"Group\n"));
|
2012-11-19 16:31:03 +08:00
|
|
|
}
|
2016-04-20 10:49:15 +08:00
|
|
|
streams.out.append_format(L"%d\n", j->pgid);
|
2012-11-19 16:31:03 +08:00
|
|
|
break;
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2016-04-20 10:49:15 +08:00
|
|
|
case JOBS_PRINT_PID: {
|
|
|
|
if (header) {
|
|
|
|
// Print table header before first job.
|
2015-09-22 02:24:49 +08:00
|
|
|
streams.out.append(_(L"Process\n"));
|
2012-11-19 16:31:03 +08:00
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-04-20 10:49:15 +08:00
|
|
|
for (p = j->first_process; p; p = p->next) {
|
|
|
|
streams.out.append_format(L"%d\n", p->pid);
|
2012-11-19 16:31:03 +08:00
|
|
|
}
|
|
|
|
break;
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2016-04-20 10:49:15 +08:00
|
|
|
case JOBS_PRINT_COMMAND: {
|
|
|
|
if (header) {
|
|
|
|
// Print table header before first job.
|
2015-09-22 02:24:49 +08:00
|
|
|
streams.out.append(_(L"Command\n"));
|
2012-11-19 16:31:03 +08:00
|
|
|
}
|
|
|
|
|
2016-04-20 10:49:15 +08:00
|
|
|
for (p = j->first_process; p; p = p->next) {
|
|
|
|
streams.out.append_format(L"%ls\n", p->argv0());
|
2012-11-19 16:31:03 +08:00
|
|
|
}
|
|
|
|
break;
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
}
|
2006-05-26 19:38:11 +08:00
|
|
|
}
|
|
|
|
|
2016-04-20 10:49:15 +08:00
|
|
|
/// The jobs builtin. Used fopr printing running jobs. Defined in builtin_jobs.c.
|
|
|
|
int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2015-07-26 09:16:00 +08:00
|
|
|
wgetopter_t w;
|
2016-04-20 10:49:15 +08:00
|
|
|
int argc = 0;
|
|
|
|
int found = 0;
|
|
|
|
int mode = JOBS_DEFAULT;
|
2012-11-19 08:30:30 +08:00
|
|
|
int print_last = 0;
|
|
|
|
|
|
|
|
argc = builtin_count_args(argv);
|
2016-04-20 10:49:15 +08:00
|
|
|
w.woptind = 0;
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2016-04-20 10:49:15 +08:00
|
|
|
while (1) {
|
|
|
|
static const struct woption long_options[] = {
|
|
|
|
{L"pid", no_argument, 0, 'p'}, {L"command", no_argument, 0, 'c'},
|
|
|
|
{L"group", no_argument, 0, 'g'}, {L"last", no_argument, 0, 'l'},
|
|
|
|
{L"help", no_argument, 0, 'h'}, {0, 0, 0, 0}};
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2012-11-19 08:30:30 +08:00
|
|
|
int opt_index = 0;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-04-20 10:49:15 +08:00
|
|
|
int opt = w.wgetopt_long(argc, argv, L"pclgh", long_options, &opt_index);
|
|
|
|
if (opt == -1) break;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-04-20 10:49:15 +08:00
|
|
|
switch (opt) {
|
|
|
|
case 0: {
|
|
|
|
if (long_options[opt_index].flag != 0) break;
|
|
|
|
streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0],
|
|
|
|
long_options[opt_index].name);
|
2006-05-26 19:38:11 +08:00
|
|
|
|
2015-09-22 02:24:49 +08:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 16:31:03 +08:00
|
|
|
return 1;
|
2016-04-20 10:49:15 +08:00
|
|
|
}
|
|
|
|
case 'p': {
|
|
|
|
mode = JOBS_PRINT_PID;
|
2012-11-19 16:31:03 +08:00
|
|
|
break;
|
2016-04-20 10:49:15 +08:00
|
|
|
}
|
|
|
|
case 'c': {
|
|
|
|
mode = JOBS_PRINT_COMMAND;
|
2012-11-19 16:31:03 +08:00
|
|
|
break;
|
2016-04-20 10:49:15 +08:00
|
|
|
}
|
|
|
|
case 'g': {
|
|
|
|
mode = JOBS_PRINT_GROUP;
|
2012-11-19 16:31:03 +08:00
|
|
|
break;
|
2016-04-20 10:49:15 +08:00
|
|
|
}
|
|
|
|
case 'l': {
|
2012-11-19 16:31:03 +08:00
|
|
|
print_last = 1;
|
|
|
|
break;
|
|
|
|
}
|
2016-04-20 10:49:15 +08:00
|
|
|
case 'h': {
|
2015-09-22 02:24:49 +08:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.out);
|
2012-11-19 16:31:03 +08:00
|
|
|
return 0;
|
2016-04-20 10:49:15 +08:00
|
|
|
}
|
|
|
|
case '?': {
|
|
|
|
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
2012-11-19 16:31:03 +08:00
|
|
|
return 1;
|
2016-04-20 10:49:15 +08:00
|
|
|
}
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
}
|
2006-05-26 19:38:11 +08:00
|
|
|
|
2016-04-20 10:49:15 +08:00
|
|
|
if (print_last) {
|
|
|
|
// Ignore unconstructed jobs, i.e. ourself.
|
2012-11-19 08:30:30 +08:00
|
|
|
job_iterator_t jobs;
|
|
|
|
const job_t *j;
|
2016-04-20 10:49:15 +08:00
|
|
|
while ((j = jobs.next())) {
|
|
|
|
if ((j->flags & JOB_CONSTRUCTED) && !job_is_completed(j)) {
|
2015-11-25 21:37:48 +08:00
|
|
|
builtin_jobs_print(j, mode, !streams.out_is_redirected, streams);
|
2012-11-19 08:30:30 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-04-20 10:49:15 +08:00
|
|
|
} else {
|
|
|
|
if (w.woptind < argc) {
|
2016-10-02 08:21:40 +08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = w.woptind; i < argc; i++) {
|
|
|
|
int pid;
|
2012-11-19 08:30:30 +08:00
|
|
|
wchar_t *end;
|
2016-10-02 08:21:40 +08:00
|
|
|
errno = 0;
|
|
|
|
pid = fish_wcstoi(argv[i], &end, 10);
|
|
|
|
if (errno || *end) {
|
2016-04-20 10:49:15 +08:00
|
|
|
streams.err.append_format(_(L"%ls: '%ls' is not a job\n"), argv[0], argv[i]);
|
2012-11-19 08:30:30 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-01-13 05:33:35 +08:00
|
|
|
const job_t *j = job_get_from_pid(pid);
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2016-04-20 10:49:15 +08:00
|
|
|
if (j && !job_is_completed(j)) {
|
2015-11-25 21:37:48 +08:00
|
|
|
builtin_jobs_print(j, mode, false, streams);
|
|
|
|
found = 1;
|
2016-04-20 10:49:15 +08:00
|
|
|
} else {
|
|
|
|
streams.err.append_format(_(L"%ls: No suitable job: %d\n"), argv[0], pid);
|
2012-11-19 08:30:30 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2016-04-20 10:49:15 +08:00
|
|
|
} else {
|
2012-01-30 08:36:21 +08:00
|
|
|
job_iterator_t jobs;
|
2012-02-28 10:43:24 +08:00
|
|
|
const job_t *j;
|
2016-04-20 10:49:15 +08:00
|
|
|
while ((j = jobs.next())) {
|
|
|
|
// Ignore unconstructed jobs, i.e. ourself.
|
|
|
|
if ((j->flags & JOB_CONSTRUCTED) && !job_is_completed(j)) {
|
2016-06-09 07:09:29 +08:00
|
|
|
builtin_jobs_print(j, mode, !found && !streams.out_is_redirected, streams);
|
2012-11-19 08:30:30 +08:00
|
|
|
found = 1;
|
|
|
|
}
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-20 10:49:15 +08:00
|
|
|
if (!found) {
|
|
|
|
// Do not babble if not interactive.
|
|
|
|
if (!streams.out_is_redirected) {
|
|
|
|
streams.out.append_format(_(L"%ls: There are no jobs\n"), argv[0]);
|
2015-11-25 21:37:48 +08:00
|
|
|
}
|
2014-06-08 14:27:06 +08:00
|
|
|
return 1;
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2012-11-19 08:30:30 +08:00
|
|
|
return 0;
|
2006-05-26 19:38:11 +08:00
|
|
|
}
|