Skip to content
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
41 changes: 41 additions & 0 deletions Lsof.8
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,47 @@ When specified without a PGID set that's all it does.
.B \-H
directs lsof to print human readable sizes, e.g. 123.4K 456.7M.
.TP \w'names'u+4
.B \-J
selects JSON output mode. Instead of the traditional tabular or
.B \-F
field output, lsof produces a single JSON object on stdout containing
a
.B "processes"
array. Each process object contains its fields and a
.B "files"
array of open file entries.
.IP
Field selection follows the same rules as
.BR \-F :
use
.B \-F
with field characters to select which fields appear in the JSON output.
Without
.BR \-F ,
the default field set is used.
.IP
.B \-J
is mutually exclusive with
.B \-j
and
.BR \-t .
Warnings and errors are sent to stderr; stdout is always valid JSON.
.TP \w'names'u+4
.B \-j
selects JSON Lines output mode. Each open file produces one JSON
object per line, combining process and file fields in a single
denormalized record. This format is suitable for streaming pipelines,
log ingestion (Splunk, Datadog, Elastic), and line\-oriented tools.
.IP
Field selection follows the same rules as
.BR \-J .
.IP
.B \-j
is mutually exclusive with
.B \-J
and
.BR \-t .
.TP \w'names'u+4
.BI \-i " [i]"
selects the listing of files any of whose Internet address
matches the address specified in \fIi\fP.
Expand Down
4 changes: 3 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,9 @@ DIALECT_NEUTRAL_TESTS = tests/case-00-hello.bash \
tests/case-20-offset-field.bash \
tests/case-20-repeat-count.bash \
tests/case-21-exit-Q-status.bash \
tests/case-22-empty-process-name.bash
tests/case-22-empty-process-name.bash \
tests/case-30-json-output.bash \
tests/case-31-jsonl-output.bash
TESTS = $(DIALECT_NEUTRAL_TESTS)
EXTRA_DIST += $(DIALECT_NEUTRAL_TESTS) \
tests/case-13-classic.bash \
Expand Down
3 changes: 3 additions & 0 deletions lib/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,9 @@ extern int ErrStat;
extern uid_t Euid;
extern int Fcntx;
extern int Ffield;
extern int Fjson;
extern int Fjsonl;
extern int Fjson_first_proc;
extern int Ffilesys;
extern int Fhelp;
extern int Fhost;
Expand Down
2 changes: 1 addition & 1 deletion lib/dialects/aix/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ misc.o: ${HDR} misc.c

node.o: ${HDR} node.c

print.o: ${HDR} print.c
print.o: ${HDR} version.h print.c

proc.o: ${HDR} proc.c

Expand Down
2 changes: 1 addition & 1 deletion lib/dialects/darwin/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ misc.o: ${HDR} misc.c

node.o: ${HDR} node.c

print.o: ${HDR} print.c
print.o: ${HDR} version.h print.c

proc.o: ${HDR} proc.c

Expand Down
2 changes: 1 addition & 1 deletion lib/dialects/freebsd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ misc.o: ${HDR} misc.c

node.o: ${HDR} node.c

print.o: ${HDR} print.c
print.o: ${HDR} version.h print.c

proc.o: ${HDR} proc.c

Expand Down
2 changes: 1 addition & 1 deletion lib/dialects/hpux/kmem/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ misc.o: ${HDR} misc.c

node.o: ${HDR} node.c

print.o: ${HDR} print.c
print.o: ${HDR} version.h print.c

proc.o: ${HDR} proc.c

Expand Down
2 changes: 1 addition & 1 deletion lib/dialects/hpux/pstat/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ misc.o: ${HDR} misc.c

node.o: ${HDR} node.c

print.o: ${HDR} print.c
print.o: ${HDR} version.h print.c

proc.o: ${HDR} proc.c

Expand Down
2 changes: 1 addition & 1 deletion lib/dialects/linux/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ misc.o: ${HDR} misc.c

node.o: ${HDR} node.c

print.o: ${HDR} print.c
print.o: ${HDR} version.h print.c

proc.o: ${HDR} proc.c

Expand Down
2 changes: 1 addition & 1 deletion lib/dialects/netbsd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ misc.o: ${HDR} misc.c

node.o: ${HDR} node.c

print.o: ${HDR} print.c
print.o: ${HDR} version.h print.c

proc.o: ${HDR} proc.c

Expand Down
2 changes: 1 addition & 1 deletion lib/dialects/openbsd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ misc.o: ${HDR} misc.c

node.o: ${HDR} node.c

print.o: ${HDR} print.c
print.o: ${HDR} version.h print.c

