smbd: Ensure we grant owner sid in check_parent_access_fsp()
[samba.git] / source3 / utils / status.c
index 876e0f0f83d5ed33f0fb89fd116428b09f2c24ff..02a5f6dbaba15613e1f635e8429fe96fd8705a48 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
    Unix SMB/CIFS implementation.
    status reporting
    Copyright (C) Andrew Tridgell 1994-1998
  */
 
 #include "includes.h"
+#include "lib/util/server_id.h"
+#include "smbd/globals.h"
 #include "system/filesys.h"
-#include "popt_common.h"
+#include "lib/cmdline/cmdline.h"
 #include "dbwrap/dbwrap.h"
 #include "dbwrap/dbwrap_open.h"
 #include "../libcli/security/security.h"
 #include "session.h"
+#include "locking/share_mode_lock.h"
 #include "locking/proto.h"
 #include "messages.h"
 #include "librpc/gen_ndr/open_files.h"
 #include "smbd/smbd.h"
 #include "librpc/gen_ndr/notify.h"
-#include "lib/conn_tdb.h"
+#include "conn_tdb.h"
 #include "serverid.h"
 #include "status_profile.h"
-#include "smbd/notifyd/notifyd.h"
+#include "status.h"
+#include "status_json.h"
+#include "smbd/notifyd/notifyd_db.h"
+#include "cmdline_contexts.h"
+#include "locking/leases_db.h"
+#include "lib/util/string_wrappers.h"
+#include "lib/param/param.h"
+
+#ifdef HAVE_JANSSON
+#include <jansson.h>
+#include "audit_logging.h" /* various JSON helpers */
+#include "auth/common_auth.h"
+#endif /* HAVE_JANSSON */
 
 #define SMB_MAXPIDS            2048
 static uid_t           Ucrit_uid = 0;               /* added by OH */
