Skip to content

Commit fc8073a

Browse files
Martin Belangerdwsuse
authored andcommitted
util: Add key-value helpers and OS identification tools
Key value helpers and also the OS identification are used for implementing TP8010 support. Signed-off-by: Martin Belanger <[email protected]> [dwagner: refactoring, reformating] Signed-off-by: Daniel Wagner <[email protected]>
1 parent 4db24ee commit fc8073a

2 files changed

Lines changed: 333 additions & 0 deletions

File tree

src/nvme/util.c

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
#include <string.h>
1212
#include <errno.h>
1313

14+
#include <sys/param.h>
1415
#include <sys/types.h>
1516
#include <arpa/inet.h>
1617
#include <netdb.h>
18+
#include <unistd.h>
1719

1820
#include <ccan/endian/endian.h>
1921

@@ -556,3 +558,204 @@ char *hostname2traddr(struct nvme_root *r, const char *traddr)
556558
freeaddrinfo(host_info);
557559
return ret_traddr;
558560
}
561+
562+
char *startswith(const char *s, const char *prefix)
563+
{
564+
size_t l;
565+
566+
l = strlen(prefix);
567+
if (!strncmp(s, prefix, l))
568+
return (char *)s + l;
569+
570+
return NULL;
571+
}
572+
573+
char *kv_strip(char *kv)
574+
{
575+
char *s;
576+
577+
kv[strcspn(kv, "\n\r")] = '\0';
578+
579+
/* Remove leading newline and spaces */
580+
kv += strspn(kv, " \t\n\r");
581+
582+
/* Skip comments and empty lines */
583+
if (*kv == '#' || *kv == '\0') {
584+
*kv = '\0';
585+
return kv;
586+
}
587+
588+
/* Remove trailing newline chars */
589+
kv[strcspn(kv, "\n\r")] = '\0';
590+
591+
/* Delete trailing comments (including spaces/tabs that precede the #)*/
592+
s = &kv[strcspn(kv, "#")];
593+
*s-- = '\0';
594+
while ((s >= kv) && ((*s == ' ') || (*s == '\t'))) {
595+
*s-- = '\0';
596+
}
597+
598+
return kv;
599+
}
600+
601+
char *kv_keymatch(const char *kv, const char *key)
602+
{
603+
char *value;
604+
605+
value = startswith(kv, key);
606+
if (value) {
607+
/* Make sure key is a whole word. I.e. it should be
608+
* followed by spaces, tabs, or a equal sign. Skip
609+
* leading spaces, tabs, and equal sign (=) */
610+
switch (*value) {
611+
case ' ':
612+
case '\t':
613+
case '=':
614+
value += strspn(value, " \t=");
615+
return value;
616+
default: ;
617+
}
618+
}
619+
620+
return NULL;
621+
}
622+
623+
/**
624+
* read_file - read contents of file into @buffer.
625+
* @fname: File name
626+
* @buffer: Where to save file's contents
627+
* @bufsz: Size of @buffer. On success, @bufsz gets decremented by the
628+
* number of characters that were writtent to @buffer.
629+
*
630+
* Return: The number of characters read. If the file cannot be opened or
631+
* nothing is read from the file, then this function returns 0.
632+
*/
633+
static size_t read_file(const char * fname, char *buffer, size_t *bufsz)
634+
{
635+
char *p;
636+
FILE *file;
637+
size_t len;
638+
639+
file = fopen(fname, "re");
640+
if (!file)
641+
return 0;
642+
643+
p = fgets(buffer, *bufsz, file);
644+
fclose(file);
645+
646+
if (!p)
647+
return 0;
648+
649+
/* Strip unwanted trailing chars */
650+
len = strcspn(buffer, " \t\n\r");
651+
*bufsz -= len;
652+
653+
return len;
654+
}
655+
656+
static size_t copy_value(char *buf, size_t buflen, const char *value)
657+
{
658+
size_t val_len;
659+
660+
memset(buf, 0, buflen);
661+
662+
/* Remove leading " */
663+
if (value[0] == '"')
664+
value++;
665+
666+
/* Remove trailing " */
667+
val_len = strcspn(value, "\"");
668+
669+
memcpy(buf, value, MIN(val_len, buflen-1));
670+
671+
return val_len;
672+
}
673+
674+
size_t get_entity_name(char *buffer, size_t bufsz)
675+
{
676+
size_t len = !gethostname(buffer, bufsz) ? strlen(buffer) : 0;
677+
678+
/* Fill the rest of buffer with zeros */
679+
memset(&buffer[len], '\0', bufsz-len);
680+
681+
return len;
682+
}
683+
684+
size_t get_entity_version(char *buffer, size_t bufsz)
685+
{
686+
FILE *file;
687+
size_t num_bytes = 0;
688+
689+
/* /proc/sys/kernel/ostype typically contains the string "Linux" */
690+
num_bytes += read_file("/proc/sys/kernel/ostype",
691+
&buffer[num_bytes], &bufsz);
692+
693+
/* /proc/sys/kernel/osrelease contains the Linux
694+
* version (e.g. 5.8.0-63-generic)
695+
*/
696+
buffer[num_bytes++] = ' '; /* Append a space */
697+
num_bytes += read_file("/proc/sys/kernel/osrelease",
698+
&buffer[num_bytes], &bufsz);
699+
700+
/* /etc/os-release contains Key-Value pairs. We only care about the key
701+
* PRETTY_NAME, which contains the Distro's version. For example:
702+
* "SUSE Linux Enterprise Server 15 SP4", "Ubuntu 20.04.3 LTS", or
703+
* "Fedora Linux 35 (Server Edition)"
704+
*/
705+
file = fopen("/etc/os-release", "re");
706+
if (file) {
707+
char name[64] = {0};
708+
size_t name_len = 0;
709+
char ver_id[64] = {0};
710+
size_t ver_id_len = 0;
711+
char line[LINE_MAX];
712+
char *p;
713+
char *s;
714+
715+
/* Read key-value pairs one line at a time */
716+
while ((!name_len || !ver_id_len) &&
717+
(p = fgets(line, sizeof(line), file)) != NULL) {
718+
/* Clean up string by removing leading/trailing blanks
719+
* and new line characters. Also eliminate trailing
720+
* comments, if any.
721+
*/
722+
p = kv_strip(p);
723+
724+
/* Empty string? */
725+
if (*p == '\0')
726+
continue;
727+
728+
s = kv_keymatch(p, "NAME");
729+
if (s)
730+
name_len = copy_value(name, sizeof(name), s);
731+
732+
s = kv_keymatch(p, "VERSION_ID");
733+
if (s)
734+
ver_id_len = copy_value(ver_id, sizeof(ver_id), s);
735+
}
736+
fclose(file);
737+
738+
if (name_len) {
739+
/* Append a space */
740+
buffer[num_bytes++] = ' ';
741+
name_len = MIN(name_len, bufsz);
742+
memcpy(&buffer[num_bytes], name, name_len);
743+
bufsz -= name_len;
744+
num_bytes += name_len;
745+
}
746+
747+
if (ver_id_len) {
748+
/* Append a space */
749+
buffer[num_bytes++] = ' ';
750+
ver_id_len = MIN(ver_id_len, bufsz);
751+
memcpy(&buffer[num_bytes], ver_id, ver_id_len);
752+
bufsz -= ver_id_len;
753+
num_bytes += ver_id_len;
754+
}
755+
}
756+
757+
/* Fill the rest of buffer with zeros */
758+
memset(&buffer[num_bytes], '\0', bufsz);
759+
760+
return num_bytes;
761+
}