proc.o: ${HDR} proc.c

Expand Down
2 changes: 1 addition & 1 deletion lib/dialects/osr/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ misc.o: ${HDR} misc.c

node.o: ${HDR} node.c

print.o: ${HDR} print.c
print.o: ${HDR} version.h print.c

proc.o: ${HDR} proc.c

Expand Down
2 changes: 1 addition & 1 deletion lib/dialects/sun/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ misc.o: ${HDR} misc.c

node.o: ${HDR} node.c

print.o: ${HDR} print.c
print.o: ${HDR} version.h print.c

proc.o: ${HDR} proc.c

Expand Down
2 changes: 1 addition & 1 deletion lib/dialects/uw/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ misc.o: ${HDR} misc.c

node.o: ${HDR} node.c

print.o: ${HDR} print.c
print.o: ${HDR} version.h print.c

proc.o: ${HDR} proc.c

Expand Down
2 changes: 2 additions & 0 deletions lib/proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ extern void print_init(struct lsof_context *ctx);
extern void printname(struct lsof_context *ctx, int nl);
extern char *print_kptr(KA_T kp, char *buf, size_t bufl);
extern int print_proc(struct lsof_context *ctx);
extern void json_open_envelope(void);
extern void json_close_envelope(void);
extern void fd_to_string(enum lsof_fd_type fd_type, int fd_num, char *buf);
extern void printrawaddr(struct lsof_context *ctx, struct sockaddr *sa);
extern void print_tcptpi(struct lsof_context *ctx, int nl);
Expand Down
156 changes: 109 additions & 47 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,58 @@ static int GetOpt(struct lsof_context *ctx, int ct, char *opt[], char *rules,
int *err);
static char *sv_fmt_str(struct lsof_context *ctx, char *f);

/*
* select_default_fields() - enable the default set of FieldSel entries
*
* Shared by -F (with no field chars) and -J/-j (when no -F given).
*/
static void select_default_fields(void) {
int i;
for (i = 0; FieldSel[i].nm; i++) {

#if !defined(HASPPID)
if (FieldSel[i].id == LSOF_FID_PPID)
continue;
#endif /* !defined(HASPPID) */

#if !defined(HASTASKS)
if (FieldSel[i].id == LSOF_FID_TCMD)
continue;
#endif /* !defined(HASTASKS) */

#if !defined(HASFSTRUCT)
if (FieldSel[i].id == LSOF_FID_CT || FieldSel[i].id == LSOF_FID_FA ||
FieldSel[i].id == LSOF_FID_FG || FieldSel[i].id == LSOF_FID_NI)
continue;
#endif /* !defined(HASFSTRUCT) */

#if defined(HASSELINUX)
if ((FieldSel[i].id == LSOF_FID_CNTX) && !CntxStatus)
continue;
#else /* !defined(HASSELINUX) */
if (FieldSel[i].id == LSOF_FID_CNTX)
continue;
#endif /* !defined(HASSELINUX) */

if (FieldSel[i].id == LSOF_FID_RDEV)
continue; /* for compatibility */

#if !defined(HASTASKS)
if (FieldSel[i].id == LSOF_FID_TID)
continue;
#endif /* !defined(HASTASKS) */

#if !defined(HASZONES)
if (FieldSel[i].id == LSOF_FID_ZONE)
continue;
#endif /* !defined(HASZONES) */

FieldSel[i].st = 1;
if (FieldSel[i].opt && FieldSel[i].ov)
*(FieldSel[i].opt) |= FieldSel[i].ov;
}
}

