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(&timestamp);
200     ptm = localtime(&timestamp);
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