JC Version 1.15.0 Released

Try the jc web demo!

I’m excited to announce the release of jc version 1.15.0 available on github and pypi. This is a significant release that includes dozens of new features and parsers.

jc now supports over 70 commands and file-types, including the new acpi, upower, /usr/bin/time, dpkg -l, rpm -qi, finger, and dir command parsers. Several existing parsers have been updated to include calculated time fields for convenience. These include date, uptime, stat, timedatectl, who, dig, and ls.

The CLI experience has been enhanced with new -h help and -v version options. External library dependencies are now optional, so jc will work just fine without them, albeit with limited functionality. JSON output is now more compact so less data is being piped between programs and unencoded unicode characters are now supported in JSON strings.

jc can be installed via pip or through several official OS package repositories, including Debian, Ubuntu, Fedora, openSUSE, Arch Linux, NixOS Linux, Guix System Linux, FreeBSD, and macOS. For more information on how to get jc, click here.

To upgrade with pip:

$ pip3 install --upgrade jc

New Features

  • -h option displays help and the parser list. jc no longer displays the help text on error. Now that there are so many parsers, the -h option prints to STDOUT so the output can be piped to more or less for paging.
  • -v option displays the version, github site, and copyright information
  • New calculated epoch timestamp fields have been added to several parsers, including date, stat, timedatectl, who, dig, and ls. These fields are also available in many of the new parsers, including upower, rpm -qi, and dir.

    All timestamps are naive (i.e. based on the timezone of the machine the parser is running on) unless the UTC timezone can be detected within the text of the command output. If the UTC timezone is detected, a timezone-aware timestamp is created. All aware timestamps have the suffix ‘_utc‘. No other timezones are supported for aware timestamps.
  • Several calculated time fields have been added to the uptime parser.
  • All external library dependencies, including pygments, ruamel.yaml, and xmltodict are now optional. If a dependency is missing, jc will still run, but will have limited functionality. For example, if the pygments library is not installed, then all JSON output will be monochrome. If the ruamel.yaml or xmltodict libraries are not installed, then the --yaml or --xml parsers, respectively, will not run.
  • JSON output is more compact, with all spaces between delimiters removed, unless the -p option is used to pretty-print the JSON output. This reduces the amount of data that needs to be piped between programs and can save some disk space if JSON output is being stored to disk.
  • Unencoded unicode characters are now printed in JSON strings. These types of characters include the Copyright ‘©’ symbol and many others.

New Parsers

jc now supports 70 parsers. New parsers include acpi, upower, /usr/bin/time, dpkg -l, rpm -qi, finger, and dir. The dir parser is the first Windows command parser to be included in jc!

Documentation and schemas for all parsers can be found here.

acpi command parser

Linux support for the acpi command. (Documentation):

$ acpi -V | jc --acpi -p          # or:  jc -p acpi -V
[
  {
    "type": "Battery",
    "id": 0,
    "state": "Charging",
    "charge_percent": 71,
    "until_charged": "00:29:20",
    "design_capacity_mah": 2110,
    "last_full_capacity": 2271,
    "last_full_capacity_percent": 100,
    "until_charged_hours": 0,
    "until_charged_minutes": 29,
    "until_charged_seconds": 20,
    "until_charged_total_seconds": 1760
  },
  {
    "type": "Adapter",
    "id": 0,
    "on-line": true
  },
  {
    "type": "Thermal",
    "id": 0,
    "mode": "ok",
    "temperature": 46.0,
    "temperature_unit": "C",
    "trip_points": [
      {
        "id": 0,
        "switches_to_mode": "critical",
        "temperature": 127.0,
        "temperature_unit": "C"
      },
      {
        "id": 1,
        "switches_to_mode": "hot",
        "temperature": 127.0,
        "temperature_unit": "C"
      }
    ]
  },
  {
    "type": "Cooling",
    "id": 0,
    "messages": [
      "Processor 0 of 10"
    ]
  },
  {
    "type": "Cooling",
    "id": 1,
    "messages": [
      "Processor 0 of 10"
    ]
  },
  {
    "type": "Cooling",
    "id": 2,
    "messages": [
      "x86_pkg_temp no state information available"
    ]
  },
  {
    "type": "Cooling",
    "id": 3,
    "messages": [
      "Processor 0 of 10"
    ]
  },
  {
    "type": "Cooling",
    "id": 4,
    "messages": [
      "intel_powerclamp no state information available"
    ]
  },
  {
    "type": "Cooling",
    "id": 5,
    "messages": [
      "Processor 0 of 10"
    ]
  }
]

