Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
Comment thread
jiegec marked this conversation as resolved.

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
92 changes: 90 additions & 2 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,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 @@ -556,6 +556,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 +1115,14 @@ int main(int argc, char *argv[]) {
(void)fprintf(stderr, "%s: -x must accompany +d or +D\n", Pn);
err++;
}
if (Fjson && Fjsonl) {
Comment thread
jiegec marked this conversation as resolved.
(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 +1152,58 @@ 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) {
for (i = 0; FieldSel[i].nm; i++) {
Copy link
Copy Markdown
Member

@jiegec jiegec Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of code duplication with existing -F handling, can we deduplicate them?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in bb8bab1. Extracted select_default_fields() static helper in main.c — now called from both the case 'F': default path and the -J/-j default field setup. Removed ~40 lines of duplicated #ifdef logic.

#if !defined(HASPPID)
if (FieldSel[i].id == LSOF_FID_PPID)
continue;
#endif
#if !defined(HASTASKS)
if (FieldSel[i].id == LSOF_FID_TCMD ||
FieldSel[i].id == LSOF_FID_TID)
continue;
#endif
#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
#if defined(HASSELINUX)
if ((FieldSel[i].id == LSOF_FID_CNTX) && !CntxStatus)
continue;
#else
if (FieldSel[i].id == LSOF_FID_CNTX)
continue;
#endif
if (FieldSel[i].id == LSOF_FID_RDEV)
continue;
#if !defined(HASZONES)
if (FieldSel[i].id == LSOF_FID_ZONE)
continue;
#endif
FieldSel[i].st = 1;
if (FieldSel[i].opt && FieldSel[i].ov)
*(FieldSel[i].opt) |= FieldSel[i].ov;
}
}
}

if (DChelp || err || Fhelp || fh || version)
usage(ctx, err ? 1 : 0, fh, version);
/*
Expand Down Expand Up @@ -1312,6 +1390,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 +1549,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 +1601,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