-static void nfs_read_stats_file (FILE *fh, char *inst)
-{
- char buffer[BUFSIZE];
-
- char plugin_instance[DATA_MAX_NAME_LEN];
-
- char *fields[48];
- int numfields = 0;
-
- if (fh == NULL)
- return;
-
- while (fgets (buffer, BUFSIZE, fh) != NULL)
- {
- numfields = strsplit (buffer, fields, 48);
-
- if (((numfields - 2) != nfs2_procedures_names_num)
- && ((numfields - 2)
- != nfs3_procedures_names_num))
- continue;
-
- if (strcmp (fields[0], "proc2") == 0)
- {
- int i;
- unsigned long long *values;
-
- if ((numfields - 2) != nfs2_procedures_names_num)
- {
- WARNING ("nfs plugin: Wrong "
- "number of fields (= %i) "
- "for NFSv2 statistics.",
- numfields - 2);
- continue;
- }
-
- ssnprintf (plugin_instance, sizeof (plugin_instance),
- "v2%s", inst);
-
- values = (unsigned long long *) malloc (nfs2_procedures_names_num * sizeof (unsigned long long));
- if (values == NULL)
- {
- char errbuf[1024];
- ERROR ("nfs plugin: malloc "
- "failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
- continue;
- }
-
- for (i = 0; i < nfs2_procedures_names_num; i++)
- values[i] = atoll (fields[i + 2]);
-
- nfs_procedures_submit (plugin_instance, values,
- nfs2_procedures_names,
- nfs2_procedures_names_num);
-
- free (values);
- }
- else if (strncmp (fields[0], "proc3", 5) == 0)
- {
- int i;
- unsigned long long *values;
-
- if ((numfields - 2) != nfs3_procedures_names_num)
- {
- WARNING ("nfs plugin: Wrong "
- "number of fields (= %i) "
- "for NFSv3 statistics.",
- numfields - 2);
- continue;
- }
-
- ssnprintf (plugin_instance, sizeof (plugin_instance),
- "v3%s", inst);
-
- values = (unsigned long long *) malloc (nfs3_procedures_names_num * sizeof (unsigned long long));
- if (values == NULL)
- {
- char errbuf[1024];
- ERROR ("nfs plugin: malloc "
- "failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
- continue;
- }
-
- for (i = 0; i < nfs3_procedures_names_num; i++)
- values[i] = atoll (fields[i + 2]);
-
- nfs_procedures_submit (plugin_instance, values,
- nfs3_procedures_names,
- nfs3_procedures_names_num);
-
- free (values);
- }
- } /* while (fgets (buffer, BUFSIZE, fh) != NULL) */
-} /* void nfs_read_stats_file */
-#undef BUFSIZE
-
-#if HAVE_LIBKSTAT && 0
-static void nfs2_read_kstat (kstat_t *ksp, char *inst)
-{
- unsigned long long values[18];
-
- values[0] = get_kstat_value (ksp, "null");
- values[1] = get_kstat_value (ksp, "getattr");
- values[2] = get_kstat_value (ksp, "setattr");
- values[3] = get_kstat_value (ksp, "root");
- values[4] = get_kstat_value (ksp, "lookup");
- values[5] = get_kstat_value (ksp, "readlink");
- values[6] = get_kstat_value (ksp, "read");
- values[7] = get_kstat_value (ksp, "wrcache");
- values[8] = get_kstat_value (ksp, "write");
- values[9] = get_kstat_value (ksp, "create");
- values[10] = get_kstat_value (ksp, "remove");
- values[11] = get_kstat_value (ksp, "rename");
- values[12] = get_kstat_value (ksp, "link");
- values[13] = get_kstat_value (ksp, "symlink");
- values[14] = get_kstat_value (ksp, "mkdir");
- values[15] = get_kstat_value (ksp, "rmdir");
- values[16] = get_kstat_value (ksp, "readdir");
- values[17] = get_kstat_value (ksp, "statfs");
-
- nfs2_procedures_submit (values, inst);
+#if KERNEL_LINUX
+static void nfs_submit_fields(int nfs_version, const char *instance,
+ char **fields, size_t fields_num,
+ const char **proc_names) {
+ char plugin_instance[DATA_MAX_NAME_LEN];
+ value_t values[fields_num];
+
+ snprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
+ instance);
+
+ for (size_t i = 0; i < fields_num; i++)
+ (void)parse_value(fields[i], &values[i], DS_TYPE_DERIVE);
+
+ nfs_procedures_submit(plugin_instance, proc_names, values, fields_num);
+}
+
+static int nfs_submit_fields_safe(int nfs_version, const char *instance,
+ char **fields, size_t fields_num,
+ const char **proc_names,
+ size_t proc_names_num) {
+ if (fields_num != proc_names_num) {
+ WARNING("nfs plugin: Wrong number of fields for "
+ "NFSv%i %s statistics. Expected %" PRIsz ", got %" PRIsz ".",
+ nfs_version, instance, proc_names_num, fields_num);
+ return EINVAL;
+ }
+
+ nfs_submit_fields(nfs_version, instance, fields, fields_num, proc_names);
+
+ return 0;
+}
+
+static int nfs_submit_nfs4_server(const char *instance, char **fields,
+ size_t fields_num) {
+ static int suppress_warning = 0;
+ size_t proc4x_names_num;
+
+ switch (fields_num) {
+ case NFS4_SERVER40_NUM_PROC:
+ case NFS4_SERVER40_NUM_PROC + 19: /* NFS 4.1 */
+ case NFS4_SERVER40_NUM_PROC + 31: /* NFS 4.2 */
+ case NFS4_SERVER40_NUM_PROC + 32: /* NFS 4.2 */
+ break;
+ default:
+ if (!suppress_warning) {
+ WARNING("nfs plugin: Unexpected number of fields for "
+ "NFSv4 %s statistics: %" PRIsz ". ",
+ instance, fields_num);
+ }
+
+ if (fields_num > NFS4_SERVER_MAX_PROC) {
+ fields_num = NFS4_SERVER_MAX_PROC;
+ suppress_warning = 1;
+ } else {
+ return EINVAL;
+ }
+ }
+
+ nfs_submit_fields(4, instance, fields, nfs4_server40_procedures_names_num,
+ nfs4_server40_procedures_names);
+
+ if (fields_num > nfs4_server40_procedures_names_num) {
+ proc4x_names_num = fields_num - nfs4_server40_procedures_names_num;
+ fields += nfs4_server40_procedures_names_num;
+
+ nfs_submit_fields(4, instance, fields, proc4x_names_num,
+ nfs4_server4x_procedures_names);
+ }
+
+ return 0;
+}
+
+static int nfs_submit_nfs4_client(const char *instance, char **fields,
+ size_t fields_num) {
+ size_t proc40_names_num, proc4x_names_num;
+
+ static int suppress_warning = 0;
+
+ switch (fields_num) {
+ case 34:
+ case 35:
+ case 36:
+ case 37:
+ case 38:
+ /* 4.0-only configuration */
+ proc40_names_num = fields_num;
+ break;
+ case 40:
+ case 41:
+ proc40_names_num = 35;
+ break;
+ case 42:
+ case 44:
+ proc40_names_num = 36;
+ break;
+ case 46:
+ case 47:
+ case 51:
+ case 53:
+ proc40_names_num = 37;
+ break;
+ case 54:
+ case 55:
+ case 57:
+ case 58:
+ case 59:
+ case 60:
+ proc40_names_num = 38;
+ break;
+ default:
+ if (!suppress_warning) {
+ WARNING("nfs plugin: Unexpected number of fields for NFSv4 %s "
+ "statistics: %" PRIsz ". ",
+ instance, fields_num);
+ }
+
+ if (fields_num > 34) {
+ /* safe fallback to basic nfs40 procedures */
+ fields_num = 34;
+ proc40_names_num = 34;
+
+ suppress_warning = 1;
+ } else {
+ return EINVAL;
+ }
+ }
+
+ nfs_submit_fields(4, instance, fields, proc40_names_num,
+ nfs4_client40_procedures_names);
+
+ if (fields_num > proc40_names_num) {
+ proc4x_names_num = fields_num - proc40_names_num;
+ fields += proc40_names_num;
+
+ nfs_submit_fields(4, instance, fields, proc4x_names_num,
+ nfs4_client4x_procedures_names);
+ }
+
+ return 0;
+}
+
+static void nfs_read_linux(FILE *fh, const char *inst) {
+ char buffer[1024];
+
+ char *fields[64];
+ int fields_num = 0;
+
+ if (fh == NULL)
+ return;
+
+ while (fgets(buffer, sizeof(buffer), fh) != NULL) {
+ fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
+
+ if (fields_num < 3)
+ continue;
+
+ if (strcmp(fields[0], "proc2") == 0 && report_v2) {
+ nfs_submit_fields_safe(/* version = */ 2, inst, fields + 2,
+ (size_t)(fields_num - 2), nfs2_procedures_names,
+ nfs2_procedures_names_num);
+ } else if (strncmp(fields[0], "proc3", 5) == 0 && report_v3) {
+ nfs_submit_fields_safe(/* version = */ 3, inst, fields + 2,
+ (size_t)(fields_num - 2), nfs3_procedures_names,
+ nfs3_procedures_names_num);
+ } else if (strcmp(fields[0], "proc4ops") == 0 && report_v4) {
+ if (inst[0] == 's')
+ nfs_submit_nfs4_server(inst, fields + 2, (size_t)(fields_num - 2));
+ } else if (strcmp(fields[0], "proc4") == 0 && report_v4) {
+ if (inst[0] == 'c')
+ nfs_submit_nfs4_client(inst, fields + 2, (size_t)(fields_num - 2));
+ }
+ } /* while (fgets) */
+} /* void nfs_read_linux */
+#endif /* KERNEL_LINUX */
+
+#if HAVE_LIBKSTAT
+static int nfs_read_kstat(kstat_t *ksp, int nfs_version, const char *inst,
+ char const **proc_names, size_t proc_names_num) {
+ char plugin_instance[DATA_MAX_NAME_LEN];
+ value_t values[proc_names_num];
+
+ if (ksp == NULL)
+ return EINVAL;
+
+ snprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
+ inst);
+
+ kstat_read(kc, ksp, NULL);
+ for (size_t i = 0; i < proc_names_num; i++) {
+ /* The name passed to kstat_data_lookup() doesn't have the
+ * "const" modifier, so we need to copy the name here. */
+ char name[32];
+ sstrncpy(name, proc_names[i], sizeof(name));
+
+ values[i].counter = (derive_t)get_kstat_value(ksp, name);
+ }
+
+ nfs_procedures_submit(plugin_instance, proc_names, values, proc_names_num);
+ return 0;