upower command parser

Linux support for the upower command. (Documentation):

$ upower -i /org/freedesktop/UPower/devices/battery | jc --upower -p          # or jc -p upower -i /org/freedesktop/UPower/devices/battery
[
  {
    "native_path": "/sys/devices/LNXSYSTM:00/device:00/PNP0C0A:00/power_supply/BAT0",
    "vendor": "NOTEBOOK",
    "model": "BAT",
    "serial": "0001",
    "power_supply": true,
    "updated": "Thu 11 Mar 2021 06:28:08 PM UTC",
    "has_history": true,
    "has_statistics": true,
    "detail": {
      "type": "battery",
      "present": true,
      "rechargeable": true,
      "state": "charging",
      "energy": 22.3998,
      "energy_empty": 0.0,
      "energy_full": 52.6473,
      "energy_full_design": 62.16,
      "energy_rate": 31.6905,
      "voltage": 12.191,
      "time_to_full": 57.3,
      "percentage": 42.5469,
      "capacity": 84.6964,
      "technology": "lithium-ion",
      "energy_unit": "Wh",
      "energy_empty_unit": "Wh",
      "energy_full_unit": "Wh",
      "energy_full_design_unit": "Wh",
      "energy_rate_unit": "W",
      "voltage_unit": "V",
      "time_to_full_unit": "minutes"
    },
    "history_charge": [
      {
        "time": 1328809335,
        "percent_charged": 42.547,
        "status": "charging"
      },
      {
        "time": 1328809305,
        "percent_charged": 42.02,
        "status": "charging"
      }
    ],
    "history_rate": [
      {
        "time": 1328809335,
        "percent_charged": 31.691,
        "status": "charging"
      }
    ],
    "updated_seconds_ago": 441975,
    "updated_epoch": 1615516088,
    "updated_epoch_utc": 1615487288
  }
]

/usr/bin/time command parser

Linux, macOS, and BSD support for the /usr/bin/time command. (Documentation):

$ /usr/bin/time --verbose -o timefile.out sleep 2.5; cat timefile.out | jc --time -p
{
  "command_being_timed": "sleep 2.5",
  "user_time": 0.0,
  "system_time": 0.0,
  "cpu_percent": 0,
  "elapsed_time": "0:02.50",
  "average_shared_text_size": 0,
  "average_unshared_data_size": 0,
  "average_stack_size": 0,
  "average_total_size": 0,
  "maximum_resident_set_size": 2084,
  "average_resident_set_size": 0,
  "major_pagefaults": 0,
  "minor_pagefaults": 72,
  "voluntary_context_switches": 2,
  "involuntary_context_switches": 1,
  "swaps": 0,
  "block_input_operations": 0,
  "block_output_operations": 0,
  "messages_sent": 0,
  "messages_received": 0,
  "signals_delivered": 0,
  "page_size": 4096,
  "exit_status": 0,
  "elapsed_time_hours": 0,
  "elapsed_time_minutes": 0,
  "elapsed_time_seconds": 2,
  "elapsed_time_centiseconds": 50,
  "elapsed_time_total_seconds": 2.5
}

dpkg -l command parser

Linux support for the dpkg -l command. (Documentation):

$ dpkg -l | jc --dpkg-l -p          # or:  jc -p dpkg -l
[
  {
    "codes": "ii",
    "name": "accountsservice",
    "version": "0.6.45-1ubuntu1.3",
    "architecture": "amd64",
    "description": "query and manipulate user account information",
    "desired": "install",
    "status": "installed"
  },
  {
    "codes": "rc",
    "name": "acl",
    "version": "2.2.52-3build1",
    "architecture": "amd64",
    "description": "Access control list utilities",
    "desired": "remove",
    "status": "config-files"
  },
  {
    "codes": "uWR",
    "name": "acpi",
    "version": "1.7-1.1",
    "architecture": "amd64",
    "description": "displays information on ACPI devices",
    "desired": "unknown",
    "status": "trigger await",
    "error": "reinstall required"
  },
  {
    "codes": "rh",
    "name": "acpid",
    "version": "1:2.0.28-1ubuntu1",
    "architecture": "amd64",
    "description": "Advanced Configuration and Power Interface event daemon",
    "desired": "remove",
    "status": "half installed"
  },
  {
    "codes": "pn",
    "name": "adduser",
    "version": "3.116ubuntu1",
    "architecture": "all",
    "description": "add and remove users and groups",
    "desired": "purge",
    "status": "not installed"
  }
]

