1 #include <lmdump.h>
2 #include <dump.h>
3 #include <diagnose.h>
4 #include <backup.h>
5 #include <repair.h>
6 #include <string_lib.h>
7 #include <logging.h>
8 #include <man.h>
9 #include <cleanup.h>            /* CallCleanupFunctions() */
10 
print_version()11 static void print_version()
12 {
13     printf("cf-check BETA version %s\n", VERSION);
14 }
15 
print_help()16 static void print_help()
17 {
18     printf(
19         "\n"
20         "cf-check:\n"
21         "\tUtility for diagnosis and repair of local CFEngine databases.\n"
22         "\tThis BETA version of the tool is for testing purposes only.\n"
23         "\n"
24         "Commands:\n"
25         "\tdump - Print the contents of a database file\n"
26         "\tdiagnose - Assess the health of one or more database files\n"
27         "\tbackup - Copy database files to a timestamped folder\n"
28         "\trepair - Diagnose, then backup and delete any corrupt databases\n"
29         "\tversion - Print version information\n"
30         "\thelp - Print this help menu\n"
31         "\n"
32         "Usage:\n"
33         "\t$ cf-check <command> [options] [file ...]\n"
34         "\n"
35         "Examples:\n"
36         "\t$ cf-check dump " WORKDIR "/state/cf_lastseen.lmdb\n"
37         "\t$ cf-check lmdump -a " WORKDIR "/state/cf_lastseen.lmdb\n"
38         "\t$ cf-check diagnose\n"
39         "\t$ cf-check repair\n"
40         "\n");
41 }
42 
43 static const char *const CF_CHECK_SHORT_DESCRIPTION =
44     "Utility for diagnosis and repair of local CFEngine databases.";
45 
46 static const char *const CF_CHECK_MANPAGE_LONG_DESCRIPTION =
47     "cf-check does not evaluate policy or rely on the integrity of the\n"
48     "databases. It is intended to be able to detect and repair a corrupt\n"
49     "database.";
50 
51 static const Description COMMANDS[] =
52 {
53     {"help",     "Prints general help or per topic",
54                  "cf-check help [command]"},
55     {"diagnose", "Assess the health of one or more database files",
56                  "cf-check diagnose"},
57     {"backup",   "Backup database files to a timestamped folder",
58                  "cf-check backup"},
59     {"repair",   "Diagnose, then backup and delete any corrupt databases",
60                  "cf-check repair"},
61     {"dump",     "Print the contents of a database file",
62                  "cf-check dump " WORKDIR "/state/cf_lastseen.lmdb"},
63     {"lmdump",   "LMDB database dumper (deprecated)",
64                  "cf-check lmdump -a " WORKDIR "/state/cf_lastseen.lmdb"},
65     {NULL, NULL, NULL}
66 };
67 
68 static const struct option OPTIONS[] =
69 {
70     {"help",        optional_argument,  0, 'h'},
71     {"manpage",     no_argument,        0, 'M'},
72     {"version",     no_argument,        0, 'V'},
73     {"debug",       no_argument,        0, 'd'},
74     {"verbose",     no_argument,        0, 'v'},
75     {"log-level",   required_argument,  0, 'g'},
76     {"inform",      no_argument,        0, 'I'},
77     {NULL,          0,                  0, '\0'}
78 };
79 
80 static const char *const HINTS[] =
81 {
82     "Print the help message",
83     "Print the man page",
84     "Output the version of the software",
85     "Enable debugging output",
86     "Enable verbose output",
87     "Specify how detailed logs should be. Possible values: 'error', 'warning', 'notice', 'info', 'verbose', 'debug'",
88     "Enable basic information output",
89     NULL
90 };
91 
CFCheckHelpTopic(const char * topic)92 static int CFCheckHelpTopic(const char *topic)
93 {
94     assert(topic != NULL);
95     bool found = false;
96     for (int i = 0; COMMANDS[i].name != NULL; ++i)
97     {
98         if (strcmp(COMMANDS[i].name, topic) == 0)
99         {
100             printf("Command:     %s\n", COMMANDS[i].name);
101             printf("Usage:       %s\n", COMMANDS[i].usage);
102             printf("Description: %s\n", COMMANDS[i].description);
103             found = true;
104             break;
105         }
106     }
107 
108     // Add more detailed explanation here if necessary:
109     if (strcmp("help", topic) == 0)
110     {
111         printf("\nYou did it, you used the help command!\n");
112     }
113     else
114     {
115         if (!found)
116         {
117             printf("Unknown help topic: '%s'\n", topic);
118             return EXIT_FAILURE;
119         }
120     }
121     return EXIT_SUCCESS;
122 }
123 
main(int argc,const char * const * argv)124 int main(int argc, const char *const *argv)
125 {
126     if (StringEndsWith(argv[0], "lmdump"))
127     {
128         // Compatibility mode; act like lmdump if symlinked or renamed:
129         int ret = lmdump_main(argc, argv);
130         CallCleanupFunctions();
131         return ret;
132     }
133 
134     // When run separately it makes sense for cf-check to have INFO messages
135     LogSetGlobalLevel(LOG_LEVEL_INFO);
136     // In agent, NOTICE log level is default, and cf-check functions
137     // will print less information
138 
139     if (argc < 2)
140     {
141         print_help();
142         Log(LOG_LEVEL_ERR, "No command given");
143         CallCleanupFunctions();
144         return EXIT_FAILURE;
145     }
146 
147     int c = 0;
148     int start_index = 1;
149     const char *optstr = "+hMg:dvI"; // + means stop for non opt arg. :)
150     while ((c = getopt_long(argc, (char *const *) argv, optstr, OPTIONS, &start_index))
151            != -1)
152     {
153         switch (c)
154         {
155         case 'd':
156         {
157             LogSetGlobalLevel(LOG_LEVEL_DEBUG);
158             break;
159         }
160         case 'v':
161         {
162             LogSetGlobalLevel(LOG_LEVEL_VERBOSE);
163             break;
164         }
165         case 'I':
166         {
167             LogSetGlobalLevel(LOG_LEVEL_INFO);
168             break;
169         }
170         case 'g':
171         {
172             LogSetGlobalLevelArgOrExit(optarg);
173             break;
174         }
175         case 'V':
176         {
177             print_version();
178             CallCleanupFunctions();
179             return EXIT_SUCCESS;
180             break;
181         }
182         case 'h':
183         {
184             print_help();
185             CallCleanupFunctions();
186             return EXIT_SUCCESS;
187             break;
188         }
189         case 'M':
190         {
191             Writer *out = FileWriter(stdout);
192             ManPageWrite(out, "cf-check", time(NULL),
193                          CF_CHECK_SHORT_DESCRIPTION,
194                          CF_CHECK_MANPAGE_LONG_DESCRIPTION,
195                          OPTIONS, HINTS,
196                          NULL, false,
197                          true);
198             FileWriterDetach(out);
199             CallCleanupFunctions();
200             return EXIT_SUCCESS;
201             break;
202         }
203         default:
204         {
205             CallCleanupFunctions();
206             return EXIT_FAILURE;
207             break;
208         }
209         }
210     }
211     const char *const *const cmd_argv = argv + optind;
212     int cmd_argc = argc - optind;
213     const char *command = cmd_argv[0];
214 
215     if (StringEqual_IgnoreCase(command, "lmdump"))
216     {
217         int ret = lmdump_main(cmd_argc, cmd_argv);
218         CallCleanupFunctions();
219         return ret;
220     }
221     if (StringEqual_IgnoreCase(command, "dump"))
222     {
223         int ret = dump_main(cmd_argc, cmd_argv);
224         CallCleanupFunctions();
225         return ret;
226     }
227     if (StringEqual_IgnoreCase(command, "diagnose"))
228     {
229         int ret = diagnose_main(cmd_argc, cmd_argv);
230         CallCleanupFunctions();
231         return ret;
232     }
233     if (StringEqual_IgnoreCase(command, "backup"))
234     {
235         int ret = backup_main(cmd_argc, cmd_argv);
236         CallCleanupFunctions();
237         return ret;
238     }
239     if (StringEqual_IgnoreCase(command, "repair") ||
240         StringEqual_IgnoreCase(command, "remediate"))
241     {
242         int ret = repair_main(cmd_argc, cmd_argv);
243         CallCleanupFunctions();
244         return ret;
245     }
246     if (StringEqual_IgnoreCase(command, "help"))
247     {
248         if (cmd_argc > 2)
249         {
250             Log(LOG_LEVEL_ERR, "help takes exactly 0 or 1 arguments");
251             CallCleanupFunctions();
252             return EXIT_FAILURE;
253         }
254         else if (cmd_argc <= 1)
255         {
256             print_help();
257         }
258         else
259         {
260             assert(cmd_argc == 2);
261             CFCheckHelpTopic(cmd_argv[1]);
262         }
263         CallCleanupFunctions();
264         return EXIT_SUCCESS;
265     }
266     if (StringEqual_IgnoreCase(command, "version"))
267     {
268         print_version();
269         CallCleanupFunctions();
270         return EXIT_SUCCESS;
271     }
272 
273     print_help();
274     Log(LOG_LEVEL_ERR, "Unrecognized command: '%s'", command);
275     CallCleanupFunctions();
276     return EXIT_FAILURE;
277 }
278