2
* nvidia-settings: A tool for configuring the NVIDIA X driver on Unix
5
* Copyright (C) 2013 NVIDIA Corporation.
7
* This program is free software; you can redistribute it and/or modify it
8
* under the terms and conditions of the GNU General Public License,
9
* version 2, as published by the Free Software Foundation.
11
* This program is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16
* You should have received a copy of the GNU General Public License
17
* along with this program. If not, see <http://www.gnu.org/licenses>.
21
* app-profiles.c - this source file contains functions for querying and
22
* assigning application profile settings, as well as parsing and saving
23
* application profile configuration files.
32
#include <sys/types.h>
39
#include "common-utils.h"
40
#include "app-profiles.h"
43
static char *slurp(FILE *fp)
46
char *text = strdup("");
50
while (text && !eof) {
51
line = fget_next_line(fp, &eof);
53
new_text = nvstrcat(text, "\n", line, NULL);
64
static void splice_string(char **s, size_t b, size_t e, const char *replace)
66
char *tail = strdup(*s + e);
67
*s = realloc(*s, b + strlen(replace) + strlen(tail) + 1);
71
sprintf(*s + b, "%s%s", replace, tail);
75
#define HEX_DIGITS "0123456789abcdefABCDEF"
77
char *nv_app_profile_cfg_file_syntax_to_json(const char *orig_s)
79
char *s = strdup(orig_s);
83
size_t start, end, size;
84
unsigned long long val;
85
char *old_substr = NULL;
87
char *new_substr = NULL;
90
while ((tok = strpbrk(tok, "\\\"#" HEX_DIGITS))) {
107
char *end_tok = nvstrchrnul(tok, '\n');
110
splice_string(&s, start, end, "");
119
case '0': case '1': case '2': case '3': case '4':
120
case '5': case '6': case '7': case '8': case '9':
121
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
122
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
124
size = strspn(tok, "Xx." HEX_DIGITS);
125
if ((tok[0] == '0') &&
126
(tok[1] == 'x' || tok[1] == 'X' || isdigit(tok[1])) &&
128
old_substr = nvstrndup(tok, size);
133
val = strtoull(old_substr, &endptr, 0);
134
if (errno || (endptr - old_substr != strlen(old_substr))) {
135
// Invalid conversion, skip this string
137
free(old_substr); old_substr = NULL;
139
new_substr = nvasprintf("%llu", val);
144
end = tok - s + size;
145
splice_string(&s, start, end, new_substr);
146
free(new_substr); new_substr = NULL;
147
free(old_substr); old_substr = NULL;
152
// Not hex or octal; let the JSON parser deal with it
157
assert(!"Unhandled character");
173
static int open_and_stat(const char *filename, const char *perms, FILE **fp, struct stat *stat_buf)
176
*fp = fopen(filename, perms);
178
if (errno != ENOENT) {
179
nv_error_msg("Could not open file %s (%s)", filename, strerror(errno));
184
ret = fstat(fileno(*fp), stat_buf);
186
nv_error_msg("Could not stat file %s (%s)", filename, strerror(errno));
192
static char *nv_dirname(const char *path)
194
char *last_slash = strrchr(path, '/');
196
return nvstrndup(path, last_slash - path);
198
return nvstrdup(".");
202
static json_t *app_profile_config_insert_file_object(AppProfileConfig *config, json_t *new_file)
204
json_t *json_filename, *json_new_filename;
206
const char *filename, *new_filename;
208
json_t *order, *file_order;
209
size_t new_file_major, new_file_minor, file_order_major, file_order_minor;
213
json_new_filename = json_object_get(new_file, "filename");
215
assert(json_new_filename);
216
new_filename = json_string_value(json_new_filename);
218
assert(nv_app_profile_config_check_valid_source_file(config,
222
// Determine the correct location of the file in the search path
226
for (i = 0; i < config->search_path_count; i++) {
227
if (!strcmp(new_filename, config->search_path[i])) {
232
dirname = nv_dirname(new_filename);
234
if (!strcmp(dirname, config->search_path[i])) {
243
num_files = json_array_size(config->parsed_files);
245
for (i = 0; i < num_files; i++) {
246
file = json_array_get(config->parsed_files, i);
247
file_order = json_object_get(file, "order");
248
file_order_major = json_integer_value(json_object_get(file_order, "major"));
249
file_order_minor = json_integer_value(json_object_get(file_order, "minor"));
250
json_filename = json_object_get(file, "filename");
251
assert(json_filename);
252
filename = json_string_value(json_filename);
253
if (file_order_major < new_file_major) {
254
} else if (file_order_major == new_file_major) {
255
if (strcoll(filename, new_filename) > 0) {
264
// Mark the order of the file
265
order = json_object_get(new_file, "order");
266
json_object_set_new(order, "major", json_integer(new_file_major));
267
json_object_set_new(order, "minor", json_integer(new_file_minor));
270
json_array_insert(config->parsed_files, i, new_file);
272
// Bump up minor for files after this one with the same major
273
num_files = json_array_size(config->parsed_files);
275
for ( ; i < num_files; i++) {
276
file = json_array_get(config->parsed_files, i);
277
file_order = json_object_get(file, "order");
278
file_order_major = json_integer_value(json_object_get(order, "major"));
279
file_order_minor = json_integer_value(json_object_get(order, "minor"));
280
if (file_order_major > new_file_major) {
283
json_object_set_new(file_order, "minor", json_integer(file_order_minor+1));
291
* Create a new empty file object and adds it to the configuration.
293
static json_t *app_profile_config_new_file(AppProfileConfig *config,
294
const char *filename)
296
json_t *new_file = json_object();
298
json_object_set_new(new_file, "filename", json_string(filename));
299
json_object_set_new(new_file, "rules", json_array());
300
json_object_set_new(new_file, "profiles", json_object());
301
json_object_set_new(new_file, "dirty", json_false());
302
json_object_set_new(new_file, "new", json_true());
303
// order is set by app_profile_config_insert_file_object() below
305
new_file = app_profile_config_insert_file_object(config, new_file);
310
static char *rule_id_to_key_string(int id)
313
key = nvasprintf("%d", id);
318
* Constructs a profile name that is guaranteed to be unique to this
319
* configuration. This is used to handle the case where there are multiple
320
* profiles with the same name (an invalid configuration).
322
static char *app_profile_config_unique_profile_name(AppProfileConfig *config,
323
const char *orig_name,
324
const char *filename,
328
json_t *json_gold_filename = json_object_get(config->profile_locations, orig_name);
330
if (json_gold_filename) {
332
char *new_name = NULL;
335
new_name = nvasprintf("%s_duplicate_%d", orig_name, i++);
336
} while (new_name && json_object_get(config->profile_locations, new_name));
338
nv_error_msg("The profile \"%s\" in the file \"%s\" has the same name "
339
"as a profile defined in the file \"%s\", and will be renamed to \"%s\".",
340
orig_name, filename, json_string_value(json_gold_filename), new_name);
347
return strdup(orig_name);
352
char *nv_app_profile_config_get_unused_profile_name(AppProfileConfig *config)
354
char *temp_name, *unique_name;
357
temp_name = nvasprintf("profile_%x", salt);
358
unique_name = app_profile_config_unique_profile_name(config,
368
static json_t *json_settings_parse(json_t *old_settings, const char *filename)
370
int uses_setting_objects;
373
json_t *new_settings, *new_setting;
374
json_t *json_key, *json_value;
376
if (!json_is_array(old_settings)) {
380
new_settings = json_array();
382
uses_setting_objects = json_array_size(old_settings) &&
383
json_is_object(json_array_get(old_settings, 0));
385
for (i = 0, size = json_array_size(old_settings); i < size; ) {
386
old_setting = json_array_get(old_settings, i++);
387
if (uses_setting_objects) {
388
json_key = json_object_get(old_setting, "key");
390
json_key = json_object_get(old_setting, "k");
392
json_value = json_object_get(old_setting, "value");
394
json_value = json_object_get(old_setting, "v");
398
nv_error_msg("App profile parse error in %s: Key/value array of odd length\n", filename);
399
json_decref(new_settings);
402
json_key = old_setting;
403
json_value = json_array_get(old_settings, i++);
406
if (!json_is_string(json_key)) {
407
nv_error_msg("App profile parse error in %s: Invalid key detected in settings array\n", filename);
408
json_decref(new_settings);
412
if (!json_is_integer(json_value) &&
413
!json_is_real(json_value) &&
414
!json_is_true(json_value) &&
415
!json_is_false(json_value) &&
416
!json_is_string(json_value)) {
417
nv_error_msg("App profile parse error in %s: Invalid value detected in settings array\n", filename);
418
json_decref(new_settings);
421
new_setting = json_object();
422
json_object_set(new_setting, "key", json_key);
423
json_object_set(new_setting, "value", json_value);
424
json_array_append_new(new_settings, new_setting);
431
* Load app profile settings from an already-open file. This operation is
432
* atomic: either all of the settings from the file are added to the
433
* configuration, or none are.
435
static void app_profile_config_load_file(AppProfileConfig *config,
436
const char *filename,
437
struct stat *stat_buf,
440
char *json_text = NULL;
441
char *orig_text = NULL;
444
json_t *orig_file = NULL;
445
json_t *orig_json_profiles, *orig_json_rules;
446
int next_free_rule_id = config->next_free_rule_id;
448
json_t *new_file = NULL;
449
json_t *new_json_profiles = NULL;
450
json_t *new_json_rules = NULL;
452
if (!S_ISREG(stat_buf->st_mode)) {
453
// Silently ignore all but regular files
457
orig_text = slurp(fp);
460
nv_error_msg("Could not read from file %s", filename);
464
// Convert the file contents to JSON
465
json_text = nv_app_profile_cfg_file_syntax_to_json(orig_text);
468
nv_error_msg("App profile parse error in %s: text is not valid app profile configuration syntax", filename);
472
new_file = json_object();
474
json_object_set_new(new_file, "dirty", json_false());
475
json_object_set_new(new_file, "filename", json_string(filename));
477
new_json_profiles = json_object();
478
new_json_rules = json_array();
480
// Parse the resulting JSON
481
orig_file = json_loads(json_text, 0, &error);
484
nv_error_msg("App profile parse error in %s: %s on %s, line %d\n",
485
filename, error.text, error.source, error.line);
489
if (!json_is_object(orig_file)) {
490
nv_error_msg("App profile parse error in %s: top-level config not an object!\n", filename);
494
orig_json_profiles = json_object_get(orig_file, "profiles");
496
if (orig_json_profiles) {
498
* Note: we store profiles internally as members of an object, but the
499
* config file syntax uses an array to store profiles.
501
if (!json_is_array(orig_json_profiles)) {
502
nv_error_msg("App profile parse error in %s: profiles value is not an array\n", filename);
506
size = json_array_size(orig_json_profiles);
507
for (i = 0; i < size; i++) {
508
const char *new_name;
509
json_t *orig_json_profile, *orig_json_name, *orig_json_settings;
510
json_t *new_json_profile, *new_json_settings;
512
new_json_profile = json_object();
514
orig_json_profile = json_array_get(orig_json_profiles, i);
515
if (!json_is_object(orig_json_profile)) {
519
orig_json_name = json_object_get(orig_json_profile, "name");
520
if (!json_is_string(orig_json_name)) {
524
orig_json_settings = json_object_get(orig_json_profile, "settings");
525
new_json_settings = json_settings_parse(orig_json_settings, filename);
526
if (!new_json_settings) {
530
new_name = app_profile_config_unique_profile_name(config,
531
json_string_value(orig_json_name),
535
json_object_set_new(new_json_profile, "settings", new_json_settings);
537
json_object_set_new(new_json_profiles, new_name, new_json_profile);
541
orig_json_rules = json_object_get(orig_file, "rules");
543
if (orig_json_rules) {
544
if (!json_is_array(orig_json_rules)) {
545
nv_error_msg("App profile parse error in %s: rules value is not an array\n", filename);
549
size = json_array_size(orig_json_rules);
550
for (i = 0; i < size; i++) {
553
json_t *orig_json_rule, *orig_json_pattern, *orig_json_profile;
554
json_t *new_json_rule, *new_json_pattern;
555
orig_json_rule = json_array_get(orig_json_rules, i);
557
if (!json_is_object(orig_json_rule)) {
561
new_id = next_free_rule_id++;
563
new_json_rule = json_object();
564
new_json_pattern = json_object();
566
orig_json_pattern = json_object_get(orig_json_rule, "pattern");
567
if (json_is_object(orig_json_pattern)) {
569
json_t *orig_json_feature, *orig_json_matches;
570
orig_json_feature = json_object_get(orig_json_pattern, "feature");
571
if (!json_is_string(orig_json_feature)) {
572
json_decref(new_json_rule);
573
json_decref(new_json_pattern);
576
orig_json_matches = json_object_get(orig_json_pattern, "matches");
577
if (!json_is_string(orig_json_matches)) {
578
json_decref(new_json_rule);
579
json_decref(new_json_pattern);
582
json_object_set(new_json_pattern, "feature", orig_json_feature);
583
json_object_set(new_json_pattern, "matches", orig_json_matches);
584
} else if (json_is_string(orig_json_pattern)) {
586
json_object_set_new(new_json_pattern, "feature", json_string("procname"));
587
json_object_set(new_json_pattern, "matches", orig_json_pattern);
589
json_decref(new_json_rule);
590
json_decref(new_json_pattern);
594
json_object_set_new(new_json_rule, "pattern", new_json_pattern);
596
orig_json_profile = json_object_get(orig_json_rule, "profile");
597
if (json_is_object(orig_json_profile) || json_is_array(orig_json_profile)) {
598
// inline profile object
599
json_t *new_json_profile;
600
json_t *orig_json_settings, *orig_json_name;
601
json_t *new_json_settings;
603
if (json_is_object(orig_json_profile)) {
604
orig_json_settings = json_object_get(orig_json_profile, "settings");
605
orig_json_name = json_object_get(orig_json_profile, "name");
608
orig_json_settings = orig_json_profile;
609
orig_json_name = NULL;
612
if (json_is_string(orig_json_name)) {
613
profile_name = app_profile_config_unique_profile_name(config,
614
json_string_value(orig_json_name),
618
} else if (!orig_json_name) {
619
char *profile_name_template;
620
profile_name_template = nvasprintf("inline_%d", new_id);
621
profile_name = app_profile_config_unique_profile_name(config,
622
profile_name_template,
626
free(profile_name_template);
628
json_decref(new_json_rule);
632
new_json_settings = json_settings_parse(orig_json_settings, filename);
634
if (!profile_name || !new_json_settings) {
636
json_decref(new_json_settings);
637
json_decref(new_json_rule);
641
new_json_profile = json_object();
642
json_object_set_new(new_json_profile, "settings", new_json_settings);
644
json_object_set_new(new_json_profiles, profile_name, new_json_profile);
645
} else if (json_is_string(orig_json_profile)) {
647
profile_name = strdup(json_string_value(orig_json_profile));
649
json_decref(new_json_rule);
653
json_object_set_new(new_json_rule, "profile", json_string(profile_name));
656
json_object_set_new(new_json_rule, "id", json_integer(new_id));
658
json_array_append_new(new_json_rules, new_json_rule);
662
json_object_set(new_file, "profiles", new_json_profiles);
663
json_object_set(new_file, "rules", new_json_rules);
664
json_object_set_new(new_file, "dirty", dirty ? json_true() : json_false());
665
json_object_set_new(new_file, "new", json_false());
667
// Don't use the atime in the stat_buf; instead measure it here
668
json_object_set_new(new_file, "atime", json_integer(time(NULL)));
670
// If we didn't fail anywhere above, add the file to our configuration
671
app_profile_config_insert_file_object(config, new_file);
673
// Add the profiles in this file to the global profiles list
677
json_object_foreach(new_json_profiles, key, value) {
678
json_object_set_new(config->profile_locations, key, json_string(filename));
682
// Add the rules in this file to the global rules list
683
size = json_array_size(new_json_rules);
684
for (i = 0; i < size; i++) {
688
new_rule = json_array_get(new_json_rules, i);
689
key = rule_id_to_key_string(json_integer_value(json_object_get(new_rule, "id")));
690
json_object_set_new(config->rule_locations, key, json_string(filename));
693
config->next_free_rule_id = next_free_rule_id;
696
json_decref(orig_file);
697
json_decref(new_file);
698
json_decref(new_json_rules);
699
json_decref(new_json_profiles);
704
// Load app profile settings from a directory
705
static void app_profile_config_load_files_from_directory(AppProfileConfig *config,
709
struct stat stat_buf;
710
struct dirent **namelist;
713
n = scandir(dirname, &namelist, NULL, alphasort);
716
nv_error_msg("Failed to open directory \"%s\"", dirname);
720
for (i = 0; i < n; i++) {
721
char *d_name = namelist[i]->d_name;
725
if ((d_name[0] == '.') &&
726
((d_name[1] == '\0') ||
727
((d_name[1] == '.') && (d_name[2] == '\0')))) {
731
full_path = nvstrcat(dirname, "/", d_name, NULL);
732
ret = open_and_stat(full_path, "r", &fp, &stat_buf);
740
app_profile_config_load_file(config,
752
static json_t *app_profile_config_load_global_options(const char *global_config_file)
755
json_t *options = json_object();
758
struct stat stat_buf;
760
json_t *options_from_file;
763
// By default, app profiles are enabled
764
json_object_set_new(options, "enabled", json_true());
766
if (!global_config_file) {
770
ret = open_and_stat(global_config_file, "r", &fp, &stat_buf);
771
if ((ret < 0) || !S_ISREG(stat_buf.st_mode)) {
775
option_text = slurp(fp);
778
options_from_file = json_loads(option_text, 0, &error);
781
if (!options_from_file) {
782
nv_error_msg("App profile parse error in %s: %s on %s, line %d\n",
783
global_config_file, error.text, error.source, error.line);
787
// Load the "enabled" option
788
option = json_object_get(options_from_file, "enabled");
789
if (option && (json_is_true(option) || json_is_false(option))) {
790
json_object_set(options, "enabled", option);
793
json_decref(options_from_file);
798
AppProfileConfig *nv_app_profile_config_load(const char *global_config_file,
800
size_t search_path_count)
803
AppProfileConfig *config = malloc(sizeof(AppProfileConfig));
809
// Initialize the config
810
config->next_free_rule_id = 0;
812
config->parsed_files = json_array();
813
config->profile_locations = json_object();
814
config->rule_locations = json_object();
816
if (global_config_file) {
817
config->global_config_file = nvstrdup(global_config_file);
819
config->global_config_file = NULL;
822
config->global_options = app_profile_config_load_global_options(global_config_file);
824
config->search_path = malloc(sizeof(char *) * search_path_count);
825
config->search_path_count = search_path_count;
827
for (i = 0; i < search_path_count; i++) {
828
config->search_path[i] = strdup(search_path[i]);
831
for (i = 0; i < search_path_count; i++) {
833
struct stat stat_buf;
834
const char *filename = search_path[i];
837
ret = open_and_stat(filename, "r", &fp, &stat_buf);
842
if (S_ISDIR(stat_buf.st_mode)) {
843
// Parse files in the directory
845
app_profile_config_load_files_from_directory(config, filename);
847
// Load the individual file
848
app_profile_config_load_file(config, filename, &stat_buf, fp);
857
static int file_in_search_path(AppProfileConfig *config, const char *filename)
860
for (i = 0; i < config->search_path_count; i++) {
861
if (!strcmp(filename, config->search_path[i])) {
869
// Print an error message and optionally capture the error string for later use
870
// Note: this assumes fmt is a string literal!
871
#define LOG_ERROR(error_str, fmt, ...) do { \
873
nv_append_sprintf(error_str, fmt "\n", __VA_ARGS__); \
875
nv_error_msg(fmt, __VA_ARGS__); \
879
* Creates parent directories as needed, similarly to "mkdir -p"
881
static int nv_mkdirp(const char *dirname, char **error_str)
885
const char *cur, *next;
886
struct stat stat_buf;
889
while (*cur && (next = strchr(cur + 1, '/'))) {
890
parent_name = nvstrndup(dirname, next - dirname);
891
ret = mkdir(parent_name, 0777);
892
if ((ret < 0) && (errno != EEXIST)) {
894
"Could not create parent directory \"%s\" "
895
"for full path \"%s\" (%s)",
896
parent_name, dirname, strerror(errno));
904
ret = mkdir(dirname, 0777);
906
if (errno != EEXIST) {
907
LOG_ERROR(error_str, "Could not create directory \"%s\" (%s)",
908
dirname, strerror(errno));
910
ret = stat(dirname, &stat_buf);
912
if (!S_ISDIR(stat_buf.st_mode)) {
913
LOG_ERROR(error_str, "Could not create directory \"%s\" "
914
"(file exists, but not as a directory)",
925
char *nv_app_profile_config_get_backup_filename(AppProfileConfig *config, const char *filename)
927
char *basename = NULL;
928
char *dirname = NULL;
929
char *backup_name = NULL;
931
if ((config->global_config_file &&
932
!strcmp(config->global_config_file, filename)) ||
933
file_in_search_path(config, filename)) {
934
// Files in the top-level search path, and the global config file, can
935
// be renamed from "$FILE" to "$FILE.backup" without affecting the
937
backup_name = nvasprintf("%s.backup", filename);
939
// Files in a search path directory *cannot* be renamed from "$FILE" to
940
// "$FILE.backup" without affecting the configuration due to the search
941
// path rules. Instead, attempt to move them to a subdirectory called
943
dirname = nv_dirname(filename);
944
basename = nv_basename(filename);
945
assert(file_in_search_path(config, dirname));
946
backup_name = nvasprintf("%s/.backup/%s", dirname, basename);
954
static int app_profile_config_backup_file(AppProfileConfig *config,
955
const char *filename,
959
char *backup_name = nv_app_profile_config_get_backup_filename(config, filename);
960
char *backup_dirname = nv_dirname(backup_name);
962
ret = nv_mkdirp(backup_dirname, error_str);
964
LOG_ERROR(error_str, "Could not create backup directory \"%s\" (%s)",
965
backup_name, strerror(errno));
969
ret = rename(filename, backup_name);
971
if (errno == ENOENT) {
972
// Clear the error; the file does not exist
975
LOG_ERROR(error_str, "Could not rename file \"%s\" to \"%s\" for backup (%s)",
976
filename, backup_name, strerror(errno));
980
nv_info_msg("", "Backing up configuration file \"%s\" as \"%s\"\n", filename, backup_name);
983
free(backup_dirname);
989
static int app_profile_config_save_updates_to_file(AppProfileConfig *config,
990
const char *filename,
991
const char *update_text,
995
int file_is_new = FALSE;
996
struct stat stat_buf;
997
char *dirname = NULL;
1001
ret = stat(filename, &stat_buf);
1003
if ((ret < 0) && (errno != ENOENT)) {
1004
LOG_ERROR(error_str, "Could not stat file \"%s\" (%s)",
1005
filename, strerror(errno));
1007
} else if ((ret < 0) && (errno == ENOENT)) {
1009
// Check if the prefix is in the search path
1010
dirname = nv_dirname(filename);
1012
if (file_in_search_path(config, dirname)) {
1013
// This file is in a directory in the search path
1014
ret = stat(dirname, &stat_buf);
1015
if ((ret < 0) && (errno != ENOENT)) {
1016
LOG_ERROR(error_str, "Could not stat file \"%s\" (%s)",
1017
dirname, strerror(errno));
1019
} else if ((ret < 0) && errno == ENOENT) {
1020
// Attempt to create the directory in the search path
1021
ret = nv_mkdirp(dirname, error_str);
1025
} else if (S_ISREG(stat_buf.st_mode)) {
1026
// If the search path entry is currently a regular file,
1027
// unlink it and create a directory instead
1029
ret = app_profile_config_backup_file(config, dirname,
1035
ret = unlink(dirname);
1037
LOG_ERROR(error_str,
1038
"Could not remove the file \"%s\" (%s)",
1039
dirname, strerror(errno));
1042
ret = nv_mkdirp(dirname, error_str);
1048
// Attempt to create parent directories for this file
1049
ret = nv_mkdirp(dirname, error_str);
1054
} else if (!S_ISREG(stat_buf.st_mode)) {
1055
// XXX: if this is a directory, we could recursively remove files here,
1056
// but that seems a little dangerous. Instead, complain and bail out
1059
LOG_ERROR(error_str,
1060
"Refusing to write to file \"%s\" "
1061
"since it is not a regular file", filename);
1065
if (!file_is_new && backup) {
1066
ret = app_profile_config_backup_file(config, filename,
1072
ret = open_and_stat(filename, "w", &fp, &stat_buf);
1074
LOG_ERROR(error_str, "Could not write to the file \"%s\" (%s)",
1075
filename, strerror(errno));
1078
nv_info_msg("", "Writing to configuration file \"%s\"\n", filename);
1079
fprintf(fp, "%s\n", update_text);
1087
int nv_app_profile_config_save_updates(AppProfileConfig *config,
1093
const char *filename;
1094
const char *update_text;
1103
for (i = 0, size = json_array_size(updates); i < size; i++) {
1104
update = json_array_get(updates, i);
1105
filename = json_string_value(json_object_get(update, "filename"));
1106
update_text = json_string_value(json_object_get(update, "text"));
1107
ret = app_profile_config_save_updates_to_file(config,
1117
assert(all_ret <= 0);
1119
// This asserts an error string is set iff we are returning an error
1120
assert(!error_str ||
1121
(!(*error_str) && (all_ret == 0)) ||
1122
((*error_str) && (all_ret < 0)));
1127
AppProfileConfig *nv_app_profile_config_dup(AppProfileConfig *config)
1130
AppProfileConfig *new_config;
1132
new_config = malloc(sizeof(AppProfileConfig));
1133
new_config->parsed_files = json_deep_copy(config->parsed_files);
1134
new_config->profile_locations = json_deep_copy(config->profile_locations);
1135
new_config->rule_locations = json_deep_copy(config->rule_locations);
1136
new_config->next_free_rule_id = config->next_free_rule_id;
1138
new_config->global_config_file =
1139
config->global_config_file ? strdup(config->global_config_file) : NULL;
1140
new_config->global_options = json_deep_copy(config->global_options);
1142
new_config->search_path = malloc(sizeof(char *) * config->search_path_count);
1143
new_config->search_path_count = config->search_path_count;
1145
for (i = 0; i < config->search_path_count; i++) {
1146
new_config->search_path[i] = strdup(config->search_path[i]);
1152
void nv_app_profile_config_set_enabled(AppProfileConfig *config,
1155
json_t *global_options = config->global_options;
1157
json_object_set_new(global_options, "enabled",
1158
enabled ? json_true() : json_false());
1161
int nv_app_profile_config_get_enabled(AppProfileConfig *config)
1163
json_t *global_options = config->global_options;
1164
json_t *enabled_json;
1166
enabled_json = json_object_get(global_options, "enabled");
1167
assert(enabled_json);
1169
return json_is_true(enabled_json);
1172
void nv_app_profile_config_free(AppProfileConfig *config)
1175
json_decref(config->global_options);
1176
json_decref(config->parsed_files);
1177
json_decref(config->profile_locations);
1178
json_decref(config->rule_locations);
1180
for (i = 0; i < config->search_path_count; i++) {
1181
free(config->search_path[i]);
1184
free(config->search_path);
1185
free(config->global_config_file);
1190
static json_t *app_profile_config_lookup_file(AppProfileConfig *config, const char *filename)
1193
json_t *json_file, *json_filename;
1195
size = json_array_size(config->parsed_files);
1197
for (i = 0; i < size; i++) {
1198
json_file = json_array_get(config->parsed_files, i);
1199
json_filename = json_object_get(json_file, "filename");
1200
if (!strcmp(json_string_value(json_filename), filename)) {
1208
static void app_profile_config_delete_file(AppProfileConfig *config, const char *filename)
1211
json_t *json_file, *json_filename;
1213
size = json_array_size(config->parsed_files);
1215
for (i = 0; i < size; i++) {
1216
json_file = json_array_get(config->parsed_files, i);
1217
json_filename = json_object_get(json_file, "filename");
1218
if (!strcmp(json_string_value(json_filename), filename)) {
1219
json_array_remove(config->parsed_files, i);
1225
static void app_profile_config_get_per_file_config(AppProfileConfig *config,
1226
const char *filename,
1231
*file = app_profile_config_lookup_file(config, filename);
1237
*rules = json_object_get(*file, "rules");
1238
*profiles = json_object_get(*file, "profiles");
1243
* Convert the internal representation of an application profile to a
1244
* representation suitable for writing to disk.
1246
static json_t *app_profile_config_profile_output(const char *profile_name, const json_t *orig_profile)
1248
json_t *new_profile = json_object();
1250
json_object_set_new(new_profile, "name", json_string(profile_name));
1251
json_object_set(new_profile, "settings", json_object_get(orig_profile, "settings"));
1256
static json_t *app_profile_config_rule_output(const json_t *orig_rule)
1258
json_t *new_rule = json_object();
1260
json_object_set(new_rule, "pattern", json_object_get(orig_rule, "pattern"));
1261
json_object_set(new_rule, "profile", json_object_get(orig_rule, "profile"));
1266
static char *config_to_cfg_file_syntax(json_t *old_rules, json_t *old_profiles)
1268
char *output = NULL;
1269
const char *profile_name;
1270
json_t *root, *rules_array, *profiles_array;
1271
json_t *old_rule, *old_profile;
1272
json_t *new_rule, *new_profile;
1275
root = json_object();
1280
rules_array = json_array();
1284
json_object_set_new(root, "rules", rules_array);
1286
profiles_array = json_array();
1287
if (!profiles_array) {
1290
json_object_set_new(root, "profiles", profiles_array);
1293
size = json_array_size(old_rules);
1294
for (i = 0; i < size; i++) {
1295
old_rule = json_array_get(old_rules, i);
1296
new_rule = app_profile_config_rule_output(old_rule);
1297
json_array_append_new(rules_array, new_rule);
1302
json_object_foreach(old_profiles, profile_name, old_profile) {
1303
new_profile = app_profile_config_profile_output(profile_name, old_profile);
1304
json_array_append_new(profiles_array, new_profile);
1308
output = json_dumps(root, JSON_ENSURE_ASCII | JSON_INDENT(4));
1315
static void add_files_from_config(AppProfileConfig *config, json_t *all_files, json_t *changed_files)
1317
json_t *file, *filename;
1319
for (i = 0, size = json_array_size(config->parsed_files); i < size; i++) {
1320
file = json_array_get(config->parsed_files, i);
1321
filename = json_object_get(file, "filename");
1322
json_object_set_new(all_files, json_string_value(filename), json_true());
1323
if (json_is_true(json_object_get(file, "dirty"))) {
1324
json_object_set_new(changed_files, json_string_value(filename), json_true());
1329
static json_t *app_profile_config_validate_global_options(AppProfileConfig *new_config,
1330
AppProfileConfig *old_config)
1332
json_t *update = NULL;
1335
assert((!new_config->global_config_file && !old_config->global_config_file) ||
1336
(!strcmp(new_config->global_config_file, old_config->global_config_file)));
1338
if (new_config->global_config_file &&
1339
!json_equal(new_config->global_options, old_config->global_options)) {
1340
update = json_object();
1341
json_object_set_new(update, "filename", json_string(new_config->global_config_file));
1342
option_text = json_dumps(new_config->global_options, JSON_ENSURE_ASCII | JSON_INDENT(4));
1343
json_object_set_new(update, "text", json_string(option_text));
1350
json_t *nv_app_profile_config_validate(AppProfileConfig *new_config,
1351
AppProfileConfig *old_config)
1353
json_t *all_files, *changed_files;
1354
json_t *new_file, *new_rules, *old_rules;
1355
json_t *old_file, *new_profiles, *old_profiles;
1356
json_t *updates, *update;
1357
const char *filename;
1361
updates = json_array();
1363
// Determine if the global config file needs to be updated
1364
update = app_profile_config_validate_global_options(new_config, old_config);
1366
json_array_append_new(updates, update);
1369
// Build a set of files to examine: this is the union of files specified
1370
// by the old configuration and the new.
1371
all_files = json_object();
1372
changed_files = json_object();
1373
add_files_from_config(new_config, all_files, changed_files);
1374
add_files_from_config(old_config, all_files, changed_files);
1376
// For each file in the set, determine if it needs to be updated
1377
json_object_foreach(all_files, filename, unused) {
1378
app_profile_config_get_per_file_config(new_config, filename, &new_file, &new_rules, &new_profiles);
1379
app_profile_config_get_per_file_config(old_config, filename, &old_file, &old_rules, &old_profiles);
1381
// Simply compare the JSON objects
1382
if (!json_equal(old_rules, new_rules) || !json_equal(old_profiles, new_profiles)) {
1383
json_object_set_new(changed_files, filename, json_true());
1387
// For each file that changed, generate an update record with the new JSON
1388
json_object_foreach(changed_files, filename, unused) {
1389
update = json_object();
1391
json_object_set_new(update, "filename", json_string(filename));
1392
app_profile_config_get_per_file_config(new_config, filename, &new_file, &new_rules, &new_profiles);
1394
update_text = config_to_cfg_file_syntax(new_rules, new_profiles);
1395
json_object_set_new(update, "text", json_string(update_text));
1397
json_array_append_new(updates, update);
1401
json_decref(all_files);
1402
json_decref(changed_files);
1407
static int file_object_is_empty(const json_t *file)
1409
const json_t *rules;
1410
const json_t *profiles;
1412
rules = json_object_get(file, "rules");
1413
profiles = json_object_get(file, "profiles");
1415
return (!json_array_size(rules) && !json_object_size(profiles));
1419
* Checks whether the given file is "empty" (contains no rules and profiles)
1420
* and "new" (created in the configuration and not loaded from disk), and
1421
* removes it from the configuration if both criteria are satisfied.
1423
static void app_profile_config_prune_empty_file(AppProfileConfig *config, const json_t *file)
1426
if (json_is_true(json_object_get(file, "new")) && file_object_is_empty(file)) {
1427
filename = strdup(json_string_value(json_object_get(file, "filename")));
1428
app_profile_config_delete_file(config, filename);
1433
int nv_app_profile_config_update_profile(AppProfileConfig *config,
1434
const char *filename,
1435
const char *profile_name,
1436
json_t *new_profile)
1439
json_t *old_file = NULL;
1440
json_t *file_profiles;
1441
const char *old_filename;
1443
old_filename = json_string_value(json_object_get(config->profile_locations, profile_name));
1447
old_file = app_profile_config_lookup_file(config, old_filename);
1451
// If there is an existing profile with a differing filename, delete it first
1452
if (old_filename && (strcmp(filename, old_filename) != 0)) {
1453
file = app_profile_config_lookup_file(config, old_filename);
1454
file_profiles = json_object_get(file, "profiles");
1456
json_object_del(file_profiles, profile_name);
1460
file = app_profile_config_lookup_file(config, filename);
1462
file = app_profile_config_new_file(config, filename);
1465
file_profiles = json_object_get(file, "profiles");
1466
json_object_set(file_profiles, profile_name, new_profile);
1467
json_object_set(config->profile_locations, profile_name, json_string(filename));
1470
app_profile_config_prune_empty_file(config, old_file);
1473
return !old_filename;
1476
void nv_app_profile_config_delete_profile(AppProfileConfig *config,
1477
const char *profile_name)
1479
json_t *file = NULL;
1480
const char *filename = json_string_value(json_object_get(config->profile_locations, profile_name));
1483
file = app_profile_config_lookup_file(config, filename);
1485
json_object_del(json_object_get(file, "profiles"), profile_name);
1489
json_object_del(config->profile_locations, profile_name);
1492
app_profile_config_prune_empty_file(config, file);
1496
int nv_app_profile_config_create_rule(AppProfileConfig *config,
1497
const char *filename,
1501
json_t *file, *file_rules;
1502
json_t *new_rule_copy;
1505
file = app_profile_config_lookup_file(config, filename);
1507
file = app_profile_config_new_file(config, filename);
1510
file_rules = json_object_get(file, "rules");
1512
// Add the rule to the head of the per-file list
1513
json_array_append(file_rules, new_rule);
1514
new_rule_copy = json_array_get(file_rules, json_array_size(file_rules) - 1);
1516
new_id = config->next_free_rule_id++;
1517
json_object_set_new(new_rule_copy, "id", json_integer(new_id));
1519
key = rule_id_to_key_string(new_id);
1520
json_object_set(config->rule_locations, key, json_string(filename));
1526
static int lookup_rule_index_in_array(json_t *rules, int id)
1528
json_t *rule, *rule_id;
1530
for (i = 0, size = json_array_size(rules); i < size; i++) {
1531
rule = json_array_get(rules, i);
1532
rule_id = json_object_get(rule, "id");
1533
if (json_integer_value(rule_id) == id) {
1541
int nv_app_profile_config_update_rule(AppProfileConfig *config,
1542
const char *filename,
1546
json_t *old_file, *new_file;
1547
json_t *old_file_rules, *new_file_rules;
1548
json_t *new_rule_copy;
1549
const char *old_filename;
1554
key = rule_id_to_key_string(id);
1555
old_filename = json_string_value(json_object_get(config->rule_locations, key));
1556
assert(old_filename);
1558
old_file = app_profile_config_lookup_file(config, old_filename);
1561
old_file_rules = json_object_get(old_file, "rules");
1563
if (filename && (strcmp(filename, old_filename) != 0)) {
1564
// If the rule has a new file, delete the rule and re-add it
1565
new_file = app_profile_config_lookup_file(config, filename);
1568
new_file = app_profile_config_new_file(config, filename);
1571
new_file_rules = json_object_get(new_file, "rules");
1573
idx = lookup_rule_index_in_array(old_file_rules, id);
1575
json_array_remove(old_file_rules, idx);
1577
json_array_insert(new_file_rules, 0, new_rule);
1578
new_rule_copy = json_array_get(new_file_rules, 0);
1579
json_object_set_new(new_rule_copy, "id", json_integer(id));
1581
json_object_set_new(config->rule_locations, key, json_string(filename));
1583
// Otherwise, just edit the existing rule
1585
idx = lookup_rule_index_in_array(old_file_rules, id);
1587
json_array_set(old_file_rules, idx, new_rule);
1588
new_rule_copy = json_array_get(old_file_rules, idx);
1589
json_object_set_new(new_rule_copy, "id", json_integer(id));
1595
app_profile_config_prune_empty_file(config, old_file);
1601
void nv_app_profile_config_delete_rule(AppProfileConfig *config, int id)
1603
json_t *file, *file_rules;
1604
const char *filename;
1608
key = rule_id_to_key_string(id);
1610
filename = json_string_value(json_object_get(config->rule_locations, key));
1613
file = app_profile_config_lookup_file(config, filename);
1616
file_rules = json_object_get(file, "rules");
1618
idx = lookup_rule_index_in_array(file_rules, id);
1620
json_array_remove(file_rules, idx);
1623
json_object_del(config->rule_locations, key);
1627
size_t nv_app_profile_config_count_rules(AppProfileConfig *config)
1629
return json_object_size(config->rule_locations);
1632
static size_t app_profile_config_count_rules_before(AppProfileConfig *config, const char *filename)
1635
size_t num_rules = 0;
1636
json_t *cur_file, *cur_filename;
1638
for (i = 0, size = json_array_size(config->parsed_files); i < size; i++) {
1639
cur_file = json_array_get(config->parsed_files, i);
1640
cur_filename = json_object_get(cur_file, "filename");
1641
if (!strcmp(filename, json_string_value(cur_filename))) {
1644
num_rules += json_array_size(json_object_get(cur_file, "rules"));
1650
static void app_profile_config_insert_rule(AppProfileConfig *config,
1653
const char *old_filename)
1656
size_t num_rules = 0;
1658
const char *filename;
1659
json_t *file, *file_rules;
1661
size_t rules_before_target[2];
1663
for (i = 0, j = 0, size = json_array_size(config->parsed_files); i < size; i++) {
1664
file = json_array_get(config->parsed_files, i);
1665
file_rules = json_object_get(file, "rules");
1666
if ((num_rules <= new_pri) &&
1667
(num_rules + json_array_size(file_rules) >= new_pri)) {
1668
// Potential target file for this rule
1669
rules_before_target[j] = num_rules;
1675
num_rules += json_array_size(file_rules);
1678
assert((j > 0) && (j <= 2));
1680
// If possible, we prefer to keep the rule in the same file as before
1681
for (i = 0; i < j; i++) {
1682
filename = json_string_value(json_object_get(target[i], "filename"));
1683
if (!strcmp(filename, old_filename)) {
1687
i = (i == j) ? 0 : i;
1689
file_rules = json_object_get(target[i], "rules");
1690
json_array_insert_new(file_rules, new_pri - rules_before_target[i], rule);
1691
// Update the hashtable to point to the new file
1692
key = rule_id_to_key_string(json_integer_value(json_object_get(rule, "id")));
1693
filename = json_string_value(json_object_get(target[i], "filename"));
1694
json_object_set_new(config->rule_locations, key, json_string(filename));
1698
size_t nv_app_profile_config_get_rule_priority(AppProfileConfig *config,
1701
json_t *file, *file_rules;
1702
const char *filename;
1706
key = rule_id_to_key_string(id);
1708
filename = json_string_value(json_object_get(config->rule_locations, key));
1711
file = app_profile_config_lookup_file(config, filename);
1714
file_rules = json_object_get(file, "rules");
1716
idx = lookup_rule_index_in_array(file_rules, id);
1720
return app_profile_config_count_rules_before(config, filename) + idx;
1723
static void app_profile_config_set_abs_rule_priority_internal(AppProfileConfig *config,
1729
json_t *rule, *rule_copy;
1730
json_t *file, *file_rules;
1731
const char *filename;
1735
if (new_pri == current_pri) {
1737
} else if (new_pri >= lowest_pri) {
1738
new_pri = lowest_pri - 1;
1741
key = rule_id_to_key_string(id);
1743
filename = json_string_value(json_object_get(config->rule_locations, key));
1746
file = app_profile_config_lookup_file(config, filename);
1749
file_rules = json_object_get(file, "rules");
1750
idx = lookup_rule_index_in_array(file_rules, id);
1752
rule = json_array_get(file_rules, idx);
1754
rule_copy = json_deep_copy(rule);
1755
json_array_remove(file_rules, idx);
1757
app_profile_config_insert_rule(config, rule_copy, new_pri, filename);
1759
app_profile_config_prune_empty_file(config, file);
1764
void nv_app_profile_config_set_abs_rule_priority(AppProfileConfig *config,
1765
int id, size_t new_pri)
1767
size_t current_pri = nv_app_profile_config_get_rule_priority(config, id);
1768
size_t lowest_pri = nv_app_profile_config_count_rules(config);
1769
app_profile_config_set_abs_rule_priority_internal(config, id, new_pri, current_pri, lowest_pri);
1772
void nv_app_profile_config_change_rule_priority(AppProfileConfig *config,
1776
size_t lowest_pri = nv_app_profile_config_count_rules(config);
1777
size_t current_pri = nv_app_profile_config_get_rule_priority(config, id);
1779
if ((delta < 0) && (((size_t)-delta) > current_pri)) {
1782
new_pri = current_pri + delta;
1784
app_profile_config_set_abs_rule_priority_internal(config, id, new_pri, current_pri, lowest_pri);
1787
const json_t *nv_app_profile_config_get_profile(AppProfileConfig *config,
1788
const char *profile_name)
1790
json_t *file, *file_profiles;
1791
json_t *filename = json_object_get(config->profile_locations, profile_name);
1797
file = app_profile_config_lookup_file(config, json_string_value(filename));
1798
file_profiles = json_object_get(file, "profiles");
1800
return json_object_get(file_profiles, profile_name);
1804
const json_t *nv_app_profile_config_get_rule(AppProfileConfig *config,
1807
char *key = rule_id_to_key_string(id);
1808
json_t *file, *rule, *filename;
1812
filename = json_object_get(config->rule_locations, key);
1819
file = app_profile_config_lookup_file(config, json_string_value(filename));
1820
file_rules = json_object_get(file, "rules");
1822
idx = lookup_rule_index_in_array(file_rules, id);
1824
rule = json_array_get(file_rules, idx);
1834
struct AppProfileConfigProfileIterRec {
1835
AppProfileConfig *config;
1841
AppProfileConfigProfileIter *nv_app_profile_config_profile_iter(AppProfileConfig *config)
1843
AppProfileConfigProfileIter *iter = malloc(sizeof(AppProfileConfigProfileIter));
1845
iter->config = config;
1847
iter->profile_iter = NULL;
1849
return nv_app_profile_config_profile_iter_next(iter);
1852
AppProfileConfigProfileIter *nv_app_profile_config_profile_iter_next(AppProfileConfigProfileIter *iter)
1854
AppProfileConfig *config = iter->config;
1859
size = json_array_size(config->parsed_files);
1860
while ((iter->file_idx < size) &&
1861
!iter->profile_iter) {
1862
file = json_array_get(config->parsed_files, iter->file_idx);
1863
iter->profiles = json_object_get(file, "profiles");
1864
iter->profile_iter = json_object_iter(iter->profiles);
1869
if (!iter->profile_iter) {
1875
iter->profile_iter = json_object_iter_next(iter->profiles, iter->profile_iter);
1878
while ((iter->file_idx < size) &&
1879
!iter->profile_iter) {
1880
file = json_array_get(config->parsed_files, iter->file_idx);
1881
iter->profiles = json_object_get(file, "profiles");
1882
iter->profile_iter = json_object_iter(iter->profiles);
1886
if (!iter->profile_iter) {
1895
struct AppProfileConfigRuleIterRec {
1896
AppProfileConfig *config;
1902
AppProfileConfigRuleIter *nv_app_profile_config_rule_iter(AppProfileConfig *config)
1904
AppProfileConfigRuleIter *iter = malloc(sizeof(AppProfileConfigRuleIter));
1907
iter->rule_idx = -1;
1908
iter->config = config;
1910
return nv_app_profile_config_rule_iter_next(iter);
1913
AppProfileConfigRuleIter *nv_app_profile_config_rule_iter_next(AppProfileConfigRuleIter *iter)
1915
AppProfileConfig *config = iter->config;
1920
size = json_array_size(config->parsed_files);
1921
while ((iter->file_idx < size) &&
1922
(iter->rule_idx == -1)) {
1923
file = json_array_get(config->parsed_files, iter->file_idx);
1924
iter->rules = json_object_get(file, "rules");
1925
if (json_array_size(iter->rules)) {
1932
if (iter->rule_idx == -1) {
1939
if (iter->rule_idx >= json_array_size(iter->rules)) {
1940
iter->rule_idx = -1;
1944
while ((iter->file_idx < size) &&
1945
(iter->rule_idx == -1)) {
1946
file = json_array_get(config->parsed_files, iter->file_idx);
1947
iter->rules = json_object_get(file, "rules");
1948
if (json_array_size(iter->rules)) {
1954
if (iter->rule_idx == -1) {
1962
const char *nv_app_profile_config_profile_iter_name(AppProfileConfigProfileIter *iter)
1964
return json_object_iter_key(iter->profile_iter);
1967
json_t *nv_app_profile_config_profile_iter_val(AppProfileConfigProfileIter *iter)
1969
return json_object_iter_value(iter->profile_iter);
1972
size_t nv_app_profile_config_rule_iter_pri(AppProfileConfigRuleIter *iter)
1974
json_t *rule = nv_app_profile_config_rule_iter_val(iter);
1975
return nv_app_profile_config_get_rule_priority(iter->config,
1976
json_integer_value(json_object_get(rule, "id")));
1979
json_t *nv_app_profile_config_rule_iter_val(AppProfileConfigRuleIter *iter)
1981
return json_array_get(iter->rules, iter->rule_idx);
1984
const char *nv_app_profile_config_profile_iter_filename(AppProfileConfigProfileIter *iter)
1986
json_t *file = json_array_get(iter->config->parsed_files, iter->file_idx - 1);
1987
return json_string_value(json_object_get(file, "filename"));
1990
const char *nv_app_profile_config_rule_iter_filename(AppProfileConfigRuleIter *iter)
1992
json_t *file = json_array_get(iter->config->parsed_files, iter->file_idx - 1);
1993
return json_string_value(json_object_get(file, "filename"));
1996
const char *nv_app_profile_config_get_rule_filename(AppProfileConfig *config,
1999
const char *filename;
2002
key = rule_id_to_key_string(id);
2003
filename = json_string_value(json_object_get(config->rule_locations, key));
2009
const char *nv_app_profile_config_get_profile_filename(AppProfileConfig *config,
2010
const char *profile_name)
2012
return json_string_value(json_object_get(config->profile_locations, profile_name));
2015
static char *get_search_path_string(AppProfileConfig *config)
2019
char *s = strdup("");
2020
for (i = 0; i < config->search_path_count; i++) {
2021
new_s = nvasprintf("%s\t\"%s\"\n",
2022
s, config->search_path[i]);
2030
static int app_profile_config_check_is_prefix(const char *filename1, const char *filename2)
2032
char *dirname1, *dirname2;
2034
dirname1 = nv_dirname(filename1);
2035
dirname2 = nv_dirname(filename2);
2037
if (!strcmp(dirname1, filename2)) {
2039
} else if (!strcmp(filename1, dirname2)) {
2048
return prefix_state;
2051
int nv_app_profile_config_check_valid_source_file(AppProfileConfig *config,
2052
const char *filename,
2055
const json_t *parsed_file;
2057
const char *cur_filename;
2059
char *search_path_string;
2062
// Check if the source file can be found in the search path
2064
for (i = 0; i < config->search_path_count; i++) {
2065
if (!strcmp(filename, config->search_path[i])) {
2069
dirname = nv_dirname(filename);
2071
if (!strcmp(dirname, config->search_path[i])) {
2078
if (i == config->search_path_count) {
2079
search_path_string = get_search_path_string(config);
2081
*reason = nvasprintf("the filename is not valid in the search path:\n\n%s\n",
2082
search_path_string);
2084
free(search_path_string);
2088
// Check if the source file is a prefix of some other file in the configuration,
2090
for (i = 0, size = json_array_size(config->parsed_files); i < size; i++) {
2091
parsed_file = json_array_get(config->parsed_files, i);
2092
cur_filename = json_string_value(json_object_get(parsed_file, "filename"));
2094
prefix_state = app_profile_config_check_is_prefix(filename, cur_filename);
2097
if (prefix_state > 0) {
2099
*reason = nvasprintf("the filename is a prefix of the existing file \"%s\".",
2102
} else if (reason) {
2103
*reason = nvasprintf("the filename would be placed in the directory \"%s\", "
2104
"but that is already a regular file in the configuration.",
2114
int nv_app_profile_config_check_backing_files(AppProfileConfig *config)
2118
const char *filename;
2121
struct stat stat_buf;
2123
int changed = FALSE;
2124
for (i = 0, size = json_array_size(config->parsed_files); i < size; i++) {
2125
file = json_array_get(config->parsed_files, i);
2126
if (json_is_false(json_object_get(file, "new"))) {
2127
// Stat the file and compare our saved atime to the file's mtime
2128
filename = json_string_value(json_object_get(file, "filename"));
2129
ret = open_and_stat(filename, "r", &fp, &stat_buf);
2132
saved_atime = (time_t)json_integer_value(json_object_get(file, "atime"));
2133
if (stat_buf.st_mtime > saved_atime) {
2134
json_object_set_new(file, "dirty", json_true());
2138
// I/O errors: assume something changed
2139
json_object_set_new(file, "dirty", json_true());
2149
* Filenames in the search path ending in "*.d" are directories by convention,
2150
* and should not be listed as valid default filenames.
2152
static inline int check_has_directory_suffix(const char *filename)
2154
size_t len = strlen(filename);
2156
return (len >= 2) &&
2157
(filename[len-2] == '.') &&
2158
(filename[len-1] == 'd');
2161
json_t *nv_app_profile_config_get_source_filenames(AppProfileConfig *config)
2164
const char *filename;
2167
int do_add_search_path_item;
2169
filenames = json_array();
2170
for (i = 0, size = json_array_size(config->parsed_files); i < size; i++) {
2171
file = json_array_get(config->parsed_files, i);
2172
json_array_append(filenames, json_object_get(file, "filename"));
2175
for (i = 0; i < config->search_path_count; i++) {
2176
do_add_search_path_item =
2177
!check_has_directory_suffix(config->search_path[i]);
2178
for (j = 0; (j < size) && do_add_search_path_item; j++) {
2179
file = json_array_get(config->parsed_files, j);
2180
filename = json_string_value(json_object_get(file, "filename"));
2181
if (!strcmp(config->search_path[i], filename) ||
2182
app_profile_config_check_is_prefix(config->search_path[i],
2184
do_add_search_path_item = FALSE;
2187
if (do_add_search_path_item) {
2188
json_array_append_new(filenames,
2189
json_string(config->search_path[i]));
2196
int nv_app_profile_config_profile_name_change_fixup(AppProfileConfig *config,
2197
const char *orig_name,
2198
const char *new_name)
2200
int fixed_up = FALSE;
2202
size_t num_files, num_rules;
2203
json_t *file, *rules, *rule, *rule_profile;
2204
const char *rule_profile_str;
2206
for (i = 0, num_files = json_array_size(config->parsed_files); i < num_files; i++) {
2207
file = json_array_get(config->parsed_files, i);
2208
rules = json_object_get(file, "rules");
2209
for (j = 0, num_rules = json_array_size(rules); j < num_rules; j++) {
2210
rule = json_array_get(rules, j);
2211
rule_profile = json_object_get(rule, "profile");
2212
assert(json_is_string(rule_profile));
2213
rule_profile_str = json_string_value(rule_profile);
2214
if (!strcmp(rule_profile_str, orig_name)) {
2215
json_object_set_new(rule, "profile", json_string(new_name));