rpm -qi command parser

Linux support for the rpm -qi command. (Documentation):

$ rpm_qia | jc --rpm_qi -p          # or:  jc -p rpm -qia
[
  {
    "name": "make",
    "epoch": 1,
    "version": "3.82",
    "release": "24.el7",
    "architecture": "x86_64",
    "install_date": "Wed 16 Oct 2019 09:21:42 AM PDT",
    "group": "Development/Tools",
    "size": 1160660,
    "license": "GPLv2+",
    "signature": "RSA/SHA256, Thu 22 Aug 2019 02:34:59 PM PDT, Key ID 24c6a8a7f4a80eb5",
    "source_rpm": "make-3.82-24.el7.src.rpm",
    "build_date": "Thu 08 Aug 2019 05:47:25 PM PDT",
    "build_host": "x86-01.bsys.centos.org",
    "relocations": "(not relocatable)",
    "packager": "CentOS BuildSystem <http://bugs.centos.org>",
    "vendor": "CentOS",
    "url": "http://www.gnu.org/software/make/",
    "summary": "A GNU tool which simplifies the build process for users",
    "description": "A GNU tool for controlling the generation of executables and other non-source...",
    "build_epoch": 1565311645,
    "build_epoch_utc": null
  },
  {
    "name": "kbd-legacy",
    "version": "1.15.5",
    "release": "15.el7",
    "architecture": "noarch",
    "install_date": "Thu 15 Aug 2019 10:53:08 AM PDT",
    "group": "System Environment/Base",
    "size": 503608,
    "license": "GPLv2+",
    "signature": "RSA/SHA256, Mon 12 Nov 2018 07:17:49 AM PST, Key ID 24c6a8a7f4a80eb5",
    "source_rpm": "kbd-1.15.5-15.el7.src.rpm",
    "build_date": "Tue 30 Oct 2018 03:40:00 PM PDT",
    "build_host": "x86-01.bsys.centos.org",
    "relocations": "(not relocatable)",
    "packager": "CentOS BuildSystem <http://bugs.centos.org>",
    "vendor": "CentOS",
    "url": "http://ftp.altlinux.org/pub/people/legion/kbd",
    "summary": "Legacy data for kbd package",
    "description": "The kbd-legacy package contains original keymaps for kbd package. Please note...",
    "build_epoch": 1540939200,
    "build_epoch_utc": null
  }
]

finger command parser

Linux, macOS, and BSD support for the finger command. (Documentation):

$ finger | jc --finger -p          # or:  jc -p finger
[
  {
    "login": "jdoe",
    "name": "John Doe",
    "tty": "tty1",
    "idle": "14d",
    "login_time": "Mar 22 21:14",
    "tty_writeable": false,
    "idle_minutes": 0,
    "idle_hours": 0,
    "idle_days": 14,
    "total_idle_minutes": 20160
  },
  {
    "login": "jdoe",
    "name": "John Doe",
    "tty": "pts/0",
    "idle": null,
    "login_time": "Apr  5 15:33",
    "details": "(192.168.1.22)",
    "tty_writeable": true,
    "idle_minutes": 0,
    "idle_hours": 0,
    "idle_days": 0,
    "total_idle_minutes": 0
  }
]

dir command parser

Windows support for the dir command – written by Rasheed Elsaleh. (Documentation):

C:> dir | jc --dir -p          # or:  jc -p dir
[
  {
    "date": "03/24/2021",
    "time": "03:15 PM",
    "dir": true,
    "size": null,
    "filename": ".",
    "parent": "C:\\Program Files\\Internet Explorer",
    "epoch": 1616624100
  },
  {
    "date": "03/24/2021",
    "time": "03:15 PM",
    "dir": true,
    "size": null,
    "filename": "..",
    "parent": "C:\\Program Files\\Internet Explorer",
    "epoch": 1616624100
  },
  {
    "date": "12/07/2019",
    "time": "02:49 AM",
    "dir": true,
    "size": null,
    "filename": "en-US",
    "parent": "C:\\Program Files\\Internet Explorer",
    "epoch": 1575715740
  },
  {
    "date": "12/07/2019",
    "time": "02:09 AM",
    "dir": false,
    "size": 54784,
    "filename": "ExtExport.exe",
    "parent": "C:\\Program Files\\Internet Explorer",
    "epoch": 1575713340
  }
]

