1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2008 University of California
4 //
5 // BOINC is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License
7 // as published by the Free Software Foundation,
8 // either version 3 of the License, or (at your option) any later version.
9 //
10 // BOINC is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 // See the GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with BOINC. If not, see <http://www.gnu.org/licenses/>.
17
18 // boinclog: command-line interface to a BOINC client,
19 // using GUI RPCs.
20 //
21 // usage: boinccmd [--host hostname] [--passwd passwd] command
22
23 #if defined(_WIN32) && !defined(__STDWX_H__) && !defined(_BOINC_WIN_) && !defined(_AFX_STDAFX_H_)
24 #include "boinc_win.h"
25 #endif
26
27 #if defined(_WIN32) && !defined(__CYGWIN32__)
28 #define snprintf _snprintf
29 #define strdate _strdate
30 #define strtime _strtime
31 #define chdir _chdir
32 #endif
33
34 #ifdef _WIN32
35 #include "win_util.h"
36 #else
37 #include "config.h"
38 #include <cstdio>
39 #include <cstring>
40 #include <unistd.h>
41 #endif
42
43 #include <vector>
44 #include <string>
45 using std::vector;
46 using std::string;
47
48 #include "gui_rpc_client.h"
49 #include "error_numbers.h"
50 #include "util.h"
51 #include "str_util.h"
52 #include "str_replace.h"
53 #include "url.h"
54 #include "version.h"
55 #include "common_defs.h"
56
57
58 #define ARGX2(s1,s2) (!strcmp(argv[i], s1)||!strcmp(argv[i], s2))
59 #define ARG(S) ARGX2("-"#S, "--"#S)
60
61
62 // Global variables
63 char g_log_filename[256];
64 int g_message_sequence;
65
version()66 void version(){
67 printf("boinclog, built from %s \n", PACKAGE_STRING );
68 exit(0);
69 }
70
usage()71 void usage() {
72 fprintf(stderr, "\n\
73 usage: boinclog [--host hostname] [--passwd passwd] [commands]\n\n\
74 Commands:\n\
75 --version, -V show version of the logging tool\n\
76 --datadir <directory> where the data directory is located\n\
77 "
78 );
79 exit(1);
80 }
81
show_error(int retval)82 void show_error(int retval) {
83 fprintf(stderr, "Error %d: %s\n", retval, boincerror(retval));
84 }
85
prio_name(int prio)86 const char* prio_name(int prio) {
87 switch (prio) {
88 case MSG_INFO: return "low";
89 case MSG_USER_ALERT: return "user notification";
90 case MSG_INTERNAL_ERROR: return "internal error";
91 }
92 return "unknown";
93 }
94
95
update_display()96 void update_display() {
97 system("cls");
98 printf("BOINC Log Conversion Client %s\n", PACKAGE_VERSION);
99 printf("Log file: %s\n", g_log_filename);
100 printf("%d message(s) processed.\n\n", g_message_sequence);
101 printf("Press CTRL-C to exit application.\n");
102 }
103
104
main(int argc,char ** argv)105 int main(int argc, char** argv) {
106 unsigned int i;
107 int retval, port=0;
108 RPC_CLIENT rpc;
109 MESSAGES msgs;
110 char buf[256];
111 char datadir[256];
112 char hostname_buf[256], passwd_buf[256];
113 char *hostname = 0, *passwd = passwd_buf, *p;
114 struct tm* ptm;
115 time_t timestamp;
116 FILE* f = NULL;
117 std::string msg_datetime;
118 std::string msg_project;
119 std::string msg_priority;
120 std::string msg_type;
121 std::string msg_body;
122 std::string msg_tmp;
123
124 strlcpy(buf, "", sizeof(buf));
125 strlcpy(datadir, "", sizeof(datadir));
126 strlcpy(hostname_buf, "", sizeof(hostname_buf));
127 strlcpy(passwd_buf, "", sizeof(passwd_buf));
128 strlcpy(g_log_filename, "", sizeof(g_log_filename));
129 g_message_sequence = 0;
130
131 #if defined(_WIN32) && defined(USE_WINSOCK)
132 WSADATA wsdata;
133 retval = WSAStartup( MAKEWORD( 1, 1 ), &wsdata);
134 if (retval) {
135 fprintf(stderr, "WinsockInitialize: %d\n", retval);
136 exit(1);
137 }
138 #endif
139
140 for (i=1; i<(unsigned int)argc; i++) {
141 if (0) {
142 } else if (ARG(h)) {
143 usage();
144 } else if (ARG(help)) {
145 usage();
146 } else if (ARG(V)) {
147 version();
148 } else if (ARG(version)) {
149 version();
150 } else if (ARG(host)) {
151 if ((i+1) == (unsigned int)argc) usage();
152 hostname = hostname_buf;
153 safe_strcpy(hostname_buf, argv[++i]);
154 p = strchr(hostname, ':');
155 if (p) {
156 port = atoi(p+1);
157 *p=0;
158 }
159 } else if (ARG(passwd)) {
160 if ((i+1) == (unsigned int)argc) usage();
161 safe_strcpy(passwd_buf, argv[++i]);
162 } else if (ARG(datadir)) {
163 if ((i+1) == (unsigned int)argc) usage();
164 safe_strcpy(datadir, argv[++i]);
165 } else {
166 printf("Unknown option: %s\n", argv[i]);
167 usage();
168 }
169 }
170
171 if (strlen(datadir)) {
172 chdir(datadir);
173 } else {
174 #ifdef _WIN32
175 chdir_to_data_dir();
176 #endif
177 }
178
179 read_gui_rpc_password(passwd_buf);
180
181 retval = rpc.init(hostname, port);
182 if (retval) {
183 fprintf(stderr, "can't connect to %s\n", hostname?hostname:"local host");
184 show_error(retval);
185 exit(1);
186 }
187
188 if (passwd) {
189 retval = rpc.authorize(passwd);
190 if (retval) {
191 fprintf(stderr, "authorization failure: %d\n", retval);
192 show_error(retval);
193 exit(1);
194 }
195 }
196
197
198 // Construct a unique filename for the output.
199 time(×tamp);
200 ptm = localtime(×tamp);
201 strftime(g_log_filename, sizeof(g_log_filename), "%Y%m%d%H%M.log", ptm);
202
203 // Open the new log file for output
204 f = fopen(g_log_filename, "w");
205 setbuf(f, NULL);
206
207 while(true) {
208 update_display();
209
210 msgs.clear();
211
212 rpc.get_messages(g_message_sequence, msgs);
213
214 for (i=0; i<msgs.messages.size(); i++) {
215 MESSAGE* pMsg = msgs.messages[i];
216
217 msg_datetime.clear();
218 msg_project.clear();
219 msg_priority.clear();
220 msg_type.clear();
221 msg_body.clear();
222
223 msg_datetime = time_to_string(double(pMsg->timestamp));
224 msg_project = pMsg->project;
225 msg_priority = prio_name(pMsg->priority);
226 msg_body = pMsg->body;
227 if (pMsg->body[0] == '[') {
228 msg_type = pMsg->body.substr(1, pMsg->body.find(']') - 1);
229 }
230
231 // If a message type is found in the message body, remove it from
232 // the message body
233 if (!msg_type.empty()) {
234 msg_tmp = std::string("[") + msg_type + std::string("] ");
235 msg_body.replace(0, msg_tmp.size(), "");
236 }
237
238 // If the last character if the message body is a newline character,
239 // remove it before continueing on. Standard BOINC messages contain
240 // a newline character at the end.
241 if (msg_body[msg_body.size() - 1] == '\n') {
242 msg_body[msg_body.size() - 1] = ' ';
243 }
244
245 // If line feeds are detected in the message body, replace them with
246 // the pipe symbol.
247 for (unsigned int j = 0; j < msg_body.size(); j++) {
248 if (msg_body[j] == '\n') {
249 msg_body[j] = '^';
250 }
251 }
252
253 // Dump to tab delimited file
254 fprintf(f,
255 "%s\t%s\t%s\t%s\t%s\n",
256 msg_datetime.c_str(),
257 msg_priority.c_str(),
258 msg_project.c_str(),
259 msg_type.c_str(),
260 msg_body.c_str()
261 );
262
263 g_message_sequence = pMsg->seqno;
264 }
265
266 boinc_sleep(1.0);
267 }
268
269 #if defined(_WIN32) && defined(USE_WINSOCK)
270 WSACleanup();
271 #endif
272 exit(retval);
273 }
274