On Fri, 2024-11-29 at 23:56 +0100, Jiri Hnidek wrote:
> > I finished implementation of pr_attr() pr_subattr() pr_list_start() 
> > pr_list_item() pr_list_end().
> > I updated the documentation, bash completion script. The dmidecode should 
> > work as expected.
> > The new PR: https://github.com/jirihnidek/dmidecode/pull/3 is ready for 
> > review.

Continuation of my review...

[Make CLI option --json mutual exlusive with some other]

mutual -> mutually

Looks good otherwise.

[Added support for attributes, when JSON output is used]

I'm skeptical about using the exact same function for attributes and
sub-attributes. It is true that indentation doesn't matter for JSON
output, however in text output this identation is not only visual, it
has a meaning. It implies that the (indented) sub-attributes relate to
the last attribute printed. This relation should be visible in the JSON
structure.

With the current implementation, we may have DMI tables where you will
try to add an already existing hash key. Consider the following type 42
record:

Handle 0x0072, DMI type 42, 145 bytes
Management Controller Host Interface
        Host Interface Type: Network
        Device Type: USB
        idVendor: 0x03f0
        idProduct: 0x2927
        Protocol ID: 04 (Redfish over IP)
                Service UUID: a1e45e76-4b48-5d97-9926-b44bd79f9432
                Host IP Assignment Type: DHCP
                Host IP Address Format: IPv4
                Redfish Service IP Discovery Type: DHCP
                Redfish Service IP Address Format: IPv4
                Redfish Service Hostname: redfish.thecircle.net

If we ever support another protocol ID, that protocol will come with
its own "Service UUID" sub-attribute, which you won't be able to add
because it already exists.

So at the very least you would need to prefix the sub-attribute keys
with their parent attribute, to make them unique again.

But IMHO it would be even cleaner if the parent-children relation would
be present in the JSON document structure. After all, JSON is designed
to represent complex data structures.

Note that I am open to changes to the current implementation if that
would make implementing JSON output cleanly easier. For example, if it
would help if printing the parent attribute was implemented as a
dedicated function instead of the generic pr_attr() (much like we have
pr_list_start), we can do this.

I also see that HPE-specific type 199 is going to cause trouble at the
pr_attr level already, as it prints multiple entries with the same name
(CPU ID). We'll have to change that, else the JSON output will be
invalid.

About the code itself:

+static void attr_json(const char *name, const char *format, va_list args) {

Opening curly brace should go to the following line.

+       char *str = NULL;
+       int ret;

Please leave a blank line between variable declarations and the actual
code.

+       ret = vasprintf(&str, format, args);
+       if (ret != -1)
+       {
+               /* Convert name to lowercase and replace " " with "_" */

I'm curious, is there anything in the JSON spec which prevents you from
using the name string as a hash key as is?

+               char *key = strdup(name);
+               if (key != NULL)
+               {
+                       int i;
+                       for (i = 0; key[i]; i++)
+                       {
+                               if (key[i] == ' ')
+                               {
+                                       key[i] = '_';
+                               }
+                               else
+                               {
+                                       key[i] = tolower(key[i]);
+                               }

What about other special characters? I can't promise there won't be
any. There's at least one name field which includes a "-" already (in
DMI type 1) and one which includes a "." (in HPE-specific type 219). A
few include a digit too (HPE-specific type 236 for example) and one
includes a "/" (in HPE-specific type 240).

+                       }
+                       ret = json_object_object_add(json_dmi_out.values, key, 
json_object_new_string(str));
+                       if (ret < 0)
+                               fprintf(stderr, "Unable to add JSON object: 
'%s' with key: '%s'\n", str, key);
+                       free(key);

It's really sad if json-c can't use the provided key string directly
and always makes a copy...

[Added support for lists, when JSON output is used]

 static void pr_list_start_json(const char *name, const char *format, va_list 
args)
 {
-       (void)name;
-       (void)format;
-       (void)args;
+       char *key = NULL;

Missing blank line here.

+       if (format != NULL)
+       {
+               char *str=NULL;

Missing spaces around "=".

+               int ret;

Missing blank line here.

+               ret = vasprintf(&str, format, args);
+               if (ret != -1)
+               {
+                       /* try to create key from name and format string */

The name itself is guaranteed to be unique, so you can ignore the
formatted value string for the hash key. This will save some code and
memory, and the key will make more sense too.

Typically the format will be a count of the list items. If you are
going to print it then it should be as a value, not as a key. Or we may
decide that JSON users can easily count the list items themselves.

I see we have one oddity in the code at the moment, in
dmi_base_board_features(), if there are not features to list, we print
"None" as the formatted value, and pr_list_item() is never called.
We'll have to ensure the JSON output can cope with that, or adjust it
if needed.

+                       size_t len_name = strlen(name);
+                       size_t len_str = strlen(str);

Missing blank line here.

+                       key = malloc(len_name + len_str + 1);
+                       if (key != NULL)
+                       {
+                               key[0] = '\0';

Not needed, strcpy is guaranteed to NULL-terminate the buffer it writes
to.

+                               strcpy(key, name);
+                               strcat(key, str);
+                       }
+                       else
+                       {
+                               fprintf(stderr, "Unable to allocate memory 
(size: %ld) for JSON key\n", len_name + len_str + 1);
+                       }
+                       free(str);
+               }
+               else
+               {
+                       fprintf(stderr, "Unable to create JSON key from name: 
'%s' and format: '%s'\n", name, format);
+               }
+       }
+       else
+       {
+               key = strdup(name);
+       }
+       if (key != NULL)
+       {
+               json_dmi_out.list = json_object_new_array();
+               int i;

Missing blank line here.

+               /* Convert key to lowercase and replace " " with "_" */
+               for (i = 0; key[i]; i++)
+               {
+                       if (key[i] == ' ')
+                       {
+                               key[i] = '_';
+                       }
+                       else
+                       {
+                               key[i] = tolower(key[i]);
+                       }
+               }

You've been doing the same in attr_json() already, so this clearly
deserves to go to a separate helper function.

+               json_object_object_add(json_dmi_out.values, key, 
json_dmi_out.list);
+               free(key);
+       }
 }

 static void pr_list_item_json(const char *format, va_list args)
 {
-       (void)format;
-       (void)args;
+       char *str = NULL;
+       int ret;

Blank line here.

+       if (json_dmi_out.list)
+       {
+               ret = vasprintf(&str, format, args);
+               if (ret != -1)
+               {
+                       json_object_array_add(json_dmi_out.list, 
json_object_new_string(str));
+                       free(str);
+               }
+       }
+       else
+       {
+               ret = vasprintf(&str, format, args);
+               if (ret != -1)
+               {
+                       fprintf(stderr, "Unable to add JSON item '%s' to the 
non existing list\n", str);
+                       free(str);
+               }

I personally would not bother checking json_dmi_out.list, as a crash
with a backtrace is the best way to investigate the bug, and this
avoids useless code at runtime. But if insist on checking it, then the
code above can be rewritten in a way which avoids duplicating code:
call vasprintf() first, and use json_dmi_out.list to decide whether you
should call json_object_array_add() or print an error message.

+       }
 }

I'll be away next week, I'll continue with the review work when I
return.

Thanks,
-- 
Jean Delvare
SUSE L3 Support

Reply via email to