Updated Parsers

  • Several parsers have been updated to include calculated epoch timestamp fields, including: date, stat, timedatectl, who, dig, and ls. See the Schema Changes section for more details.
  • The uptime parser has been enhanced with additional calculated time fields. See the Schema Changes section for more details.

Schema Changes

date command parser

The date command parser has been completely rewritten and enhanced with several new fields, including: epoch, epoch_utc, hour_24, utc_offset, day_of_year, week_of_year, iso, and timezone_aware. The weekday_num field has also been updated to conform to ISO 8601 compliant numbering. (Documentation)

$ date | jc --date -p          # or:  jc -p date
{
  "year": 2021,
  "month": "Mar",
  "month_num": 3,
  "day": 25,
  "weekday": "Thu",
  "weekday_num": 4,
  "hour": 2,
  "hour_24": 2,
  "minute": 2,
  "second": 26,
  "period": "AM",
  "timezone": "UTC",
  "utc_offset": "+0000",
  "day_of_year": 84,
  "week_of_year": 12,
  "iso": "2021-03-25T02:02:26+00:00",
  "epoch": 1616662946,
  "epoch_utc": 1616637746,
  "timezone_aware": true
}

stat command parser

The stat parser has been updated to add the following fields: access_time_epoch, access_time_epoch_utc, modify_time_epoch, modify_time_epoch_utc, change_time_epoch, change_time_epoch_utc, birth_time_epoch, birth_time_epoch_utc. (Documentation)

