Skip to content

Commit cf0b102

Browse files
committed
feat: implement JSON process/file printing for -J/-j
1 parent f221123 commit cf0b102

1 file changed

Lines changed: 224 additions & 0 deletions

File tree

src/print.c

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1865,6 +1865,180 @@ int human_readable_size(SZOFFTYPE sz, int print, int col) {
18651865
return strlen(buf);
18661866
}
18671867

1868+
/*
1869+
* json_print_file() - print a single file entry as JSON fields
1870+
*/
1871+
static void json_print_file(struct lsof_context *ctx, int *sep) {
1872+
char buf[128];
1873+
char fd_str[FDLEN];
1874+
char type[TYPEL];
1875+
char *cp;
1876+
unsigned long ul;
1877+
1878+
if (FieldSel[LSOF_FIX_FD].st) {
1879+
if (Lf->fd_type == LSOF_FD_NUMERIC) {
1880+
char num_buf[32];
1881+
snprintf(num_buf, sizeof(num_buf), "%d", Lf->fd_num);
1882+
json_print_str(sep, "fd", num_buf);
1883+
} else {
1884+
fd_to_string(Lf->fd_type, Lf->fd_num, fd_str);
1885+
json_print_str(sep, "fd", fd_str);
1886+
}
1887+
}
1888+
if (FieldSel[LSOF_FIX_ACCESS].st) {
1889+
char a[2] = {access_to_char(Lf->access), '\0'};
1890+
if (a[0] != ' ')
1891+
json_print_str(sep, "access", a);
1892+
}
1893+
if (FieldSel[LSOF_FIX_LOCK].st) {
1894+
char l[2] = {lock_to_char(Lf->lock), '\0'};
1895+
if (l[0] != ' ')
1896+
json_print_str(sep, "lock", l);
1897+
}
1898+
if (FieldSel[LSOF_FIX_TYPE].st && Lf->type != LSOF_FILE_NONE) {
1899+
file_type_to_string(Lf->type, Lf->unknown_file_type_number, type,
1900+
TYPEL);
1901+
json_print_str(sep, "type", type);
1902+
}
1903+
if (FieldSel[LSOF_FIX_DEVCH].st && Lf->dev_ch && Lf->dev_ch[0]) {
1904+
for (cp = Lf->dev_ch; *cp == ' '; cp++)
1905+
;
1906+
if (*cp)
1907+
json_print_str(sep, "device", cp);
1908+
}
1909+
if (FieldSel[LSOF_FIX_DEVN].st && Lf->dev_def) {
1910+
if (sizeof(unsigned long) > sizeof(dev_t))
1911+
ul = (unsigned long)((unsigned int)Lf->dev);
1912+
else
1913+
ul = (unsigned long)Lf->dev;
1914+
snprintf(buf, sizeof(buf), "0x%lx", ul);
1915+
json_print_str(sep, "device_number", buf);
1916+
}
1917+
if (FieldSel[LSOF_FIX_RDEV].st && Lf->rdev_def) {
1918+
if (sizeof(unsigned long) > sizeof(dev_t))
1919+
ul = (unsigned long)((unsigned int)Lf->rdev);
1920+
else
1921+
ul = (unsigned long)Lf->rdev;
1922+
snprintf(buf, sizeof(buf), "0x%lx", ul);
1923+
json_print_str(sep, "raw_device", buf);
1924+
}
1925+
if (FieldSel[LSOF_FIX_SIZE].st && Lf->sz_def) {
1926+
json_print_uint64_str(sep, "size", (uint64_t)Lf->sz);
1927+
}
1928+
if (FieldSel[LSOF_FIX_OFFSET].st && Lf->off_def) {
1929+
json_print_uint64_str(sep, "offset", (uint64_t)Lf->off);
1930+
}
1931+
if (FieldSel[LSOF_FIX_INODE].st && Lf->inp_ty == 1) {
1932+
json_print_uint64_str(sep, "inode", (uint64_t)Lf->inode);
1933+
}
1934+
if (FieldSel[LSOF_FIX_NLINK].st && Lf->nlink_def) {
1935+
json_print_long(sep, "nlink", Lf->nlink);
1936+
}
1937+
if (FieldSel[LSOF_FIX_PROTO].st && Lf->inp_ty == 2) {
1938+
for (cp = Lf->iproto; *cp == ' '; cp++)
1939+
;
1940+
if (*cp)
1941+
json_print_str(sep, "protocol", cp);
1942+
}
1943+
1944+
#if defined(HASFSTRUCT)
1945+
if (FieldSel[LSOF_FIX_FG].st && (Fsv & FSV_FG) && (Lf->fsv & FSV_FG) &&
1946+
(FsvFlagX || Lf->ffg || Lf->pof)) {
1947+
json_print_str(sep, "flags", print_fflags(ctx, Lf->ffg, Lf->pof));
1948+
}
1949+
#endif /* defined(HASFSTRUCT) */
1950+
1951+
if (FieldSel[LSOF_FIX_NAME].st && Lf->nm) {
1952+
json_print_str(sep, "name", Lf->nm);
1953+
}
1954+
1955+
/* TCP/TPI info as nested object */
1956+
if (Lf->lts.type >= 0 && FieldSel[LSOF_FIX_TCPTPI].st) {
1957+
if (*sep)
1958+
putchar(',');
1959+
printf("\"tcp_info\":{");
1960+
int tsep = 0;
1961+
1962+
if ((Ftcptpi & TCPTPI_STATE) && Lf->lts.type == 0) {
1963+
if (!TcpNstates)
1964+
(void)build_IPstates(ctx);
1965+
int s = Lf->lts.state.i;
1966+
if (s >= 0 && s < TcpNstates && TcpSt[s])
1967+
json_print_str(&tsep, "state", TcpSt[s]);
1968+
else {
1969+
snprintf(buf, sizeof(buf), "UNKNOWN_%d", s);
1970+
json_print_str(&tsep, "state", buf);
1971+
}
1972+
}
1973+
#if defined(HASTCPTPIQ)
1974+
if (Ftcptpi & TCPTPI_QUEUES) {
1975+
if (Lf->lts.rqs)
1976+
json_print_ulong(&tsep, "recv_queue", Lf->lts.rq);
1977+
if (Lf->lts.sqs)
1978+
json_print_ulong(&tsep, "send_queue", Lf->lts.sq);
1979+
}
1980+
#endif /* defined(HASTCPTPIQ) */
1981+
#if defined(HASTCPTPIW)
1982+
if (Ftcptpi & TCPTPI_WINDOWS) {
1983+
if (Lf->lts.rws)
1984+
json_print_ulong(&tsep, "read_window", Lf->lts.rw);
1985+
if (Lf->lts.wws)
1986+
json_print_ulong(&tsep, "write_window", Lf->lts.ww);
1987+
}
1988+
#endif /* defined(HASTCPTPIW) */
1989+
putchar('}');
1990+
*sep = 1;
1991+
}
1992+
}
1993+
1994+
/*
1995+
* json_print_proc_fields() - print process-level JSON fields
1996+
*/
1997+
static void json_print_proc_fields(struct lsof_context *ctx, int *sep) {
1998+
int ty;
1999+
char *cp;
2000+
2001+
if (FieldSel[LSOF_FIX_PID].st)
2002+
json_print_int(sep, "pid", Lp->pid);
2003+
2004+
#if defined(HASTASKS)
2005+
if (FieldSel[LSOF_FIX_TID].st && Lp->tid)
2006+
json_print_int(sep, "tid", Lp->tid);
2007+
if (FieldSel[LSOF_FIX_TCMD].st && Lp->tcmd)
2008+
json_print_str(sep, "task_cmd", Lp->tcmd);
2009+
#endif
2010+
2011+
#if defined(HASZONES)
2012+
if (FieldSel[LSOF_FIX_ZONE].st && Fzone && Lp->zn)
2013+
json_print_str(sep, "zone", Lp->zn);
2014+
#endif
2015+
2016+
#if defined(HASSELINUX)
2017+
if (FieldSel[LSOF_FIX_CNTX].st && Fcntx && Lp->cntx && CntxStatus)
2018+
json_print_str(sep, "security_context", Lp->cntx);
2019+
#endif
2020+
2021+
if (FieldSel[LSOF_FIX_PGID].st && Fpgid)
2022+
json_print_int(sep, "pgid", Lp->pgid);
2023+
2024+
#if defined(HASPPID)
2025+
if (FieldSel[LSOF_FIX_PPID].st && Fppid)
2026+
json_print_int(sep, "ppid", Lp->ppid);
2027+
#endif
2028+
2029+
if (FieldSel[LSOF_FIX_CMD].st)
2030+
json_print_str(sep, "command", Lp->cmd ? Lp->cmd : "(unknown)");
2031+
2032+
if (FieldSel[LSOF_FIX_UID].st)
2033+
json_print_int(sep, "uid", (int)Lp->uid);
2034+
2035+
if (FieldSel[LSOF_FIX_LOGIN].st) {
2036+
cp = printuid(ctx, (UID_ARG)Lp->uid, &ty);
2037+
if (ty == 0)
2038+
json_print_str(sep, "login", cp);
2039+
}
2040+
}
2041+
18682042
/*
18692043
* print_proc() - print process
18702044
*/
@@ -1898,6 +2072,56 @@ int print_proc(struct lsof_context *ctx) {
18982072
}
18992073
return (0);
19002074
}
2075+
/*
2076+
* JSON output modes — only emit on PrPass 1 (actual printing pass)
2077+
*/
2078+
if ((Fjson || Fjsonl) && PrPass) {
2079+
for (Lf = Lp->file; Lf; Lf = Lf->next) {
2080+
if (is_file_sel(ctx, Lp, Lf))
2081+
break;
2082+
}
2083+
if (!Lf)
2084+
return (rv);
2085+
rv = 1;
2086+
2087+
if (Fjson) {
2088+
/* Nested JSON: process object with files array */
2089+
if (!Fjson_first_proc)
2090+
putchar(',');
2091+
Fjson_first_proc = 0;
2092+
int sep = 0;
2093+
putchar('{');
2094+
json_print_proc_fields(ctx, &sep);
2095+
if (sep)
2096+
putchar(',');
2097+
printf("\"files\":[");
2098+
int first_file = 1;
2099+
for (Lf = Lp->file; Lf; Lf = Lf->next) {
2100+
if (!is_file_sel(ctx, Lp, Lf))
2101+
continue;
2102+
if (!first_file)
2103+
putchar(',');
2104+
putchar('{');
2105+
int fsep = 0;
2106+
json_print_file(ctx, &fsep);
2107+
putchar('}');
2108+
first_file = 0;
2109+
}
2110+
printf("]}");
2111+
} else {
2112+
/* JSON Lines: one line per file, process fields denormalized */
2113+
for (Lf = Lp->file; Lf; Lf = Lf->next) {
2114+
if (!is_file_sel(ctx, Lp, Lf))
2115+
continue;
2116+
int sep = 0;
2117+
putchar('{');
2118+
json_print_proc_fields(ctx, &sep);
2119+
json_print_file(ctx, &sep);
2120+
printf("}\n");
2121+
}
2122+
}
2123+
return (rv);
2124+
}
19012125
/*
19022126
* If fields have been selected, output the process-only ones, provided
19032127
* that some file has also been selected.

0 commit comments

Comments
 (0)