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