@@ -72,10 +87,10 @@ static void Ucrit_addUid(uid_t uid)
 
 static unsigned int Ucrit_checkUid(uid_t uid)
 {
-       if ( !Ucrit_IsActive ) 
+       if ( !Ucrit_IsActive )
                return 1;
 
-       if ( uid == Ucrit_uid ) 
+       if ( uid == Ucrit_uid )
                return 1;
 
        return 0;
@@ -85,11 +100,11 @@ static unsigned int Ucrit_checkPid(struct server_id pid)
 {
        int i;
 
-       if ( !Ucrit_IsActive ) 
+       if ( !Ucrit_IsActive )
                return 1;
 
        for (i=0;i<Ucrit_MaxPid;i++) {
-               if (serverid_equal(&pid, &Ucrit_pid[i])) {
+               if (server_id_equal(&pid, &Ucrit_pid[i])) {
                        return 1;
                }
        }
@@ -103,7 +118,7 @@ static bool Ucrit_addPid( struct server_id pid )
                return True;
 
        if ( Ucrit_MaxPid >= SMB_MAXPIDS ) {
-               d_printf("ERROR: More than %d pids for user %s!\n",
+               fprintf(stderr, "ERROR: More than %d pids for user %s!\n",
                         SMB_MAXPIDS, uidtoname(Ucrit_uid));
 
                return False;
@@ -114,101 +129,266 @@ static bool Ucrit_addPid( struct server_id pid )
        return True;
 }
 
-static int print_share_mode(const struct share_mode_entry *e,
-                           const char *sharepath,
-                           const char *fname,
-                           const char *sname,
-                           void *dummy)
+static int print_share_mode_stdout(struct traverse_state *state,
+                                  const char *pid,
+                                  const char *user_name,
+                                  const char *denymode,
+                                  int access_mask,
+                                  const char *rw,
+                                  const char *oplock,
+                                  const char *servicepath,
+                                  const char *filename,
+                                  const char *timestr)
 {
-       static int count;
+       if (state->first) {
+               d_printf("\nLocked files:\n");
+               d_printf("Pid          User(ID)   DenyMode   Access      R/W        Oplock           SharePath   Name   Time\n");
+               d_printf("--------------------------------------------------------------------------------------------------\n");
 
-       if (do_checks && !is_valid_share_mode_entry(e)) {
-               return 0;
+               state->first = false;
        }
 
-       if (count==0) {
-               d_printf("Locked files:\n");
-               d_printf("Pid          Uid        DenyMode   Access      R/W        Oplock           SharePath   Name   Time\n");
-               d_printf("--------------------------------------------------------------------------------------------------\n");
+       d_printf("%-11s  %-9s  %-10s 0x%-8x  %-10s %-14s   %s   %s   %s",
+                pid, user_name, denymode, access_mask, rw, oplock,
+                servicepath, filename, timestr);
+       return 0;
+}
+
+static int prepare_share_mode(struct traverse_state *state)
+{
+       if (!state->json_output) {
+               /* only print header line if there are open files */
+               state->first = true;
+       } else {
+               add_section_to_json(state, "open_files");
+       }
+       return 0;
+}
+
+static uint32_t map_share_mode_to_deny_mode(
+       uint32_t share_access, uint32_t private_options)
+{
+       switch (share_access & ~FILE_SHARE_DELETE) {
+       case FILE_SHARE_NONE:
+               return DENY_ALL;
+       case FILE_SHARE_READ:
+               return DENY_WRITE;
+       case FILE_SHARE_WRITE:
+               return DENY_READ;
+       case FILE_SHARE_READ|FILE_SHARE_WRITE:
+               return DENY_NONE;
+       }
+       if (private_options & NTCREATEX_FLAG_DENY_DOS) {
+               return DENY_DOS;
+       } else if (private_options & NTCREATEX_FLAG_DENY_FCB) {
+               return DENY_FCB;
+       }
+
+       return (uint32_t)-1;
+}
+
+static int print_share_mode(struct file_id fid,
+                           const struct share_mode_data *d,
+                           const struct share_mode_entry *e,
+                           void *private_data)
+{
+       const char *denymode = NULL;
+       uint denymode_int;
+       const char *oplock = NULL;
+       const char *pid = NULL;
+       const char *rw = NULL;
+       const char *filename = NULL;
+       const char *timestr = NULL;
+       const char *user_str = NULL;
+       uint32_t lstate;
+       struct traverse_state *state = (struct traverse_state *)private_data;
+
+       TALLOC_CTX *tmp_ctx = talloc_stackframe();
+       if (tmp_ctx == NULL) {
+               return -1;
+       }
+
+       if (do_checks && !is_valid_share_mode_entry(e)) {
+               TALLOC_FREE(tmp_ctx);
+               return 0;
        }
-       count++;
 
        if (do_checks && !serverid_exists(&e->pid)) {
                /* the process for this entry does not exist any more */
+               TALLOC_FREE(tmp_ctx);
                return 0;
        }
 
        if (Ucrit_checkPid(e->pid)) {
                struct server_id_buf tmp;
-               d_printf("%-11s  ", server_id_str_buf(e->pid, &tmp));
-               d_printf("%-9u  ", (unsigned int)e->uid);
-               switch (map_share_mode_to_deny_mode(e->share_access,
-                                                   e->private_options)) {
-                       case DENY_NONE: d_printf("DENY_NONE  "); break;
-                       case DENY_ALL:  d_printf("DENY_ALL   "); break;
-                       case DENY_DOS:  d_printf("DENY_DOS   "); break;
-                       case DENY_READ: d_printf("DENY_READ  "); break;
-                       case DENY_WRITE:printf("DENY_WRITE "); break;
-                       case DENY_FCB:  d_printf("DENY_FCB "); break;
+               pid = server_id_str_buf(e->pid, &tmp);
+               if (state->resolve_uids) {
+                       user_str = talloc_asprintf(tmp_ctx, "%s", uidtoname(e->uid));
+               } else {
+                       user_str = talloc_asprintf(tmp_ctx, "%u", (unsigned int)e->uid);
+               }
+               if (user_str == NULL) {
+                       TALLOC_FREE(tmp_ctx);
+                       return -1;
+               }
+
+               denymode_int = map_share_mode_to_deny_mode(e->share_access,
+                                                          e->private_options);
+               switch (denymode_int) {
+                       case DENY_NONE:
+                               denymode = "DENY_NONE";
+                               break;
+                       case DENY_ALL:
+                               denymode = "DENY_ALL";
+                               break;
+                       case DENY_DOS:
+                               denymode = "DENY_DOS";
+                               break;
+                       case DENY_READ:
+                               denymode = "DENY_READ";
+                               break;
+                       case DENY_WRITE:
+                               denymode = "DENY_WRITE";
+                               break;
+                       case DENY_FCB:
+                               denymode = "DENY_FCB";
+                               break;
                        default: {
-                               d_printf("unknown-please report ! "
-                                        "e->share_access = 0x%x, "
-                                        "e->private_options = 0x%x\n",
-                                        (unsigned int)e->share_access,
-                                        (unsigned int)e->private_options );
+                               denymode = talloc_asprintf(tmp_ctx,
+                                                          "UNKNOWN(0x%08x)",
+                                                          denymode_int);
+                               if (denymode == NULL) {
+                                       TALLOC_FREE(tmp_ctx);
+                                       return -1;
+                               }
+                               fprintf(stderr,
+                                       "unknown-please report ! "
+                                       "e->share_access = 0x%x, "
+                                       "e->private_options = 0x%x\n",
+                                       (unsigned int)e->share_access,
+                                       (unsigned int)e->private_options);
                                break;
                        }
                }
-               d_printf("0x%-8x  ",(unsigned int)e->access_mask);
+               filename = talloc_asprintf(tmp_ctx,
+                                          "%s%s",
+                                          d->base_name,
+                                          (d->stream_name != NULL) ? d->stream_name : "");
+               if (filename == NULL) {
+                       TALLOC_FREE(tmp_ctx);
+                       return -1;
+               }
                if ((e->access_mask & (FILE_READ_DATA|FILE_WRITE_DATA))==
                                (FILE_READ_DATA|FILE_WRITE_DATA)) {
-                       d_printf("RDWR       ");
+                       rw = "RDWR";
                } else if (e->access_mask & FILE_WRITE_DATA) {
-                       d_printf("WRONLY     ");
+                       rw = "WRONLY";
                } else {
-                       d_printf("RDONLY     ");
+                       rw = "RDONLY";
                }
 
-               if((e->op_type & (EXCLUSIVE_OPLOCK|BATCH_OPLOCK)) == 
-                                       (EXCLUSIVE_OPLOCK|BATCH_OPLOCK)) {
-                       d_printf("EXCLUSIVE+BATCH ");
+               if (e->op_type & BATCH_OPLOCK) {
+                       oplock = "BATCH";
                } else if (e->op_type & EXCLUSIVE_OPLOCK) {
-                       d_printf("EXCLUSIVE       ");
-               } else if (e->op_type & BATCH_OPLOCK) {
-                       d_printf("BATCH           ");
+                       oplock = "EXCLUSIVE";
                } else if (e->op_type & LEVEL_II_OPLOCK) {
-                       d_printf("LEVEL_II        ");
+                       oplock = "LEVEL_II";
                } else if (e->op_type == LEASE_OPLOCK) {
-                       uint32_t lstate = e->lease->current_state;
-                       d_printf("LEASE(%s%s%s)%s%s%s      ",
-                                (lstate & SMB2_LEASE_READ)?"R":"",
-                                (lstate & SMB2_LEASE_WRITE)?"W":"",
-                                (lstate & SMB2_LEASE_HANDLE)?"H":"",
-                                (lstate & SMB2_LEASE_READ)?"":" ",
-                                (lstate & SMB2_LEASE_WRITE)?"":" ",
-                                (lstate & SMB2_LEASE_HANDLE)?"":" ");
+                       NTSTATUS status;
+
+                       status = leases_db_get(
+                               &e->client_guid,
+                               &e->lease_key,
+                               &d->id,
+                               &lstate, /* current_state */
+                               NULL, /* breaking */
+                               NULL, /* breaking_to_requested */
+                               NULL, /* breaking_to_required */
+                               NULL, /* lease_version */
+                               NULL); /* epoch */
+
+                       if (NT_STATUS_IS_OK(status)) {
+                               oplock = talloc_asprintf(tmp_ctx, "LEASE(%s%s%s)%s%s%s",
+                                                (lstate & SMB2_LEASE_READ)?"R":"",
+                                                (lstate & SMB2_LEASE_WRITE)?"W":"",
+                                                (lstate & SMB2_LEASE_HANDLE)?"H":"",
+                                                (lstate & SMB2_LEASE_READ)?"":" ",
+                                                (lstate & SMB2_LEASE_WRITE)?"":" ",
+                                                (lstate & SMB2_LEASE_HANDLE)?"":" ");
+                       } else {
+                               oplock = "LEASE STATE UNKNOWN";
+                       }
                } else {
-                       d_printf("NONE            ");
+                       oplock = "NONE";
                }
 
-               d_printf(" %s   %s%s   %s",
-                        sharepath, fname,
-                        sname ? sname : "",
-                        time_to_asc((time_t)e->time.tv_sec));
+               timestr = time_to_asc((time_t)e->time.tv_sec);
+
+               if (!state->json_output) {
+                       print_share_mode_stdout(state,
+                                               pid,
+                                               user_str,
+                                               denymode,
+                                               (unsigned int)e->access_mask,
+                                               rw,
+                                               oplock,
+                                               d->servicepath,
+                                               filename,
+                                               timestr);
+               } else {
+                       print_share_mode_json(state,
+                                             d,
+                                             e,
+                                             fid,
+                                             user_str,
+                                             oplock,
+                                             lstate,
+                                             filename);
+               }
        }
+       TALLOC_FREE(tmp_ctx);
+       return 0;
+}
 
+static void print_brl_stdout(struct traverse_state *state,
+                            char *pid,
+                            char *id,
+                            const char *desc,
+                            intmax_t start,
+                            intmax_t size,
+                            const char *sharepath,
+                            char *fname)
+{
+       if (state->first) {
+               d_printf("Byte range locks:\n");
+               d_printf("Pid        dev:inode       R/W  start     size      SharePath               Name\n");
+               d_printf("--------------------------------------------------------------------------------\n");
+
+               state->first = false;
+       }
+       d_printf("%-10s %-15s %-4s %-9jd %-9jd %-24s %-24s\n",
+                pid, id, desc, start, size, sharepath, fname);
+}
+
+static int prepare_brl(struct traverse_state *state)
+{
+       if (!state->json_output) {
+               /* only print header line if there are locked files */
+               state->first = true;
+       } else {
+               add_section_to_json(state, "byte_range_locks");
+       }
        return 0;
 }
 
 static void print_brl(struct file_id id,
-                       struct server_id pid, 
+                       struct server_id pid,
                        enum brl_type lock_type,
                        enum brl_flavour lock_flav,
                        br_off start,
                        br_off size,
                        void *private_data)
 {
-       static int count;
        unsigned int i;
        static const struct {
                enum brl_type lock_type;
@@ -216,8 +396,6 @@ static void print_brl(struct file_id id,
        } lock_types[] = {
                { READ_LOCK, "R" },
                { WRITE_LOCK, "W" },
-               { PENDING_READ_LOCK, "PR" },
-               { PENDING_WRITE_LOCK, "PW" },
                { UNLOCK_LOCK, "U" }
        };
        const char *desc="X";
@@ -225,24 +403,13 @@ static void print_brl(struct file_id id,
        char *fname = NULL;
        struct share_mode_lock *share_mode;
        struct server_id_buf tmp;
-
-       if (count==0) {
-               d_printf("Byte range locks:\n");
-               d_printf("Pid        dev:inode       R/W  start     size      SharePath               Name\n");
-               d_printf("--------------------------------------------------------------------------------\n");
-       }
-       count++;
+       struct file_id_buf ftmp;
+       struct traverse_state *state = (struct traverse_state *)private_data;
 
        share_mode = fetch_share_mode_unlocked(NULL, id);
        if (share_mode) {
-               bool has_stream = share_mode->data->stream_name != NULL;
-
-               fname = talloc_asprintf(NULL, "%s%s%s",
-                                       share_mode->data->base_name,
-                                       has_stream ? ":" : "",
-                                       has_stream ?
-                                       share_mode->data->stream_name :
-                                       "");
+               fname = share_mode_filename(NULL, share_mode);
+               sharepath = share_mode_servicepath(share_mode);
        } else {
                fname = talloc_strdup(NULL, "");
                if (fname == NULL) {
@@ -256,116 +423,641 @@ static void print_brl(struct file_id id,
                }
        }
 
-       d_printf("%-10s %-15s %-4s %-9jd %-9jd %-24s %-24s\n",
-                server_id_str_buf(pid, &tmp), file_id_string_tos(&id),
-                desc,
-                (intmax_t)start, (intmax_t)size,
-                sharepath, fname);
+       if (!state->json_output) {
+               print_brl_stdout(state,
+                                server_id_str_buf(pid, &tmp),
+                                file_id_str_buf(id, &ftmp),
+                                desc,
+                                (intmax_t)start,
+                                (intmax_t)size,
+                                sharepath,
+                                fname);
+       } else {
+               print_brl_json(state,
+                              pid,
+                              id,
+                              desc,
+                              lock_flav,
+                              (intmax_t)start,
+                              (intmax_t)size,
+                              sharepath,
+                              fname);
+
+       }
 
        TALLOC_FREE(fname);
        TALLOC_FREE(share_mode);
 }
 
-static int traverse_connections(const struct connections_key *key,
-                               const struct connections_data *crec,
-                               void *state)
+static const char *session_dialect_str(uint16_t dialect)
+{
+       static fstring unknown_dialect;
+
+       switch(dialect){
+       case SMB2_DIALECT_REVISION_000:
+               return "NT1";
+       case SMB2_DIALECT_REVISION_202:
+               return "SMB2_02";
+       case SMB2_DIALECT_REVISION_210:
+               return "SMB2_10";
+       case SMB2_DIALECT_REVISION_222:
+               return "SMB2_22";
+       case SMB2_DIALECT_REVISION_224:
+               return "SMB2_24";
+       case SMB3_DIALECT_REVISION_300:
+               return "SMB3_00";
+       case SMB3_DIALECT_REVISION_302:
+               return "SMB3_02";
+       case SMB3_DIALECT_REVISION_310:
+               return "SMB3_10";
+       case SMB3_DIALECT_REVISION_311:
+               return "SMB3_11";
+       }
+
+       fstr_sprintf(unknown_dialect, "Unknown (0x%04x)", dialect);
+       return unknown_dialect;
+}
+
+static int traverse_connections_stdout(struct traverse_state *state,
+                                      const char *servicename,
+                                      char *server_id,
+                                      const char *machine,
+                                      const char *timestr,
+                                      const char *encryption_cipher,
+                                      enum crypto_degree encryption_degree,
+                                      const char *signing_cipher,
+                                      enum crypto_degree signing_degree)
+{
+       fstring encryption;
+       fstring signing;
+
+       if (encryption_degree == CRYPTO_DEGREE_FULL) {
+               fstr_sprintf(encryption, "%s", encryption_cipher);
+       } else if (encryption_degree == CRYPTO_DEGREE_ANONYMOUS) {
+               fstr_sprintf(encryption, "anonymous(%s)", encryption_cipher);
+       } else if (encryption_degree == CRYPTO_DEGREE_PARTIAL) {
+               fstr_sprintf(encryption, "partial(%s)", encryption_cipher);
+       } else {
+               fstr_sprintf(encryption, "-");
+       }
+       if (signing_degree == CRYPTO_DEGREE_FULL) {
+               fstr_sprintf(signing, "%s", signing_cipher);
+       } else if (signing_degree == CRYPTO_DEGREE_ANONYMOUS) {
+               fstr_sprintf(signing, "anonymous(%s)", signing_cipher);
+       } else if (signing_degree == CRYPTO_DEGREE_PARTIAL) {
+               fstr_sprintf(signing, "partial(%s)", signing_cipher);
+       } else {
+               fstr_sprintf(signing, "-");
+       }
+
+       d_printf("%-12s %-7s %-13s %-32s %-12s %-12s\n",
+                servicename, server_id, machine, timestr, encryption, signing);
+
+       return 0;
+}
+
+static int prepare_connections(struct traverse_state *state)
+{
+       if (!state->json_output) {
+               /* always print header line */
+               d_printf("\n%-12s %-7s %-13s %-32s %-12s %-12s\n", "Service", "pid", "Machine", "Connected at", "Encryption", "Signing");
+               d_printf("---------------------------------------------------------------------------------------------\n");
+       } else {
+               add_section_to_json(state, "tcons");
+       }
+       return 0;
+}
+
+static int traverse_connections(const struct connections_data *crec,
+                               void *private_data)
 {
        struct server_id_buf tmp;
+       char *timestr = NULL;
+       int result = 0;
+       const char *encryption = "-";
+       enum crypto_degree encryption_degree = CRYPTO_DEGREE_NONE;
+       const char *signing = "-";
+       enum crypto_degree signing_degree = CRYPTO_DEGREE_NONE;
+       struct traverse_state *state = (struct traverse_state *)private_data;
+
+       TALLOC_CTX *tmp_ctx = talloc_stackframe();
+       if (tmp_ctx == NULL) {
+               return -1;
+       }
 
-       if (crec->cnum == TID_FIELD_INVALID)
+       if (crec->cnum == TID_FIELD_INVALID) {
+               TALLOC_FREE(tmp_ctx);
                return 0;
+       }
 
        if (do_checks &&
            (!process_exists(crec->pid) || !Ucrit_checkUid(crec->uid))) {
+               TALLOC_FREE(tmp_ctx);
                return 0;
        }
 
-       d_printf("%-10s   %s   %-12s  %s",
-                crec->servicename, server_id_str_buf(crec->pid, &tmp),
-                crec->machine,
-                time_to_asc(crec->start));
+       timestr = timestring(tmp_ctx, nt_time_to_unix(crec->start));
+       if (timestr == NULL) {
+               TALLOC_FREE(tmp_ctx);
+               return -1;
+       }
+
+       if (smbXsrv_is_encrypted(crec->encryption_flags) ||
+           smbXsrv_is_partially_encrypted(crec->encryption_flags))
+       {
+               switch (crec->cipher) {
+               case SMB_ENCRYPTION_GSSAPI:
+                       encryption = "GSSAPI";
+                       break;
+               case SMB2_ENCRYPTION_AES128_CCM:
+                       encryption = "AES-128-CCM";
+                       break;
+               case SMB2_ENCRYPTION_AES128_GCM:
+                       encryption = "AES-128-GCM";
+                       break;
+               case SMB2_ENCRYPTION_AES256_CCM:
+                       encryption = "AES-256-CCM";
+                       break;
+               case SMB2_ENCRYPTION_AES256_GCM:
+                       encryption = "AES-256-GCM";
+                       break;
+               default:
+                       encryption = "???";
+                       break;
+               }
+               if (smbXsrv_is_encrypted(crec->encryption_flags)) {
+                       encryption_degree = CRYPTO_DEGREE_FULL;
+               } else if (smbXsrv_is_partially_encrypted(crec->encryption_flags)) {
+                       encryption_degree = CRYPTO_DEGREE_PARTIAL;
+               }
+               if (encryption_degree != CRYPTO_DEGREE_NONE &&
+                   !crec->authenticated)
+               {
+                       encryption_degree = CRYPTO_DEGREE_ANONYMOUS;
+               }
+       }
+
+       if (smbXsrv_is_signed(crec->signing_flags) ||
+           smbXsrv_is_partially_signed(crec->signing_flags))
+       {
+               switch (crec->signing) {
+               case SMB2_SIGNING_MD5_SMB1:
+                       signing = "HMAC-MD5";
+                       break;
+               case SMB2_SIGNING_HMAC_SHA256:
+                       signing = "HMAC-SHA256";
+                       break;
+               case SMB2_SIGNING_AES128_CMAC:
+                       signing = "AES-128-CMAC";
+                       break;
+               case SMB2_SIGNING_AES128_GMAC:
+                       signing = "AES-128-GMAC";
+                       break;
+               default:
+                       signing = "???";
+                       break;
+               }
+               if (smbXsrv_is_signed(crec->signing_flags)) {
+                       signing_degree = CRYPTO_DEGREE_FULL;
+               } else if (smbXsrv_is_partially_signed(crec->signing_flags)) {
+                       signing_degree = CRYPTO_DEGREE_PARTIAL;
+               }
+               if (signing_degree != CRYPTO_DEGREE_NONE &&
+                   !crec->authenticated)
+               {
+                       signing_degree = CRYPTO_DEGREE_ANONYMOUS;
+               }
+       }
+
+       if (!state->json_output) {
+               result = traverse_connections_stdout(state,
+                                                    crec->servicename,
+                                                    server_id_str_buf(crec->pid, &tmp),
+                                                    crec->machine,
+                                                    timestr,
+                                                    encryption,
+                                                    encryption_degree,
+                                                    signing,
+                                                    signing_degree);
+       } else {
+               result = traverse_connections_json(state,
+                                                  crec,
+                                                  encryption,
+                                                  encryption_degree,
+                                                  signing,
+                                                  signing_degree);
+       }
+
+       TALLOC_FREE(timestr);
+       TALLOC_FREE(tmp_ctx);
+
+       return result;
+}
+
+static int traverse_sessionid_stdout(struct traverse_state *state,
+                                    char *server_id,
+                                    char *uid_gid_str,
+                                    char *machine_hostname,
+                                    const char *dialect,
+                                    const char *encryption_cipher,
+                                    enum crypto_degree encryption_degree,
+                                    const char *signing_cipher,
+                                    enum crypto_degree signing_degree)
+{
+       fstring encryption;
+       fstring signing;
+
+       if (encryption_degree == CRYPTO_DEGREE_FULL) {
+               fstr_sprintf(encryption, "%s", encryption_cipher);
+       } else if (encryption_degree == CRYPTO_DEGREE_ANONYMOUS) {
+               fstr_sprintf(encryption, "anonymous(%s)", encryption_cipher);
+       } else if (encryption_degree == CRYPTO_DEGREE_PARTIAL) {
+               fstr_sprintf(encryption, "partial(%s)", encryption_cipher);
+       } else {
+               fstr_sprintf(encryption, "-");
+       }
+       if (signing_degree == CRYPTO_DEGREE_FULL) {
+               fstr_sprintf(signing, "%s", signing_cipher);
+       } else if (signing_degree == CRYPTO_DEGREE_ANONYMOUS) {
+               fstr_sprintf(signing, "anonymous(%s)", signing_cipher);
+       } else if (signing_degree == CRYPTO_DEGREE_PARTIAL) {
+               fstr_sprintf(signing, "partial(%s)", signing_cipher);
+       } else {
+               fstr_sprintf(signing, "-");
+       }
+
+       d_printf("%-7s %-25s %-41s %-17s %-20s %-21s\n",
+                server_id, uid_gid_str, machine_hostname, dialect, encryption,
+                signing);
 
        return 0;
 }
 
+static int prepare_sessionid(struct traverse_state *state)
+{
+       if (!state->json_output) {
+               /* always print header line */
+               d_printf("\nSamba version %s\n",samba_version_string());
+               d_printf("%-7s %-12s %-12s %-41s %-17s %-20s %-21s\n", "PID", "Username", "Group", "Machine", "Protocol Version", "Encryption", "Signing");
+               d_printf("----------------------------------------------------------------------------------------------------------------------------------------\n");
+       } else {
+               add_section_to_json(state, "sessions");
+       }
+       return 0;
+
+}
+
 static int traverse_sessionid(const char *key, struct sessionid *session,
                              void *private_data)
 {
-       fstring uid_str, gid_str;
+       fstring uid_gid_str;
+       fstring uid_str;
+       fstring gid_str;
        struct server_id_buf tmp;
+       char *machine_hostname = NULL;
+       int result = 0;
+       const char *encryption = "-";
+       enum crypto_degree encryption_degree = CRYPTO_DEGREE_NONE;
+       const char *signing = "-";
+       enum crypto_degree signing_degree = CRYPTO_DEGREE_NONE;
+       struct traverse_state *state = (struct traverse_state *)private_data;
+
+       TALLOC_CTX *tmp_ctx = talloc_stackframe();
+       if (tmp_ctx == NULL) {
+               return -1;
+       }
 
        if (do_checks &&
            (!process_exists(session->pid) ||
             !Ucrit_checkUid(session->uid))) {
+               TALLOC_FREE(tmp_ctx);
                return 0;
        }
 
        Ucrit_addPid(session->pid);
 
-       fstrcpy(uid_str, "-1");
-
-       if (session->uid != -1) {
-               if (numeric_only) {
-                       fstr_sprintf(uid_str, "%u", (unsigned int)session->uid);
+       if (numeric_only) {
+               fstr_sprintf(gid_str, "%u", (unsigned int)session->gid);
+               fstr_sprintf(uid_str, "%u", (unsigned int)session->uid);
+               fstr_sprintf(uid_gid_str, "%-12u %-12u",
+                            (unsigned int)session->uid,
+                            (unsigned int)session->gid);
+       } else {
+               if (session->uid == -1 && session->gid == -1) {
+                       /*
+                        * The session is not fully authenticated yet.
+                        */
+                       fstrcpy(uid_gid_str, "(auth in progress)");
+                       fstrcpy(gid_str, "(auth in progress)");
+                       fstrcpy(uid_str, "(auth in progress)");
                } else {
-                       fstrcpy(uid_str, uidtoname(session->uid));
+                       /*
+                        * In theory it should not happen that one of
+                        * session->uid and session->gid is valid (ie != -1)
+                        * while the other is not (ie = -1), so we a check for
+                        * that case that bails out would be reasonable.
+                        */
+                       const char *uid_name = "-1";
+                       const char *gid_name = "-1";
+
+                       if (session->uid != -1) {
+                               uid_name = uidtoname(session->uid);
+                               if (uid_name == NULL) {
+                                       TALLOC_FREE(tmp_ctx);
+                                       return -1;
+                               }
+                       }
+                       if (session->gid != -1) {
+                               gid_name = gidtoname(session->gid);
+                               if (gid_name == NULL) {
+                                       TALLOC_FREE(tmp_ctx);
+                                       return -1;
+                               }
+                       }
+                       fstr_sprintf(gid_str, "%s", gid_name);
+                       fstr_sprintf(uid_str, "%s", uid_name);
+                       fstr_sprintf(uid_gid_str, "%-12s %-12s",
+                                    uid_name, gid_name);
                }
        }
 
-       fstrcpy(gid_str, "-1");
+       machine_hostname = talloc_asprintf(tmp_ctx, "%s (%s)",
+                                          session->remote_machine,
+                                          session->hostname);
+       if (machine_hostname == NULL) {
+               TALLOC_FREE(tmp_ctx);
+               return -1;
+       }
 
-       if (session->gid != -1) {
-               if (numeric_only) {
-                       fstr_sprintf(gid_str, "%u", (unsigned int)session->gid);
-               } else {
-                       fstrcpy(gid_str, gidtoname(session->gid));
+       if (smbXsrv_is_encrypted(session->encryption_flags) ||
+                       smbXsrv_is_partially_encrypted(session->encryption_flags)) {
+               switch (session->cipher) {
+               case SMB2_ENCRYPTION_AES128_CCM:
+                       encryption = "AES-128-CCM";
+                       break;
+               case SMB2_ENCRYPTION_AES128_GCM:
+                       encryption = "AES-128-GCM";
+                       break;
+               case SMB2_ENCRYPTION_AES256_CCM:
+                       encryption = "AES-256-CCM";
+                       break;
+               case SMB2_ENCRYPTION_AES256_GCM:
+                       encryption = "AES-256-GCM";
+                       break;
+               default:
+                       encryption = "???";
+                       result = -1;
+                       break;
+               }
+               if (smbXsrv_is_encrypted(session->encryption_flags)) {
+                       encryption_degree = CRYPTO_DEGREE_FULL;
+               } else if (smbXsrv_is_partially_encrypted(session->encryption_flags)) {
+                       encryption_degree = CRYPTO_DEGREE_PARTIAL;
+               }
+               if (encryption_degree != CRYPTO_DEGREE_NONE &&
+                   !session->authenticated)
+               {
+                       encryption_degree = CRYPTO_DEGREE_ANONYMOUS;
                }
        }
 
-       d_printf("%-7s   %-12s  %-12s  %-12s (%s) %-12s\n",
-                server_id_str_buf(session->pid, &tmp),
-                uid_str, gid_str,
-                session->remote_machine, session->hostname, session->protocol_ver);
+       if (smbXsrv_is_signed(session->signing_flags) ||
+                       smbXsrv_is_partially_signed(session->signing_flags)) {
+               switch (session->signing) {
+               case SMB2_SIGNING_MD5_SMB1:
+                       signing = "HMAC-MD5";
+                       break;
+               case SMB2_SIGNING_HMAC_SHA256:
+                       signing = "HMAC-SHA256";
+                       break;
+               case SMB2_SIGNING_AES128_CMAC:
+                       signing = "AES-128-CMAC";
+                       break;
+               case SMB2_SIGNING_AES128_GMAC:
+                       signing = "AES-128-GMAC";
+                       break;
+               default:
+                       signing = "???";
+                       result = -1;
+                       break;
+               }
+               if (smbXsrv_is_signed(session->signing_flags)) {
+                       signing_degree = CRYPTO_DEGREE_FULL;
+               } else if (smbXsrv_is_partially_signed(session->signing_flags)) {
+                       signing_degree = CRYPTO_DEGREE_PARTIAL;
+               }
+               if (signing_degree != CRYPTO_DEGREE_NONE &&
+                   !session->authenticated)
+               {
+                       signing_degree = CRYPTO_DEGREE_ANONYMOUS;
+               }
+       }
 
-       return 0;
+
+       if (!state->json_output) {
+               traverse_sessionid_stdout(state,
+                        server_id_str_buf(session->pid, &tmp),
+                        uid_gid_str,
+                        machine_hostname,
+                        session_dialect_str(session->connection_dialect),
+                        encryption,
+                        encryption_degree,
+                        signing,
+                        signing_degree);
+       } else {
+               result = traverse_sessionid_json(state,
+                                                session,
+                                                uid_str,
+                                                gid_str,
+                                                encryption,
+                                                encryption_degree,
+                                                signing,
+                                                signing_degree,
+                                                session_dialect_str(session->connection_dialect));
+       }
+
+       TALLOC_FREE(machine_hostname);
+       TALLOC_FREE(tmp_ctx);
+
+       return result;
+}
+
+
+static bool print_notify_rec_stdout(struct traverse_state *state,
+                                   const char *path,
+                                   char *server_id_str,
+                                   unsigned filter,
+                                   unsigned subdir_filter)
+{
+       d_printf("%s\\%s\\%x\\%x\n", path, server_id_str,
+                filter, subdir_filter);
+
+       return true;
 }
 
+static int prepare_notify(struct traverse_state *state)
+{
+       if (!state->json_output) {
+               /* don't print header line */
+       } else {
+               add_section_to_json(state, "notifies");
+       }
+       return 0;
+}
 
 static bool print_notify_rec(const char *path, struct server_id server,
                             const struct notify_instance *instance,
                             void *private_data)
 {
        struct server_id_buf idbuf;
+       struct traverse_state *state = (struct traverse_state *)private_data;
+       bool result;
 
-       d_printf("%s\\%s\\%x\\%x\n", path, server_id_str_buf(server, &idbuf),
-                (unsigned)instance->filter,
-                (unsigned)instance->subdir_filter);
+       if (!state->json_output) {
+               result = print_notify_rec_stdout(state,
+                                                path,
+                                                server_id_str_buf(server, &idbuf),
+                                                (unsigned)instance->filter,
+                                                (unsigned)instance->subdir_filter);
 
-       return true;
+       } else {
+               result = print_notify_rec_json(state,
+                                              instance,
+                                              server,
+                                              path);
+       }
+
+       return result;
 }
 
+enum {
+       OPT_RESOLVE_UIDS = 1000,
+};
+
 int main(int argc, const char *argv[])
 {
        int c;
        int profile_only = 0;
        bool show_processes, show_locks, show_shares;
        bool show_notify = false;
-       poptContext pc;
+       poptContext pc = NULL;
+       struct traverse_state state = {0};
        struct poptOption long_options[] = {
                POPT_AUTOHELP
-               {"processes",   'p', POPT_ARG_NONE,     NULL, 'p', "Show processes only" },
-               {"verbose",     'v', POPT_ARG_NONE,     NULL, 'v', "Be verbose" },
-               {"locks",       'L', POPT_ARG_NONE,     NULL, 'L', "Show locks only" },
-               {"shares",      'S', POPT_ARG_NONE,     NULL, 'S', "Show shares only" },
-               {"notify",      'N', POPT_ARG_NONE,     NULL, 'N', "Show notifies" },
-               {"user",        'u', POPT_ARG_STRING,   &username, 'u', "Switch to user" },
-               {"brief",       'b', POPT_ARG_NONE,     NULL, 'b', "Be brief" },
-               {"profile",     'P', POPT_ARG_NONE, NULL, 'P', "Do profiling" },
-               {"profile-rates", 'R', POPT_ARG_NONE, NULL, 'R', "Show call rates" },
-               {"byterange",   'B', POPT_ARG_NONE,     NULL, 'B', "Include byte range locks"},
-               {"numeric",     'n', POPT_ARG_NONE,     NULL, 'n', "Numeric uid/gid"},
-               {"fast",        'f', POPT_ARG_NONE,     NULL, 'f', "Skip checks if processes still exist"},
+               {
+                       .longName   = "processes",
+                       .shortName  = 'p',
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = 'p',
+                       .descrip    = "Show processes only",
+               },
+               {
+                       .longName   = "verbose",
+                       .shortName  = 'v',
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = 'v',
+                       .descrip    = "Be verbose",
+               },
+               {
+                       .longName   = "locks",
+                       .shortName  = 'L',
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = 'L',
+                       .descrip    = "Show locks only",
+               },
+               {
+                       .longName   = "shares",
+                       .shortName  = 'S',
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = 'S',
+                       .descrip    = "Show shares only",
+               },
+               {
+                       .longName   = "notify",
+                       .shortName  = 'N',
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = 'N',
+                       .descrip    = "Show notifies",
+               },
+               {
+                       .longName   = "user",
+                       .shortName  = 'u',
+                       .argInfo    = POPT_ARG_STRING,
+                       .arg        = &username,
+                       .val        = 'u',
+                       .descrip    = "Switch to user",
+               },
+               {
+                       .longName   = "brief",
+                       .shortName  = 'b',
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = 'b',
+                       .descrip    = "Be brief",
+               },
+               {
+                       .longName   = "profile",
+                       .shortName  =     'P',
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = 'P',
+                       .descrip    = "Do profiling",
+               },
+               {
+                       .longName   = "profile-rates",
+                       .shortName  = 'R',
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = 'R',
+                       .descrip    = "Show call rates",
+               },
+               {
+                       .longName   = "byterange",
+                       .shortName  = 'B',
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = 'B',
+                       .descrip    = "Include byte range locks"
+               },
+               {
+                       .longName   = "numeric",
+                       .shortName  = 'n',
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = 'n',
+                       .descrip    = "Numeric uid/gid"
+               },
+               {
+                       .longName   = "json",
+                       .shortName  = 'j',
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = 'j',
+                       .descrip    = "JSON output"
+               },
+               {
+                       .longName   = "fast",
+                       .shortName  = 'f',
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = 'f',
+                       .descrip    = "Skip checks if processes still exist"
+               },
+               {
+                       .longName   = "resolve-uids",
+                       .shortName  = 0,
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = OPT_RESOLVE_UIDS,
+                       .descrip    = "Try to resolve UIDs to usernames"
+               },
                POPT_COMMON_SAMBA
+               POPT_COMMON_VERSION
                POPT_TABLEEND
        };
        TALLOC_CTX *frame = talloc_stackframe();
@@ -373,29 +1065,36 @@ int main(int argc, const char *argv[])
        struct messaging_context *msg_ctx = NULL;
        char *db_path;
        bool ok;
+       struct loadparm_context *lp_ctx = NULL;
 
-       sec_init();
-       smb_init_locale();
+       state.first = true;
+       state.json_output = false;
+       state.resolve_uids = false;
 
-       setup_logging(argv[0], DEBUG_STDERR);
-       lp_set_cmdline("log level", "0");
+       smb_init_locale();
 
-       if (getuid() != geteuid()) {
-               d_printf("smbstatus should not be run setuid\n");
-               ret = 1;
-               goto done;
+       ok = samba_cmdline_init(frame,
+                               SAMBA_CMDLINE_CONFIG_CLIENT,
+                               false /* require_smbconf */);
+       if (!ok) {
+               DBG_ERR("Failed to init cmdline parser!\n");
+               TALLOC_FREE(frame);
+               exit(1);
        }
-
-       if (getuid() != 0) {
-               d_printf("smbstatus only works as root!\n");
-               ret = 1;
-               goto done;
+       lp_ctx = samba_cmdline_get_lp_ctx();
+       lpcfg_set_cmdline(lp_ctx, "log level", "0");
+
+       pc = samba_popt_get_context(getprogname(),
+                                   argc,
+                                   argv,
+                                   long_options,
+                                   POPT_CONTEXT_KEEP_FIRST);
+       if (pc == NULL) {
+               DBG_ERR("Failed to setup popt context!\n");
+               TALLOC_FREE(frame);
+               exit(1);
        }
 
-
-       pc = poptGetContext(NULL, argc, argv, long_options,
-                           POPT_CONTEXT_KEEP_FIRST);
-
        while ((c = poptGetNextOpt(pc)) != -1) {
                switch (c) {
                case 'p':
@@ -429,12 +1128,49 @@ int main(int argc, const char *argv[])
                case 'n':
                        numeric_only = true;
                        break;
+               case 'j':
+                       state.json_output = true;
+                       break;
                case 'f':
                        do_checks = false;
                        break;
+               case OPT_RESOLVE_UIDS:
+                       state.resolve_uids = true;
+                       break;
+               case POPT_ERROR_BADOPT:
+                       fprintf(stderr, "\nInvalid option %s: %s\n\n",
+                               poptBadOption(pc, 0), poptStrerror(c));
+                       poptPrintUsage(pc, stderr, 0);
+                       exit(1);
                }
        }
 
+       sec_init();
+
+#ifdef HAVE_JANSSON
+       state.root_json = json_new_object();
+       if (!json_is_invalid(&state.root_json)) {
+               add_general_information_to_json(&state);
+       }
+#else /* HAVE_JANSSON */
+       if (state.json_output) {
+               fprintf(stderr, "JSON support not available, please install lib Jansson\n");
+               goto done;
+       }
+#endif /* HAVE_JANSSON */
+
+       if (getuid() != geteuid()) {
+               fprintf(stderr, "smbstatus should not be run setuid\n");
+               ret = 1;
+               goto done;
+       }
+
+       if (getuid() != 0) {
+               fprintf(stderr, "smbstatus only works as root!\n");
+               ret = 1;
+               goto done;
+       }
+
        /* setup the flags based on the possible combincations */
 
        show_processes = !(shares_only || locks_only || profile_only) || processes_only;
@@ -444,32 +1180,13 @@ int main(int argc, const char *argv[])
        if ( username )
                Ucrit_addUid( nametouid(username) );
 
-       if (verbose) {
+       if (verbose && !state.json_output) {
                d_printf("using configfile = %s\n", get_dyn_CONFIGFILE());
        }
 
-       if (!lp_load_initial_only(get_dyn_CONFIGFILE())) {
-               fprintf(stderr, "Can't load %s - run testparm to debug it\n",
-                       get_dyn_CONFIGFILE());
-               ret = -1;
-               goto done;
-       }
-
-
-       /*
-        * This implicitly initializes the global ctdbd connection,
-        * usable by the db_open() calls further down.
-        */
-       msg_ctx = messaging_init(NULL, samba_tevent_context_init(NULL));
+       msg_ctx = cmdline_messaging_context(get_dyn_CONFIGFILE());
        if (msg_ctx == NULL) {
-               fprintf(stderr, "messaging_init failed\n");
-               ret = -1;
-               goto done;
-       }
-
-       if (!lp_load_global(get_dyn_CONFIGFILE())) {
-               fprintf(stderr, "Can't load %s - run testparm to debug it\n",
-                       get_dyn_CONFIGFILE());
+               fprintf(stderr, "Could not initialize messaging, not root?\n");
                ret = -1;
                goto done;
        }
@@ -477,22 +1194,26 @@ int main(int argc, const char *argv[])
        switch (profile_only) {
                case 'P':
                        /* Dump profile data */
-                       ok = status_profile_dump(verbose);
-                       return ok ? 0 : 1;
+                       ok = status_profile_dump(verbose, &state);
+                       ret = ok ? 0 : 1;
+                       goto done;
                case 'R':
                        /* Continuously display rate-converted data */
-                       ok = status_profile_rates(verbose);
-                       return ok ? 0 : 1;
+                       if (!state.json_output) {
+                               ok = status_profile_rates(verbose);
+                               ret = ok ? 0 : 1;
+                       } else {
+                               fprintf(stderr, "Call rates not available in a json output.\n");
+                               ret = 1;
+                       }
+                       goto done;
                default:
                        break;
        }
 
        if ( show_processes ) {
-               d_printf("\nSamba version %s\n",samba_version_string());
-               d_printf("PID     Username      Group         Machine            Protocol Version       \n");
-               d_printf("------------------------------------------------------------------------------\n");
-
-               sessionid_traverse_read(traverse_sessionid, NULL);
+               prepare_sessionid(&state);
+               sessionid_traverse_read(traverse_sessionid, &state);
 
                if (processes_only) {
                        goto done;
@@ -503,13 +1224,12 @@ int main(int argc, const char *argv[])
                if (brief) {
                        goto done;
                }
+               prepare_connections(&state);
+               connections_forall_read(traverse_connections, &state);
 
-               d_printf("\nService      pid     machine       Connected at\n");
-               d_printf("-------------------------------------------------------\n");
-
-               connections_forall_read(traverse_connections, NULL);
-
-               d_printf("\n");
+               if (!state.json_output) {
+                       d_printf("\n");
+               }
 
                if ( shares_only ) {
                        goto done;
@@ -520,9 +1240,9 @@ int main(int argc, const char *argv[])
                int result;
                struct db_context *db;
 
-               db_path = lock_path("locking.tdb");
+               db_path = lock_path(talloc_tos(), "locking.tdb");
                if (db_path == NULL) {
-                       d_printf("Out of memory - exiting\n");
+                       fprintf(stderr, "Out of memory - exiting\n");
                        ret = -1;
                        goto done;
                }
@@ -532,52 +1252,58 @@ int main(int argc, const char *argv[])
                             DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
 
                if (!db) {
-                       d_printf("%s not initialised\n", db_path);
-                       d_printf("This is normal if an SMB client has never "
+                       fprintf(stderr, "%s not initialised\n", db_path);
+                       fprintf(stderr, "This is normal if an SMB client has never "
                                 "connected to your server.\n");
                        TALLOC_FREE(db_path);
-                       exit(0);
+                       ret = 0;
+                       goto done;
                } else {
                        TALLOC_FREE(db);
                        TALLOC_FREE(db_path);
                }
 
                if (!locking_init_readonly()) {
-                       d_printf("Can't initialise locking module - exiting\n");
+                       fprintf(stderr, "Can't initialise locking module - exiting\n");
                        ret = 1;
                        goto done;
                }
 
-               result = share_entry_forall(print_share_mode, NULL);
+               prepare_share_mode(&state);
+               result = share_entry_forall(print_share_mode, &state);
 
-               if (result == 0) {
-                       d_printf("No locked files\n");
-               } else if (result < 0) {
-                       d_printf("locked file list truncated\n");
+               if (result == 0 && !state.json_output) {
+                       fprintf(stderr, "No locked files\n");
+               } else if (result < 0 && !state.json_output) {
+                       fprintf(stderr, "locked file list truncated\n");
                }
 
-               d_printf("\n");
+               if (!state.json_output) {
+                       d_printf("\n");
+               }
 
                if (show_brl) {
-                       brl_forall(print_brl, NULL);
+                       prepare_brl(&state);
+                       brl_forall(print_brl, &state);
                }
 
                locking_end();
        }
 
        if (show_notify) {
-               struct notify_context *n;
-
-               n = notify_init(talloc_tos(), msg_ctx,
-                               messaging_tevent_context(msg_ctx));
-               if (n == NULL) {
-                       goto done;
-               }
-               notify_walk(n, print_notify_rec, NULL);
-               TALLOC_FREE(n);
+               prepare_notify(&state);
+               notify_walk(msg_ctx, print_notify_rec, &state);
        }
 
 done:
+       cmdline_messaging_context_free();
+       poptFreeContext(pc);
+#ifdef HAVE_JANSSON
+       if (state.json_output) {
+               d_printf("%s\n", json_to_string(frame, &state.root_json));
+       }
+       json_free(&state.root_json);
+#endif /* HAVE_JANSSON */
        TALLOC_FREE(frame);
        return ret;
 }