/*
* main() - main function for lsof
*/
Expand Down Expand Up @@ -151,7 +203,7 @@ int main(int argc, char *argv[]) {
* Create option mask.
*/
(void)snpf(options, sizeof(options),
"?a%sbc:%sD:d:%s%sf:F:g:hHi:%s%slL:%s%snNo:Op:QPr:%ss:S:tT:u:"
"?a%sbc:%sD:d:%s%sf:F:g:hHi:%s%sJjlL:%s%snNo:Op:QPr:%ss:S:tT:u:"
"UvVwx:%s%s%s",

#if defined(HAS_AFS) && defined(HASAOPT)
Expand Down Expand Up @@ -429,51 +481,7 @@ int main(int argc, char *argv[]) {
} else if (*GOv == '0')
Terminator = '\0';
}
for (i = 0; FieldSel[i].nm; i++) {

#if !defined(HASPPID)
if (FieldSel[i].id == LSOF_FID_PPID)
continue;
#endif /* !defined(HASPPID) */

#if !defined(HASTASKS)
if (FieldSel[i].id == LSOF_FID_TCMD)
continue;
#endif /* !defined(HASTASKS) */

#if !defined(HASFSTRUCT)
if (FieldSel[i].id == LSOF_FID_CT ||
FieldSel[i].id == LSOF_FID_FA ||
FieldSel[i].id == LSOF_FID_FG ||
FieldSel[i].id == LSOF_FID_NI)
continue;
#endif /* !defined(HASFSTRUCT) */

#if defined(HASSELINUX)
if ((FieldSel[i].id == LSOF_FID_CNTX) && !CntxStatus)
continue;
#else /* !defined(HASSELINUX) */
if (FieldSel[i].id == LSOF_FID_CNTX)
continue;
#endif /* !defined(HASSELINUX) */

if (FieldSel[i].id == LSOF_FID_RDEV)
continue; /* for compatibility */

#if !defined(HASTASKS)
if (FieldSel[i].id == LSOF_FID_TID)
continue;
#endif /* !defined(HASTASKS) */

#if !defined(HASZONES)
if (FieldSel[i].id == LSOF_FID_ZONE)
continue;
#endif /* !defined(HASZONES) */

FieldSel[i].st = 1;
if (FieldSel[i].opt && FieldSel[i].ov)
*(FieldSel[i].opt) |= FieldSel[i].ov;
}
select_default_fields();

#if defined(HASFSTRUCT)
Ffield = FsvFlagX = 1;
Expand Down Expand Up @@ -556,6 +564,24 @@ int main(int argc, char *argv[]) {
case '?':
Fhelp = 1;
break;
case 'J':
if (GOp == '+') {
(void)fprintf(stderr, "%s: +J is not supported\n", Pn);
err = 1;
break;
}
Fjson = 1;
Ffield = 1;
break;
case 'j':
if (GOp == '+') {
(void)fprintf(stderr, "%s: +j is not supported\n", Pn);
err = 1;
break;
}
Fjsonl = 1;
Ffield = 1;
break;
case 'i':
if (!GOv || *GOv == '-' || *GOv == '+') {
Fnet = 1;
Expand Down Expand Up @@ -1097,6 +1123,14 @@ int main(int argc, char *argv[]) {
(void)fprintf(stderr, "%s: -x must accompany +d or +D\n", Pn);
err++;
}
if (Fjson && Fjsonl) {
(void)fprintf(stderr, "%s: -J and -j are mutually exclusive\n", Pn);
err++;
}
if ((Fjson || Fjsonl) && Fterse) {
(void)fprintf(stderr, "%s: -J/-j and -t are mutually exclusive\n", Pn);
err++;
}

#if defined(HASEOPT)
if (Efsysl) {
Expand Down Expand Up @@ -1126,6 +1160,24 @@ int main(int argc, char *argv[]) {
}
#endif /* defined(HASEOPT) */

/*
* If -J/-j was given, ensure field selections are set.
* If -F was also given with field chars, those selections are already
* in FieldSel[]. Otherwise, enable the default field set.
*/
if (Fjson || Fjsonl) {
int has_fields = 0;
for (i = 0; FieldSel[i].nm; i++) {
if (FieldSel[i].st && FieldSel[i].id != LSOF_FID_PID &&
FieldSel[i].id != LSOF_FID_MARK) {
has_fields = 1;
break;
}
}
if (!has_fields)
select_default_fields();
}

if (DChelp || err || Fhelp || fh || version)
usage(ctx, err ? 1 : 0, fh, version);
/*
Expand Down Expand Up @@ -1312,6 +1364,9 @@ int main(int argc, char *argv[]) {
(void)qsort((QSORT_P *)slp, (size_t)Nlproc,
(size_t)sizeof(struct lproc *), comppid);
}
if (Fjson) {
json_open_envelope();
}
if ((n = Nlproc)) {

#if defined(HASNCACHE)
Expand Down Expand Up @@ -1468,6 +1523,11 @@ int main(int argc, char *argv[]) {
}
Lf = lf;
}
if (Fjson) {
json_close_envelope();
} else if (Fjsonl && RptTm) {
putchar('\n');
}
/*
* If a repeat time is set, sleep for the specified time.
*
Expand Down Expand Up @@ -1515,7 +1575,9 @@ int main(int argc, char *argv[]) {
}
#endif /* defined(HAS_STRFTIME) */

if (Ffield) {
if (Fjson || Fjsonl) {
/* JSON modes handle their own cycle separation */
} else if (Ffield) {
putchar(LSOF_FID_MARK);

#if defined(HAS_STRFTIME)
Expand Down
Loading
Loading