$ stat /bin/* | jc --stat -p          # or:  jc -p stat /bin/*
[
  {
    "file": "/bin/bash",
    "size": 1113504,
    "blocks": 2176,
    "io_blocks": 4096,
    "type": "regular file",
    "device": "802h/2050d",
    "inode": 131099,
    "links": 1,
    "access": "0755",
    "flags": "-rwxr-xr-x",
    "uid": 0,
    "user": "root",
    "gid": 0,
    "group": "root",
    "access_time": "2019-11-14 08:18:03.509681766 +0000",
    "modify_time": "2019-06-06 22:28:15.000000000 +0000",
    "change_time": "2019-08-12 17:21:29.521945390 +0000",
    "birth_time": null,
    "access_time_epoch": 1573748283,
    "access_time_epoch_utc": 1573719483,
    "modify_time_epoch": 1559885295,
    "modify_time_epoch_utc": 1559860095,
    "change_time_epoch": 1565655689,
    "change_time_epoch_utc": 1565630489,
    "birth_time_epoch": null,
    "birth_time_epoch_utc": null
  },
  {
    "file": "/bin/btrfs",
    "size": 716464,
    "blocks": 1400,
    "io_blocks": 4096,
    "type": "regular file",
    "device": "802h/2050d",
    "inode": 131100,
    "links": 1,
    "access": "0755",
    "flags": "-rwxr-xr-x",
    "uid": 0,
    "user": "root",
    "gid": 0,
    "group": "root",
    "access_time": "2019-11-14 08:18:28.990834276 +0000",
    "modify_time": "2018-03-12 23:04:27.000000000 +0000",
    "change_time": "2019-08-12 17:21:29.545944399 +0000",
    "birth_time": null,
    "access_time_epoch": 1573748308,
    "access_time_epoch_utc": 1573719508,
    "modify_time_epoch": 1520921067,
    "modify_time_epoch_utc": 1520895867,
    "change_time_epoch": 1565655689,
    "change_time_epoch_utc": 1565630489,
    "birth_time_epoch": null,
    "birth_time_epoch_utc": null
  }
]

timedatectl command parser

The epoch_utc field has been added to the timedatectl command parser. (Documentation)

timedatectl | jc --timedatectl -p          # or: jc -p timedatectl
{
  "local_time": "Tue 2020-03-10 17:53:21 PDT",
  "universal_time": "Wed 2020-03-11 00:53:21 UTC",
  "rtc_time": "Wed 2020-03-11 00:53:21",
  "time_zone": "America/Los_Angeles (PDT, -0700)",
  "ntp_enabled": true,
  "ntp_synchronized": true,
  "rtc_in_local_tz": false,
  "dst_active": true,
  "epoch_utc": 1583888001
}

who command parser

The epoch field has been added to the who command parser. (Documentation)

$ who | jc --who -p          # or:  jc -p who
[
  {
    "user": "joeuser",
    "tty": "ttyS0",
    "time": "2020-03-02 02:52",
    "epoch": 1583146320
  },
  {
    "user": "joeuser",
    "tty": "pts/0",
    "time": "2020-03-02 05:15",
    "from": "192.168.71.1",
    "epoch": 1583154900
  }
]

dig command parser

The when_epoch and when_epoch_utc fields have been added to the dig command parser. (Documentation)

$ dig cnn.com www.cnn.com @205.251.194.64 | jc --dig -p          # or:  jc -p dig cnn.com www.cnn.com @205.251.194.64
[
  {
    "id": 52172,
    "opcode": "QUERY",
    "status": "NOERROR",
    "flags": [
      "qr",
      "rd",
      "ra"
    ],
    "query_num": 1,
    "answer_num": 4,
    "authority_num": 0,
    "additional_num": 1,
    "question": {
      "name": "cnn.com.",
      "class": "IN",
      "type": "A"
    },
    "answer": [
      {
        "name": "cnn.com.",
        "class": "IN",
        "type": "A",
        "ttl": 27,
        "data": "151.101.65.67"
      },
      {
        "name": "cnn.com.",
        "class": "IN",
        "type": "A",
        "ttl": 27,
        "data": "151.101.129.67"
      },
      {
        "name": "cnn.com.",
        "class": "IN",
        "type": "A",
        "ttl": 27,
        "data": "151.101.1.67"
      },
      {
        "name": "cnn.com.",
        "class": "IN",
        "type": "A",
        "ttl": 27,
        "data": "151.101.193.67"
      }
    ],
    "query_time": 38,
    "server": "2600",
    "when": "Tue Mar 30 20:07:59 PDT 2021",
    "rcvd": 100,
    "when_epoch": 1617160079,
    "when_epoch_utc": null
  },
  {
    "id": 36292,
    "opcode": "QUERY",
    "status": "NOERROR",
    "flags": [
      "qr",
      "aa",
      "rd"
    ],
    "query_num": 1,
    "answer_num": 1,
    "authority_num": 4,
    "additional_num": 1,
    "question": {
      "name": "www.cnn.com.",
      "class": "IN",
      "type": "A"
    },
    "answer": [
      {
        "name": "www.cnn.com.",
        "class": "IN",
        "type": "CNAME",
        "ttl": 300,
        "data": "turner-tls.map.fastly.net."
      }
    ],
    "authority": [
      {
        "name": "cnn.com.",
        "class": "IN",
        "type": "NS",
        "ttl": 3600,
        "data": "ns-1086.awsdns-07.org."
      },
      {
        "name": "cnn.com.",
        "class": "IN",
        "type": "NS",
        "ttl": 3600,
        "data": "ns-1630.awsdns-11.co.uk."
      },
      {
        "name": "cnn.com.",
        "class": "IN",
        "type": "NS",
        "ttl": 3600,
        "data": "ns-47.awsdns-05.com."
      },
      {
        "name": "cnn.com.",
        "class": "IN",
        "type": "NS",
        "ttl": 3600,
        "data": "ns-576.awsdns-08.net."
      }
    ],
    "query_time": 27,
    "server": "205.251.194.64#53(205.251.194.64)",
    "when": "Tue Mar 30 20:07:59 PDT 2021",
    "rcvd": 212,
    "when_epoch": 1617160079,
    "when_epoch_utc": null
  }
]

ls command parser

The epoch and epoch_utc fields have been added to the ls command parser. Note, that these fields are only available if the --full-time or -l --time-style=full-iso options are used when running ls. (Documentation)

$ ls --full-time /usr/bin | jc --ls -p          # or:  jc -p ls --full-time /usr/bin
[
  {
    "filename": "acpi",
    "flags": "-rwxr-xr-x",
    "links": 1,
    "owner": "root",
    "group": "root",
    "size": 23656,
    "date": "2018-01-14 19:20:21.000000000 -0800",
    "epoch": 1515986421,
    "epoch_utc": null
  },
  {
    "filename": "acpi_listen",
    "flags": "-rwxr-xr-x",
    "links": 1,
    "owner": "root",
    "group": "root",
    "size": 14608,
    "date": "2017-04-27 21:28:10.000000000 -0700",
    "epoch": 1493353690,
    "epoch_utc": null
  }
]

uptime command parser

Several calculated time fields have been added to the uptime command parser, including: uptime_days, uptime_hours, uptime_minutes, uptime_total_seconds, time_hour, time_minute, and time_second. (Documentation)

$ uptime | jc --uptime -p          # or:  jc -p uptime
{
  "time": "11:35",
  "uptime": "3 days, 4:03",
  "users": 5,
  "load_1m": 1.88,
  "load_5m": 2.0,
  "load_15m": 1.94,
  "time_hour": 11,
  "time_minute": 35,
  "time_second": null,
  "uptime_days": 3,
  "uptime_hours": 4,
  "uptime_minutes": 3,
  "uptime_total_seconds": 273780
}

Full Parser List

  • acpi
  • airport -I
  • airport -s
  • arp
  • blkid
  • cksum
  • crontab
  • crontab (with user info)
  • csv
  • date
  • df
  • dig
  • dir
  • dmidecode
  • dpkg -l
  • du
  • env
  • file
  • finger
  • free
  • fstab
  • group
  • gshadow
  • hash
  • hashsum (various hash sum programs: md5, md5sum, shasum, etc.)
  • hciconfig
  • history
  • hosts
  • id
  • ifconfig
  • ini
  • iptables
  • iw_scan
  • jobs
  • kv
  • last
  • ls
  • lsblk
  • lsmod
  • lsof
  • mount
  • netstat
  • ntpq
  • passwd
  • ping
  • pip list
  • pip show
  • ps
  • route
  • rpm -qi
  • shadow
  • ss
  • stat
  • sysctl
  • systemctl
  • systemctl list-jobs
  • systemctl list-sockets
  • systemctl list-unit-files
  • time (/usr/bin/time)
  • timedatectl
  • tracepath
  • traceroute
  • uname -a
  • upower
  • uptime
  • w
  • wc
  • who
  • xml
  • yaml

Version 1.15.1 Updates

  • New feature to show parser documentation interactively with -h --parser_name. For example: $ jc -h --arp
  • Man page added to pypi package for easier packaging in homebrew
  • Update rpm-qi parser to add two calculated timestamp fields: install_date_epoch and install_date_epoch_utc
  • Clean up documentation and autogenerate the Parser Information section from metadata

Schema Changes

The rpm-qi parser has been updated to add two calculated timestamp fields: install_date_epoch (naive) and install_date_epoch_utc (timezone-aware).

$ rpm -qia | jc --rpm-qi -p
    [
      {
        "name": "make",
        "epoch": 1,
        "version": "3.82",
        "release": "24.el7",
        "architecture": "x86_64",
        "install_date": "Wed 16 Oct 2019 09:21:42 AM PDT",
        "group": "Development/Tools",
        "size": 1160660,
        "license": "GPLv2+",
        "signature": "RSA/SHA256, Thu 22 Aug 2019 02:34:59 PM PDT, Key ID 24c6a8a7f4a80eb5",
        "source_rpm": "make-3.82-24.el7.src.rpm",
        "build_date": "Thu 08 Aug 2019 05:47:25 PM PDT",
        "build_host": "x86-01.bsys.centos.org",
        "relocations": "(not relocatable)",
        "packager": "CentOS BuildSystem <http://bugs.centos.org>",
        "vendor": "CentOS",
        "url": "http://www.gnu.org/software/make/",
        "summary": "A GNU tool which simplifies the build process for users",
        "description": "A GNU tool for controlling the generation of executables and other...",
        "build_epoch": 1565311645,
        "build_epoch_utc": null,
        "install_date_epoch": 1571242902,
        "install_date_epoch_utc": null
      }
    ]

Happy parsing!

For more information on the motivations for creating jc, see my blog post.

Published by kellyjonbrazil

I'm a cybersecurity and cloud computing nerd.

Leave a Reply

RSS
Follow by Email
LinkedIn
LinkedIn
Share
%d bloggers like this: