File: wfut.cpp

package info (click to toggle)
libwfut 0.2.1-2
  • links: PTS
  • area: main
  • in suites: squeeze, wheezy
  • size: 2,596 kB
  • ctags: 1,927
  • sloc: cpp: 18,504; sh: 9,196; python: 630; makefile: 76
file content (317 lines) | stat: -rw-r--r-- 9,677 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
// This file may be redistributed and modified only under the terms of
// the GNU Lesser General Public License (See COPYING for details).
// Copyright (C) 2005 - 2008 Simon Goodall

#ifdef HAVE_CONFIG_H
  #include "config.h"
#endif

#include <getopt.h>

#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <cstdio>
#include <algorithm>

#include <sigc++/bind.h>

#include <libwfut/WFUT.h>
#include <libwfut/Encoder.h>
#include <libwfut/platform.h>

using namespace WFUT;

static const bool debug = true;

// getopt long argument struct. 
static struct option long_options [] =
{
  { "update", 1, 0, 'u' },
  { "system", 1, 0, 's' },
  { "server", 1, 0, 'S' },
  { "prefix", 1, 0, 'p' },
  { "version", 0, 0, 'v' },
  { "help", 0, 0, 'h' },

};

// getopt short argument struct. One char per command
// follow by a : to indicate that an argument it required
static char short_options [] = "u:s:p:vS:h";

static void recordUpdate(const FileObject &fo, const std::string &tmpfile) {
  FILE *fp = 0;
  if (!os_exists(tmpfile)) {
    // Write header 
    fp = fopen(tmpfile.c_str(), "wt");
    if (!fp) {
      //error
      return;
    }
    fprintf(fp, "<?xml version=\"1.0\"?>\n");
    fprintf(fp, "<fileList dir=\"\">\n");
  } else {
    fp = fopen(tmpfile.c_str(), "at");
    if (!fp) {
      //error
      return;
    }
  }
  fprintf(fp, "<file filename=\"%s\" version=\"%d\" crc32=\"%lu\" size=\"%ld\" execute=\"%s\"/>\n", Encoder::encodeString(fo.filename).c_str(), fo.version, fo.crc32, fo.size, (fo.execute) ? ("true") : ("false"));
  fclose(fp);

}

// Signal handler called when a file is sicessfully downloaded
// We use this to update the local file list with the updated details
void onDownloadComplete(const std::string &u, const std::string &f, const ChannelFileList &updates, ChannelFileList *local, const std::string &tmpfile)  {
  printf("Downloaded: %s\n", f.c_str());

  const WFUT::FileMap &ulist = updates.getFiles();
  WFUT::FileMap::const_iterator I = ulist.find(f);
  // The fileobject should exist, otherwise how did we get here?
  assert (I != ulist.end());
  // Add the updated file record to the local list.
  // addFile will overwrite any existing fileobject with the same
  // filename
  local->addFile(I->second);

  // We store in a tmp file the fact that we sucessfully downloaded
  // this file, incase of a crash.
  recordUpdate(I->second, tmpfile);
}

// Signal handler called when a download fails.
void onDownloadFailed(const std::string &u, const std::string &f, const std::string &r, int *error)  {
  fprintf(stderr, "Error downloading: %s - %s\n", u.c_str(), r.c_str());
  // Increment error count
  ++error;
}

void onUpdateReason(const std::string &filename, const WFUT::WFUTUpdateReason wu) {
  if (wu == WFUT::WFUT_UPDATE_MODIFIED) {
    printf("%s has been modified, will not update.\n", filename.c_str());
  }
}

void print_usage(const char *name) {
  printf("WFUT Version: %s\n", VERSION);
  printf("Usage: %s [options]\n", name);
  printf("\nOptions:\n");
  printf("\t-p, --prefix channel_name -- The destination directory. (Optional)\n");
  printf("\t-s, --system channel_name -- The system channels directory. (Optional)\n");
  printf("\t-S, --server channel_name -- The URL to the update server.(Optional)\n");
  printf("\t-u, --update channel_name -- The name of the channel to update.\n");
  printf("\t-v, --version -- Display the version information.\n");
  printf("\t-h, --help -- Display this message.\n");
}

int main(int argc, char *argv[]) {

  // Set some default values which we can override with command line parameters.
  std::string server_root = "http://white.worldforgedev.org/WFUT/";
  std::string mirror_file = "mirrors.xml";
  std::string channel_file = "wfut.xml";
  std::string tmpfile = "tempwfut.xml";

  std::string channel = ".";
  std::string local_path = "./";
  std::string system_path = "";

  if (argc == 1) {
    print_usage(argv[0]);
    return 0;
  }
 
  while (true) {
    int opt_index = 0;
    int c = getopt_long(argc, argv, short_options, long_options, &opt_index);
    if (c == -1) break;
    switch (c) {
      case '?':
      case 'h':
        print_usage(argv[0]);
        return 0;
        break;
      case 'v':
        fprintf(stderr, "WFUT Version: %s\n", VERSION);
        return 0;
        break;
      case 'u':
        if (optarg) {
          channel = optarg;
        } else {
          fprintf(stderr, "Missing channel name\n");
        }
        break;
       case 's':
        if (optarg) {
          system_path = optarg;
        } else {
          fprintf(stderr, "Missing system path\n");
        }
        break;
       case 'p':
        if (optarg) {
          local_path = optarg;
        } else {
          fprintf(stderr, "Missing prefix\n");
        }
        break;
       case 'S':
        if (optarg) {
          server_root = optarg;
        } else {
          fprintf(stderr, "Missing system path\n");
        }
        break;
 
      default:
        fprintf(stderr, "Unknown command: %c\n", c);
    }
  }

  if (debug) printf("Channel name: %s\n", channel.c_str());
  // This is the base path for all files that will be downloaded
  const std::string &local_root = local_path + "/" + channel + "/";

  WFUTClient wfut;
  // Initialise wfut. This does the curl setup.
  wfut.init();

  // Get the list of mirrors
  MirrorList mirrors;
  const std::string mirror_url = server_root + "/" + mirror_file;
  wfut.getMirrorList(mirror_url, mirrors);
 
  // Randomly select a mirror to use. 
  if (mirrors.empty() == false) {
    // Initialise random number generators. random_shuffle could use either
    srand((unsigned)time(NULL)); 
#if defined (WIN32) || defined (_WIN32) || defined( __WIN32__) 
#else
    srand48((unsigned)time(NULL)); 
#endif
    // Shuffle mirror list
    std::random_shuffle(mirrors.begin(), mirrors.end());
    // Pick first mirror
    server_root = (*mirrors.begin()).url;
  }

  // Define the channelfilelist objects we will use
  ChannelFileList local, system, server, updates, tmplist;


  // Look for local wfut file.
  const std::string &local_wfut = local_path  + "/" + channel + "/" + channel_file;
  if (debug) printf("Local wfut: %s\n", local_wfut.c_str());

  if (os_exists(local_wfut)) {
    if (wfut.getLocalList(local_wfut, local)) {
      fprintf(stderr, "Error reading local wfut.xml file\n");
      wfut.shutdown();
      return 1;
    } else {
      if (channel == ".") channel = local.getName();
    }
  }

  // Look for tmpwfut file. If it exists, update the local files list.
  const std::string &tmp_wfut = local_path  + "/" + tmpfile;
  if (debug) printf("Tmp wfut: %s\n", tmp_wfut.c_str());

  if (os_exists(tmp_wfut)) {
    if (wfut.getLocalList(tmp_wfut, tmplist)) {
      fprintf(stderr, "Error reading tmpwfut.xml file\n");
      wfut.shutdown();
      return 1;
    } else {
      const FileMap &fm = tmplist.getFiles();
      FileMap::const_iterator I = fm.begin();
      FileMap::const_iterator Iend = fm.end();
      for (; I != Iend; ++I) {
        local.addFile(I->second);
      }
    }
  }

  // Look for a system wfut file
  if (!system_path.empty()) {
  const std::string &system_wfut = system_path + "/" + channel + "/" + channel_file; 
  if (debug) printf("System wfut: %s\n", system_wfut.c_str());

  if (os_exists(system_wfut)) {
    if (wfut.getLocalList(system_wfut, system)) {
      fprintf(stderr, "Error reading system wfut.xml file\n");
      wfut.shutdown();
      return 1;
    } else {
      if (channel == ".") channel = system.getName();
    }
  }
  }
  // By now we should have a proper channel name. If not, then there is nothing 
  // we can do to find the server updates.
  if (channel.empty() || channel == ".") {
    fprintf(stderr, "Unable to determine channel name.\n");
    wfut.shutdown();
    return 1;
  }

  const std::string &server_wfut = server_root + "/" + channel + "/" + channel_file; 
  if (debug) printf("Server wfut: %s\n", server_wfut.c_str());

  if (wfut.getFileList(server_wfut, server)) {
    printf("Error downloading server channel file\n");
    wfut.shutdown();
    return 1;
  }

  if (debug) printf("Local Root: %s\n", local_root.c_str());

  printf("Updating Channel: %s\n", channel.c_str());

  // Now we have loaded all our data files, lets find out what we really need
  // to download
  wfut.UpdateReason.connect(sigc::ptr_fun(onUpdateReason));
  if (wfut.calculateUpdates(server, system, local, updates, local_root)) {
    printf("Error determining file to update\n");
    wfut.shutdown();
    return 1;
  }

  // Make sure the local file has the correct channel name
  local.setName(server.getName());

  // Queue the list of files to download
  wfut.updateChannel(updates, server_root + "/" + channel, local_root);

  // error counts the number of download failures. This is incremented in the
  // download failed signal handler
  int error = 0;
  // Connect up the signal handlers
  wfut.DownloadComplete.connect(sigc::bind(sigc::ptr_fun(onDownloadComplete), updates, &local, tmp_wfut));
  wfut.DownloadFailed.connect(sigc::bind(sigc::ptr_fun(onDownloadFailed), &error));

  // Keep polling to download some more file bits.
  while (wfut.poll());

  // Save the completed download list
  wfut.saveLocalList(local, local_wfut);
  // Delete tmpwfut.xml if we get here. We only keep it around in case of 
  // an abnormal exit and the real wfut.xml has not been saved.
  if (os_exists(tmp_wfut)) unlink(tmp_wfut.c_str());

  // Clean up WFUT. Closes curl handles
  wfut.shutdown();

  if (error) {
    fprintf(stderr, "%d files failed to download.\n", error);
  }


  // Return error. Will be 0 on success
  return error;
}