src/nvme/util.h

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define _LIBNVME_UTIL_H
1111

1212
#include "types.h"
13+
#include <ccan/endian/endian.h>
1314

1415
/**
1516
* DOC: util.h
@@ -424,4 +425,133 @@ static inline void nvme_id_ns_flbas_to_lbaf_inuse(__u8 flbas, __u8 *lbaf_inuse)
424425
struct nvme_root;
425426

426427
char *hostname2traddr(struct nvme_root *r, const char *traddr);
428+
429+
/**
430+
* get_entity_name - Get Entity Name (ENAME).
431+
* @buffer: The buffer where the ENAME will be saved as an ASCII string.
432+
* @bufsz: The size of @buffer.
433+
*
434+
* Per TP8010, ENAME is defined as the name associated with the host (i.e.
435+
* hostname).
436+
*
437+
* Return: Number of characters copied to @buffer.
438+
*/
439+
size_t get_entity_name(char *buffer, size_t bufsz);
440+
441+
/**
442+
* get_entity_version - Get Entity Version (EVER).
443+
* @buffer: The buffer where the EVER will be saved as an ASCII string.
444+
* @bufsz: The size of @buffer.
445+
*
446+
* EVER is defined as the operating system name and version as an ASCII
447+
* string. This function reads different files from the file system and
448+
* builds a string as follows: [os type] [os release] [distro release]
449+
*
450+
* E.g. "Linux 5.17.0-rc1 SLES 15.4"
451+
*
452+
* Return: Number of characters copied to @buffer.
453+
*/
454+
size_t get_entity_version(char *buffer, size_t bufsz);
455+
456+
/**
457+
* kv_strip - Strip blanks from key value string
458+
* @kv: The key-value string to strip
459+
*
460+
* Strip leading/trailing blanks as well as trailing comments from the
461+
* Key=Value string pointed to by @kv.
462+
*
463+
* Return: A pointer to the stripped string. Note that the original string,
464+
* @kv, gets modified.
465+
*/
466+
char *kv_strip(char *kv);
467+
468+
/**
469+
* kv_keymatch - Look for key in key value string
470+
* @kv: The key=value string to search for the presence of @key
471+
* @key: The key to look for
472+
*
473+
* Look for @key in the Key=Value pair pointed to by @k and return a
474+
* pointer to the Value if @key is found.
475+
*
476+
* Check if @kv starts with @key. If it does then make sure that we
477+
* have a whole-word match on the @key, and if we do, return a pointer
478+
* to the first character of value (i.e. skip leading spaces, tabs,
479+
* and equal sign)
480+
*
481+
* Return: A pointer to the first character of "value" if a match is found.
482+
* NULL otherwise.
483+
*/
484+
char *kv_keymatch(const char *kv, const char *key);
485+
486+
/**
487+
* startswith - Checks that a string starts with a given prefix.
488+
* @s: The string to check
489+
* @prefix: A string that @s could be starting with
490+
*
491+
* Return: If @s starts with @prefix, then return a pointer within @s at
492+
* the first character after the matched @prefix. NULL otherwise.
493+
*/
494+
char* startswith(const char *s, const char *prefix);
495+
496+
#define __round_mask(val, mult) ((__typeof__(val))((mult)-1))
497+
498+
/**
499+
* round_up - Round a value @val to the next multiple specified by @mult.
500+
* @val: Value to round
501+
* @mult: Multiple to round to.
502+
*
503+
* usage: int x = round_up(13, sizeof(__u32)); // 13 -> 16
504+
*/
505+
#define round_up(val, mult) ((((val)-1) | __round_mask((val), (mult)))+1)
506+
507+
/**
508+
* nvmf_exat_len() - Return length rounded up by 4
509+
* @val_len: Value lenght
510+
*
511+
* Return the size in bytes, rounded to a multiple of 4 (e.g., size of
512+
* __u32), of the buffer needed to hold the exat value of size
513+
* @val_len.
514+
*
515+
* Return: Lenght rounded up by 4
516+
*/
517+
static inline __u16 nvmf_exat_len(size_t val_len)
518+
{
519+
return (__u16)round_up(val_len, sizeof(__u32));
520+
}
521+
522+
/**
523+
* nvmf_exat_size - Return min algined size to hold value
524+
* @val_len: This is the length of the data to be copied to the "exatval"
525+
* field of a "struct nvmf_ext_attr".
526+
*
527+
* Return the size of the "struct nvmf_ext_attr" needed to hold
528+
* a value of size @val_len.
529+
*
530+
* Return: The size in bytes, rounded to a multiple of 4 (i.e. size of
531+
* __u32), of the "struct nvmf_ext_attr" required to hold a string of
532+
* length @val_len.
533+
*/
534+
static inline __u16 nvmf_exat_size(size_t val_len)
535+
{
536+
return (__u16)(sizeof(struct nvmf_ext_attr) + nvmf_exat_len(val_len));
537+
}
538+
539+
/**
540+
* nvmf_exat_ptr_next - Increment @p to the next element in the array.
541+
* @p: Pointer to an element of an array of "struct nvmf_ext_attr".
542+
*
543+
* Extended attributes are saved to an array of "struct nvmf_ext_attr"
544+
* where each element of the array is of variable size. In order to
545+
* move to the next element in the array one must increment the
546+
* pointer to the current element (@p) by the size of the current
547+
* element.
548+
*
549+
* Return: Pointer to the next element in the array.
550+
*/
551+
static inline struct nvmf_ext_attr *nvmf_exat_ptr_next(struct nvmf_ext_attr *p)
552+
{
553+
return (struct nvmf_ext_attr *)
554+
((uintptr_t)p + (ptrdiff_t)nvmf_exat_size(le16_to_cpu(p->exatlen)));
555+
}
556+
427557
#endif /* _LIBNVME_UTIL_H */

0 commit comments

Comments
 (0)