Rosetta 3.5
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
boinc.cc
Go to the documentation of this file.
1 // -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*-
2 // vi: set ts=2 noet:
3 //
4 // (c) Copyright Rosetta Commons Member Institutions.
5 // (c) This file is part of the Rosetta software suite and is made available under license.
6 // (c) The Rosetta software is developed by the contributing members of the Rosetta Commons.
7 // (c) For more information, see http://www.rosettacommons.org. Questions about this can be
8 // (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu.
9 
10 /// @file protocols/boinc/boinc.cc
11 /// @brief Wrappers to make BOINC work
12 /// @author David Kim, Mike Tyka
13 /// @note Diagnostics are defined at http://boinc.berkeley.edu/trac/wiki/DiagnosticsApi
14 
15 // Project headers
16 
17 #ifdef BOINC
18 // This has to come before boinc.hh or we get this error on VC++
19 // '_read' : is not a member of 'std::basic_istream<_Elem,_Traits>'
20 #include <utility/io/izstream.hh>
21 #endif
22 
23 // Unit header
24 #include <protocols/boinc/boinc.hh>
25 
26 #ifdef BOINC
27 // Project headers
29 #include <utility/io/ozstream.hh>
30 #include <utility/basic_sys_util.hh>
31 #include <utility/string_util.hh>
32 
33 #include <basic/options/option.hh>
34 #include <basic/options/after_opts.hh>
35 #endif
36 
37 #include <iostream>
38 
39 
40 #ifdef BOINC
41 
42 // ObjexxFCL Headers
43 #include <ObjexxFCL/format.hh>
44 
45 #ifdef BOINC_GRAPHICS
46 #include <core/pose/Pose.hh>
48 #include <core/scoring/rms_util.hh>
50 #endif
51 
52 
53 
54 // C++ headers
55 #include <iostream>
56 
57 // option key includes
58 
59 #include <basic/options/keys/boinc.OptionKeys.gen.hh>
60 
61 
62 
63 namespace protocols {
64 namespace boinc {
65 
66 Boinc& Boinc::instance(void) {
67  static Boinc theBoinc;
68  return theBoinc;
69 }
70 
71 void
73 {
74  using std::cout;
75  using std::cerr;
76  using std::endl;
77  using std::exit;
78 
79  if (worker_initialized_) return;
80 
81  // DIAGNOSTICS
82  // http://boinc.berkeley.edu/trac/wiki/DiagnosticsApi
83  boinc_init_diagnostics(
84  BOINC_DIAG_DUMPCALLSTACKENABLED |
85  BOINC_DIAG_HEAPCHECKENABLED |
86  BOINC_DIAG_MEMORYLEAKCHECKENABLED |
87  BOINC_DIAG_REDIRECTSTDERR |
88  BOINC_DIAG_REDIRECTSTDOUT |
89  BOINC_DIAG_TRACETOSTDERR
90  );
91 
92  cerr << utility::timestamp() << " :: BOINC:: Initializing ... ok." << std::endl;
93  if ( boinc_init() ) exit( EXIT_FAILURE ); // BOINC API call
94  cerr << utility::timestamp() << " :: BOINC :: boinc_init()" << endl;
95 
96  // allow upload of stdout.txt
97  char filename[256];
98  boinc_resolve_filename("stdout.txt", filename, 256);
99  freopen(filename, "a", stdout);
100 
101  boinc_fraction_done(0.00);
102 
103 #ifdef BOINC_GRAPHICS
104  // create shared mem segment for graphics, and arrange to update it
105  //
106  // http://boinc.berkeley.edu/trac/wiki/GraphicsApi
107  // create shared memory segment
108  cerr << "BOINC:: Setting up shared resources ... ok." << std::endl;
109 
110  create_shared_memory();
111 
112  cerr << "BOINC:: Setting up semaphores ... ok." << std::endl;
113  // create samaphore for data synchronization
115 
116  cerr << "BOINC:: Updating status ... ok." << std::endl;
117 
118  // updated status info
119  update_status_shmem();
120 
121  cerr << "BOINC:: Registering timer callback... ok." << std::endl;
122 
123  boinc_register_timer_callback(update_status_shmem);
124 #endif
125 
126  worker_initialized_ = true;
127 
128  cerr << "BOINC:: Worker initialized successfully. " << std::endl;
129 }
130 
131 
135 
136 void Boinc::set_project_pref_max_gfx_fps( double project_pref_max_gfx_fps ) {
137  project_pref_max_gfx_fps_ = project_pref_max_gfx_fps;
138 }
139 void Boinc::set_project_pref_max_gfx_cpu( double project_pref_max_gfx_cpu ) {
140  project_pref_max_gfx_cpu_ = project_pref_max_gfx_cpu;
141 }
142 void Boinc::set_project_pref_max_cpu_run_time( int project_pref_max_cpu_run_time ) {
143  project_pref_max_cpu_run_time_ = project_pref_max_cpu_run_time;
144 }
145 
146 void Boinc::set_working_set_size( double working_set_size ) {
147  working_set_size_ = working_set_size;
150  }
151 }
152 
154 
155  // parse the boinc init data file. may not need to do this.
156  //boinc_parse_init_data_file(); // BOINC API call
157 
158  /*
159 Get information from the core client; this information may be useful for graphics.
160 See http://boinc.berkeley.edu/trac/wiki/BasicApi
161  */
162 
163  APP_INIT_DATA app_init_data;
164  boinc_get_init_data(app_init_data); // BOINC API call
165  if (!app_init_data.project_preferences) return;
166 
167  double max_gfx_fpstmp = 0.0;
168  double max_gfx_cputmp = 0.0;
169  int cpu_run_timetmp = 0;
170  parse_double(app_init_data.project_preferences, "<max_fps>", max_gfx_fpstmp);
171  if (max_gfx_fpstmp > 0) project_pref_max_gfx_fps_ = max_gfx_fpstmp;
172  parse_double(app_init_data.project_preferences, "<max_cpu>", max_gfx_cputmp);
173  if (max_gfx_cputmp > 0) project_pref_max_gfx_cpu_ = max_gfx_cputmp;
174  parse_int(app_init_data.project_preferences, "<cpu_run_time>", cpu_run_timetmp);
175  if (cpu_run_timetmp > 0 && cpu_run_timetmp != project_pref_max_cpu_run_time_) {
176  project_pref_max_cpu_run_time_ = cpu_run_timetmp;
177  std::cerr << "# cpu_run_time_pref: " << project_pref_max_cpu_run_time_ << std::endl;std::cerr.flush();
178  }
179 }
180 
182  read_and_set_project_prefs(); // get user max cpu run time preference
183  boinc_wu_cpu_time(cpu_time_); // BOINC API call for wu cpu run time
185 }
186 
188  read_and_set_project_prefs(); // get user max cpu run time preference
189  boinc_wu_cpu_time(cpu_time_); // BOINC API call for wu cpu run time
190 
191 
192  // Estimate the percentage done depending on the number of decoys already produced:
193  // If we're still working on the first decoy, assume that it may well take more then the user's preferred time, in
194  // fact it may take up to the watchdog timeout in theory (3x runtime!)
195  // If we've done many decoys then safely make a more accurate estimate of the time remaining.
196  using namespace basic::options;
197  int cpu_run_timeout = option[ OptionKeys::boinc::cpu_run_timeout ]();
198  double estimated_wu_length = (double)project_pref_max_cpu_run_time_ + (double(cpu_run_timeout) / (double(decoy_count_) + 1.0) );
199 
200  if( ( estimated_wu_length - cpu_time_ ) < 10*60 ) estimated_wu_length = cpu_time_ + 10*60;
201  double new_frac = cpu_time_ / ( estimated_wu_length );
202 
203  //Only update if increasing
204  if (new_frac > fraction_done_) {
205  fraction_done_ = new_frac;
206  boinc_fraction_done(fraction_done_);
207 /*
208  std::cout << utility::timestamp() << " :: BOINC" <<
209  " :: cpu_time_pref: " << project_pref_max_cpu_run_time_ <<
210  " :: cpu_time: " << cpu_time_ <<
211  " :: fraction_done: " << ObjexxFCL::fmt::F( 7, 2, fraction_done_ ) << std::endl;
212 */
213  }
214 }
215 
216 
217 
218 bool Boinc::worker_is_finished( const int & total_nstruct ){
219  // finished if too many restarts without progress
220  boinc_wu_cpu_time(cpu_time_);
221  if ( ( !boinc_is_standalone() ) && // don't do this in standalone mode
222  ( no_progress_init_cnt_ >= BOINC_MAX_NO_PROGRESS_INIT_CNT ) && // nor if the limit of restarts is reached
223  ( cpu_time_ > (20*60) ) // nor in the first 20 minutes (costhe validator will kill it!)
224  ) {
225  std::cerr << "Too many restarts with no progress. Keep application in memory while preempted." << std::endl;
226  return true;
227  }
228 
229  // did we just complete a decoy ?
230  if( decoy_count_ < total_nstruct ){
231  // then reset the no_progress_init_cnt_
232  // counter.
234  }
235  decoy_count_ = std::max(0, total_nstruct);
236 #ifdef BOINC_GRAPHICS
237  if (shmem_) {
238  shmem_->model_count = total_nstruct + 1;
239  shmem_->total_mc_trial_count = 0; // reset total monte carlo trial count
240  if (shmem_->low_energy) {
241  shmem_->model_low_energy = shmem_->low_energy;
242  if (shmem_->native_pose_exists) {
243  // get native
244  core::pose::PoseOP nativeposeOP = new core::pose::Pose;
245  core::io::serialization::BUFFER bn((char*)(&shmem_->native_pose_buf ),protocols::boinc::POSE_BUFSIZE);
246  core::io::serialization::read_binary(*nativeposeOP,bn);
247  if (shmem_->low_energy_pose_exists) {
248  core::pose::PoseOP lowenergyposeOP = new core::pose::Pose;
249  core::io::serialization::BUFFER bl((char*)(&shmem_->low_energy_pose_buf ),protocols::boinc::POSE_BUFSIZE);
250  core::io::serialization::read_binary(*lowenergyposeOP,bl);
251  shmem_->model_low_energy_rmsd = core::scoring::native_CA_rmsd( *nativeposeOP, *lowenergyposeOP);
252  }
253  }
254  }
255  }
256 #endif
257  // not finished if a structure is not made yet
258  if (total_nstruct <= 0) return false;
259  double time_left = get_remaining_cpu_time();
260  using namespace basic::options;
261  if (
262  // finished if max_cpu_run_time_ reached
263  time_left < 1 ||
264  // finished if there is not enough time to make another structure
265  time_left < cpu_time_/double(total_nstruct) ||
266  // finished if too many models
267  total_nstruct >= option[ OptionKeys::boinc::max_nstruct ]
268  ) {
269  // FINISHED!
270  // update status for graphics
271  fraction_done_ = 1.0;
272  boinc_fraction_done(fraction_done_);
273 #ifdef BOINC_GRAPHICS
274  update_status_shmem();
275 #endif
276  return true;
277  }
278  return false;
279 }
280 
281 void Boinc::worker_startup() {
282  std::cerr << "BOINC:: Worker startup. " << std::endl;
283  using namespace basic::options;
284  if (!worker_initialized_)
285  utility_exit_with_message( "Must initialize Boinc before calling startup." );
286 
287  if ( option[ OptionKeys::boinc::frame_rate ].user() )
288  project_pref_max_gfx_fps_ = option[ OptionKeys::boinc::frame_rate ];
289  if ( option[ OptionKeys::boinc::cpu_frac].user() )
290  project_pref_max_gfx_cpu_ = option[ OptionKeys::boinc::cpu_frac ];
291  if ( option[ OptionKeys::boinc::cpu_run_time].user() )
292  project_pref_max_cpu_run_time_ = option[ OptionKeys::boinc::cpu_run_time ];
293 
294 /**
295 The following code is meant to keep track of progress upon restarts.
296 Clients that do not keep tasks in memory may stop jobs before they
297 checkpoint which can cause an endless cycle of restarts wasting
298 cpu time. If there are too many restarts without a checkpoint, the
299 job will end (see Boinc::is_finished()).
300 **/
301  // restore checkpoint count
302  utility::io::izstream chckptcnt_istream( BOINC_CHECKPOINT_COUNT_FILE );
303  if ( chckptcnt_istream ) {
304  chckptcnt_istream >> checkpoint_count_;
305  chckptcnt_istream.close();
306  chckptcnt_istream.clear();
307  }
308 
309  // check number of restarts
310  // read last init count
311  int prev_initcnt = 0;
312  int prev_chckptcnt = 0;
313  utility::io::izstream initcnt_istream( BOINC_INIT_COUNT_FILE );
314  if ( initcnt_istream ) {
315  initcnt_istream >> prev_initcnt >> prev_chckptcnt;
316  initcnt_istream.close();
317  initcnt_istream.clear();
318  }
319  // iterate no_progress_init_cnt if checkpoint count has not increased
320  no_progress_init_cnt_ = ( checkpoint_count_ > prev_chckptcnt ) ? prev_initcnt : prev_initcnt + 1;
321 
322  // update counts
323  utility::io::ozstream initcnt_ostream( BOINC_INIT_COUNT_FILE );
324  if ( initcnt_ostream ) {
325  initcnt_ostream << no_progress_init_cnt_ << " " << checkpoint_count_;
326  initcnt_ostream.close();
327  initcnt_ostream.clear();
328  }
329 
330  worker_running_ = true;
332 
333 }
334 
335 void Boinc::worker_shutdown() {
336 
337  // already did this in worker_is_finished, but oh well
338  fraction_done_ = 1.0;
339  boinc_fraction_done(fraction_done_);
340 #ifdef BOINC_GRAPHICS
341  update_status_shmem();
342 #endif
343 
344  worker_running_ = false;
345 
346  // report memory usage
347  // do not edit this because it is parsed from stderr by the
348  // RALPH@home sub batch page to report memory usage
349  std::cerr << "BOINC :: WS_max " << working_set_size_max_val_ << std::endl;
350 
352  std::cerr << "BOINC :: BOINC support services shutting down cleanly ..." << std::endl;
353  boinc_finish(0); // BOINC API call. Does not return.
354 }
355 
356 // the validator requires this format printed to stderr upon job completion
357 void Boinc::worker_finish_summary( const int & num_decoys, const int & attempted_decoys, const int & starting_structs ) {
358  using namespace ObjexxFCL::fmt;
359  // job distributor iterates decoy count before ending so decrement it for the
360  // actual count
361  int decoy_cnt = (num_decoys > 0) ? num_decoys-1 : num_decoys;
362  int attempt_cnt = (attempted_decoys > 0) ? attempted_decoys-1 : attempted_decoys;
363  double cputime = 0.0;
364  boinc_wu_cpu_time(cputime); // get wu cpu run time
365  // prevent invalid cpu times (validator doesnt accept things that took less then 1200 seconds ):
366  if(cputime < 1200 ) cputime = 1201;
367 
368  std::cerr << "======================================================" << std::endl;
369  std::cerr << "DONE ::" << I( 6, std::min( decoy_cnt, std::max(starting_structs,1) ) ) <<
370  " starting structures " << I( 8, cputime ) << " cpu seconds" << std::endl;
371  std::cerr << "This process generated " <<
372  I( 6, decoy_cnt ) << " decoys from " << I( 7, attempt_cnt ) <<
373  " attempts" << std::endl;
374  std::cerr << "======================================================" << std::endl;
375 }
376 
378  checkpoint_count_++;
379  return checkpoint_count_;
380 }
381 
382 // must be called after a checkpoint to keep track of progress
384 
385  int checkpointcount = iterate_checkpoint_count();
386  // save checkpoint count to keep track of progress
387  utility::io::ozstream chckptcnt_ostream( BOINC_CHECKPOINT_COUNT_FILE );
388  if ( chckptcnt_ostream ) {
389  chckptcnt_ostream << checkpointcount;
390  chckptcnt_ostream.close();
391  chckptcnt_ostream.clear();
392  }
393  boinc_checkpoint_completed();
394 
395 }
396 
397 
398 // for watchdog
400  return worker_running_;
401 }
402 
404  worker_running_ = false;
405 }
406 
407 
408 #ifdef BOINC_GRAPHICS
409 
410 //////////////////////////////////////////////////////////////////////////////////////
411 // FOR GRAPHICS SHARED MEMORY
412 //////////////////////////////////////////////////////////////////////////////////////
413 
414 // called by worker
415 void Boinc::set_wu_desc() {
416  using namespace basic::options;
417  if (!shmem_) return;
418  static bool init = false;
419  if (!init){
420  // read description file if one exists and keep row format
421  std::string description_file;
422  if ( option[ OptionKeys::boinc::description_file ].user() ) {
423  std::string description_file = option[ OptionKeys::boinc::description_file ];
424  utility::io::izstream desc_stream( description_file );
425  if ( desc_stream ) {
426  std::vector<std::string> wu_desc_rows;
427  std::string tmpstr;
428  while (desc_stream && !desc_stream.eof()) {
429  desc_stream.getline( tmpstr );
430  utility::trim( tmpstr );
431  if ( tmpstr.length() > 0)
432  wu_desc_rows.push_back( tmpstr );
433  }
434  if (!wait_semaphore()) {
435  core::io::serialization::BUFFER b((char*)(&shmem_->wu_desc_buf),POSE_BUFSIZE);
437  shmem_->wu_desc_exists = 1;
439  }
440  }
441  desc_stream.close();
442  desc_stream.clear();
443  }
444  init = true;
445  }
446 }
447 
448 // CURRENT POSE
449 // called by worker
450 const int Boinc::attach_graphics_current_pose_observer( core::pose::Pose & pose ) {
451  if (!shmem_) return 0;
452  static BoincCurrentPoseObserverOP bpo = new BoincCurrentPoseObserver();
453  bpo->attach_to( pose );
454  return 1;
455 }
456 
457 // NATIVE POSE
458 // called by worker
459 const int Boinc::set_graphics_native_pose( core::pose::Pose & pose ) {
460  if (!shmem_) return 0;
461  if (!wait_semaphore()) {
462  boinc_begin_critical_section();
463  core::io::serialization::BUFFER b((char*)(&shmem_->native_pose_buf),POSE_BUFSIZE);
465  shmem_->native_pose_exists = 1;
466  boinc_end_critical_section();
468  }
469  return 1;
470 }
471 
472 void Boinc::update_mc_trial_info( const int & trial_cnt, const std::string & mover_type ) {
473  if (shmem_) {
474  // stage info
475  core::io::serialization::BUFFER b((char*)(&shmem_->mover_type_text),TEXT_BUFSIZE);
477  // monte carlo mover step count
478  shmem_->mover_mc_trial_count = trial_cnt;
479  shmem_->total_mc_trial_count++;
480  }
481 }
482 
483 
484 // MC LOW ENERGY POSE
485 // called by worker mc object
486 void Boinc::update_graphics_current( core::pose::Pose & pose ) {
487 /*
488  static int count = 0;
489  if (count > 0) {
490  count--;
491  return;
492  }
493  count = SKIP_FOR_EFFICIENCY;
494 */
495  if (!shmem_) return;
496  if (!trywait_semaphore()) {
497  boinc_begin_critical_section();
498  if (pose.total_residue() > 0) {
499  core::io::serialization::BUFFER b((char*)(&shmem_->current_pose_buf ),POSE_BUFSIZE);
501  shmem_->current_pose_exists = 1;
502  }
503  boinc_end_critical_section();
505  }
506 }
507 
508 
509 // MC LOW ENERGY POSE
510 // called by worker mc object
511 void Boinc::update_graphics_low_energy( core::pose::Pose & pose, core::Real low_energy, update_mode_enum update_mode ) {
512  static bool persist = false;
513  if (update_mode == RESET) {
514  persist = false;
515  } else if (update_mode == PERSIST) {
516  persist = true;
517  }
518 /*
519  static int count = 0;
520  if (count > 0) {
521  count--;
522  return;
523  }
524  count = SKIP_FOR_EFFICIENCY;
525 */
526  if (!shmem_ || (persist && update_mode == DEFAULT)) return;
527  if (!trywait_semaphore()) {
528  boinc_begin_critical_section();
529  shmem_->low_energy = low_energy;
530  core::io::serialization::BUFFER b((char*)(&shmem_->low_energy_pose_buf ),POSE_BUFSIZE);
532  shmem_->low_energy_update_cnt++;
533  shmem_->low_energy_pose_exists = 1;
534  boinc_end_critical_section();
536  }
537 }
538 
539 // MC LAST ACCEPTED POSE
540 // called by worker mc object
541 void Boinc::update_graphics_last_accepted( core::pose::Pose & pose, core::Real last_accepted_energy ) {
542 /*
543  static int count = 0;
544  if (count > 0) {
545  count--;
546  return;
547  }
548  count = SKIP_FOR_EFFICIENCY;
549 */
550  if (!shmem_) return;
551  if (!trywait_semaphore()) {
552  boinc_begin_critical_section();
553  shmem_->last_accepted_energy = last_accepted_energy;
554  core::io::serialization::BUFFER b((char*)(&shmem_->last_accepted_pose_buf ),POSE_BUFSIZE);
556  shmem_->last_accepted_pose_exists = 1;
557  boinc_end_critical_section();
559  }
560 }
561 
562 
563 void Boinc::create_shared_memory() {
564  if (shmem_ == NULL) {
565  shmem_ = (BoincSharedMemory*)boinc_graphics_make_shmem(BOINC_SHMEM_NAME, sizeof(BoincSharedMemory));
566  if (!shmem_) {
567  std::cerr << "failed to create shared mem segment: " << BOINC_SHMEM_NAME << " Size: " << sizeof(BoincSharedMemory) << std::endl;
568  } else {
569  std::cout << "Created shared memory segment " << std::endl;
570  }
571  }
572 }
573 
574 void Boinc::attach_shared_memory() {
575  if (shmem_ == NULL) {
576  int attempts = 15;
577  while (attempts>1) {
578  shmem_ = (protocols::boinc::BoincSharedMemory*)boinc_graphics_get_shmem(BOINC_SHMEM_NAME);
579  if (!shmem_) {
580 #ifdef _WIN32
581  Sleep( 1000 );
582 #else
583  sleep(1);
584 #endif
585  attempts--;
586  } else {
587  std::cout << "Attached shared memory segment " << std::endl;
588  return;
589  }
590  }
591  std::cerr << "failed to attach shared mem segment " << std::endl;
592  }
593 }
594 
595 BoincSharedMemory* Boinc::get_shmem() { return shmem_; }
596 
597 void Boinc::update_status_shmem() {
598  if (!shmem_) return;
599  shmem_->fraction_done = fraction_done_; //boinc_get_fraction_done();
600  boinc_wu_cpu_time(cpu_time_);
601  shmem_->cpu_time = cpu_time_; //boinc_worker_thread_cpu_time();
602  shmem_->update_time = dtime();
603  boinc_get_status(&shmem_->status);
604 }
605 
606 
607 // SEMAPHORE STUFF
608 // This stuff should be moved into its own class eventually.
609 //
610 // WINDOWS IMPLEMENTATION MAY HAVE SECURITY ISSUES WHEN RUN AS A SERVICE
611 // I don't know how to set the attributes and what to set it to in CreateSemaphore
612 //
613 // For graphics shared memory data synchronization
614 
615 #ifndef USE_SYSV_SEMAPHORE
616 
617 void Boinc::get_sema_name( char * name ) {
618  // make it slot specific since multiple tasks may run simultaneously
619  APP_INIT_DATA aid;
620  int retval = boinc_get_init_data(aid);
621  if (retval) aid.slot = 0;
622  sprintf(name, "boinc_minirosetta_sema_%d", aid.slot);
623 }
624 
625 #else
626 
627 const key_t Boinc::get_sema_key() {
628  // make it slot specific since multiple tasks may run simultaneously
629  static int sema_key;
630  if (sema_key) return sema_key;
631  APP_INIT_DATA aid;
632  int retval = boinc_get_init_data(aid);
633  if (retval) aid.slot = 0;
634  sema_key = SEMA_KEY_PREFIX + aid.slot;
635  return sema_key;
636 }
637 
638 const int Boinc::destroy_semaphore() {
639  int id = semget(get_sema_key(), 0, 0);
640  if (id < 0) return 0;
641  if (semctl(id, 1, IPC_RMID, 0)) return 0;
642  sem_id_ = 0;
643  return 1;
644 }
645 
646 #endif // USE_SYSV_SEMAPHORE
647 
649 
650 #ifdef _WIN32
651  // Windows semaphore
652 
653  char name[256];
654  get_sema_name(name);
655 
656  CloseHandle(sem_des_);
657  // to do: use a specific security attribute instead of null (default attribute)?
658 
659 // WINDOWS SECURITY DESCRIPTOR FROM BOINC
660 
661  // Win9X doesn't like any reference to a security descriptor.
662  // So if we detect that we are running on the Win9X platform pass
663  // a NULL value for it.
664  //
665 
666  DWORD dwError = 0;
667  DWORD dwRes = 0;
668  PSID pEveryoneSID = NULL;
669  PACL pACL = NULL;
670  PSECURITY_DESCRIPTOR pSD = NULL;
671  EXPLICIT_ACCESS ea;
672  SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
673  SECURITY_ATTRIBUTES sa;
674  OSVERSIONINFO osvi;
675  char global_sema_name[256];
676 
677  osvi.dwOSVersionInfoSize = sizeof(osvi);
678  GetVersionEx(&osvi);
679  if (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
680  sem_des_ = CreateSemaphore(NULL,1,1, name);
681  } else {
682  // Create a well-known SID for the Everyone group.
683  if(!AllocateAndInitializeSid(&SIDAuthWorld, 1,
684  SECURITY_WORLD_RID,
685  0, 0, 0, 0, 0, 0, 0,
686  &pEveryoneSID)
687  ) {
688  std::cerr << "CreateSemaphore failure: AllocateAndInitializeSid Error " << GetLastError() << std::endl;
689  goto Cleanup;
690  }
691 
692  // Initialize an EXPLICIT_ACCESS structure for an ACE.
693  // The ACE will allow Everyone all access to the shared memory object.
694  ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
695  ea.grfAccessPermissions = SEMAPHORE_ALL_ACCESS;
696  ea.grfAccessMode = SET_ACCESS;
697  ea.grfInheritance= NO_INHERITANCE;
698  ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
699  ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
700  ea.Trustee.ptstrName = (LPTSTR) pEveryoneSID;
701 
702  // Create a new ACL that contains the new ACEs.
703  dwRes = SetEntriesInAcl(1, &ea, NULL, &pACL);
704  if (ERROR_SUCCESS != dwRes) {
705  std::cerr << "CreateSemaphore failure: SetEntriesInAcl " << GetLastError() << std::endl;
706  goto Cleanup;
707  }
708 
709  // Initialize a security descriptor.
710  pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
711  if (NULL == pSD) {
712  std::cerr << "CreateSemaphore failure: LocalAlloc Error " << GetLastError() << std::endl;
713  goto Cleanup;
714  }
715 
716  if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) {
717  std::cerr << "CreateSemaphore failure: InitializeSecurityDescriptor Error " << GetLastError() << std::endl;
718  goto Cleanup;
719  }
720 
721  // Add the ACL to the security descriptor.
722  if (!SetSecurityDescriptorDacl(pSD,
723  TRUE, // bDaclPresent flag
724  pACL,
725  FALSE) // not a default DACL
726  ) {
727  std::cerr << "CreateSemaphore failure: SetSecurityDescriptorDacl Error " << GetLastError() << std::endl;
728  goto Cleanup;
729  }
730 
731  // Initialize a security attributes structure.
732  sa.nLength = sizeof (SECURITY_ATTRIBUTES);
733  sa.lpSecurityDescriptor = pSD;
734  sa.bInheritHandle = FALSE;
735 
736  // Use the security attributes to set the security descriptor
737  // when you create a shared file mapping.
738 
739  // Try using 'Global' so that it can cross terminal server sessions
740  // The 'Global' prefix must be included in the shared memory
741  // name if the shared memory segment is going to cross
742  // terminal server session boundaries.
743  //
744  bool try_global = true;
745  if (try_global) {
746  sprintf(global_sema_name, "Global\\%s", name);
747  sem_des_ = CreateSemaphore(&sa, 1, 1, global_sema_name);
748  dwError = GetLastError();
749  if (!sem_des_ && (ERROR_ACCESS_DENIED == dwError)) {
750  // Couldn't use the 'Global' tag, so try the original name.
751  try_global = false;
752  }
753  }
754  if (!try_global) {
755  sem_des_ = CreateSemaphore(&sa, 1, 1, name);
756  dwError = GetLastError();
757  }
758  }
759 
760  if (sem_des_) {
761  if (GetLastError() == ERROR_ALREADY_EXISTS) {
762  CloseHandle(sem_des_);
763  sem_des_ = NULL;
764  }
765  }
766 
767  if (!sem_des_) {
768  std::cerr << "CreateSemaphore failure! Cannot create semaphore!" << std::endl;
769  return 0;
770  } else {
771  std::cout << "Created semaphore" << std::endl;
772  return 1;
773  }
774 
775 Cleanup:
776 
777  if (osvi.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
778  if (pEveryoneSID)
779  FreeSid(pEveryoneSID);
780  if (pACL)
781  LocalFree(pACL);
782  if (pSD)
783  LocalFree(pSD);
784  }
785  return 0;
786 
787 #else
788 
789 #ifndef USE_SYSV_SEMAPHORE
790 // POSIX semaphore
791 
792  char name[256];
793  get_sema_name(name);
794 
795  sem_close(sem_des_);
796  sem_unlink(name);
797  sem_des_ = sem_open(name, O_CREAT|O_EXCL, 0777, 1);
798  if(sem_des_ == (void*)-1) {
799  std::cerr << "sem_open create failure! Cannot create semaphore!" << std::endl;
800  return 0;
801  } else {
802  std::cout << "Created semaphore" << std::endl;
803  return 1;
804  }
805 
806 #else
807 // System V semaphore
808 // POSIX semaphore gave a seg fault when sem_wait was called on linux
809 
810  destroy_semaphore();
811 
812  union semun
813  {
814  int val;
815  struct semid_ds *buf;
816  unsigned short *array;
817  } arg;
818  arg.val = 1;
819 
820  int semid = semget(get_sema_key(), 1, IPC_CREAT|IPC_EXCL|0777);
821  if (semid < 0) {
822  std::cerr << "semget failure! Cannot create semaphore!" << std::endl;
823  return 0;
824  }
825  if( semctl(semid, 0, SETVAL, arg) < 0) {
826  std::cerr << "semctl failure! Cannot set semaphore value!" << std::endl;
827  return 0;
828  }
829  sem_id_ = semid;
830  std::cout << "Created semaphore" << std::endl;
831  return 1;
832 
833 #endif // USE_SYSV_SEMAPHORE
834 #endif // _WIN32
835 
836 }
837 
838 int Boinc::get_semaphore() {
839 
840 #ifdef _WIN32
841  // already opened
842  if (sem_des_) return 1;
843 
844  char name[256];
845  get_sema_name(name);
846 
847  char global_sema_name[256];
848 
849  // The 'Global' prefix must be included in the shared memory
850  // name if the shared memory segment is going to cross
851  // terminal server session boundries.
852  //
853  sprintf(global_sema_name, "Global\\%s", name);
854  sem_des_ = OpenSemaphore(SEMAPHORE_ALL_ACCESS, NULL, global_sema_name);
855  if (!sem_des_) {
856  // Couldn't use the 'Global' tag, so just attempt to use the original
857  // name.
858  //
859  sem_des_ = OpenSemaphore(SEMAPHORE_ALL_ACCESS, NULL, name);
860  }
861  if (!sem_des_) {
862  std::cerr << "OpenSemaphore failure" << std::endl;
863  return 0;
864  }
865  std::cerr << "Opened semaphore" << std::endl;
866  return 1;
867 #else
868 #ifndef USE_SYSV_SEMAPHORE
869 
870  // already opened
871  if (sem_des_) return 1;
872 
873  char name[256];
874  get_sema_name(name);
875  sem_des_ = sem_open(name, 0, 0777, 0);
876  if(sem_des_ == (void*) -1){
877  std::cerr << "sem_open failure" << std::endl;
878  return 0;
879  }
880  std::cout << "Opened semaphore" << std::endl;
881  return 1;
882 #else
883  if (sem_id_) return 1;
884 
885  int semid = semget(get_sema_key(), 0, 0);
886  if (semid < 0) {
887  std::cerr << "semget failure! Cannot open semaphore!" << std::endl;
888  return 1;
889  }
890  sem_id_ = semid;
891  std::cout << "Opened semaphore" << std::endl;
892  return 0;
893 #endif // USE_SYSV_SEMAPHORE
894 #endif // _WIN32
895 }
896 
897 int Boinc::wait_semaphore() {
898 #ifdef _WIN32
899  if (WaitForSingleObject(sem_des_, INFINITE) == WAIT_OBJECT_0) return 0;
900  return 1;
901 #else
902 #ifndef USE_SYSV_SEMAPHORE
903  return sem_wait(sem_des_);
904 #else
905  if (sem_id_ <= 0) return 1;
906  struct sembuf ops[] = {{0, -1, 0}};
907  if (semop(sem_id_, ops, 1)) return 1;
908  return 0;
909 #endif // USE_SYSV_SEMAPHORE
910 #endif // _WIN32
911 }
912 
914 #ifdef _WIN32
915  if (WaitForSingleObject(sem_des_, 0) == WAIT_OBJECT_0) return 0;
916  return 1;
917 #else
918 #ifndef USE_SYSV_SEMAPHORE
919  return sem_trywait(sem_des_);
920 #else
921  if (sem_id_ <= 0) return 1;
922  struct sembuf ops[] = {{0, -1, IPC_NOWAIT}};
923  if (semop(sem_id_, ops, 1)) return 1;
924  return 0;
925 #endif // USE_SYSV_SEMAPHORE
926 #endif // _WIN32
927 }
928 
930 #ifdef _WIN32
931  // returns non-zero on success
932  if (ReleaseSemaphore(sem_des_, 1, NULL)) return 0;
933  return 1;
934 #else
935 #ifndef USE_SYSV_SEMAPHORE
936  return sem_post(sem_des_); // returns zero on success
937 #else
938  if (sem_id_ <= 0) return 1;
939  struct sembuf ops[] = {{0, 1, 0}};
940  if (semop(sem_id_, ops, 1)) return 1;
941  return 0;
942 #endif // USE_SYSV_SEMAPHORE
943 #endif // _WIN32
944 }
945 
946 #ifdef _WIN32
947 HANDLE Boinc::sem_des_ = NULL;
948 #else
949 #ifndef USE_SYSV_SEMAPHORE
950 sem_t* Boinc::sem_des_ = NULL;
951 #else
952 int Boinc::sem_id_;
953 #endif // USE_SYSV_SEMAPHORE
954 #endif // _WIN32
955 
956 // SEMAPHORE STUFF */
957 
958 BoincSharedMemory* Boinc::shmem_ = NULL;
959 
960 #endif // BOINC_GRAPHICS
961 
962 // DATA
963 
964 // worker status
965 bool Boinc::worker_initialized_ = false;
966 double Boinc::fraction_done_ = 0.0;
967 double Boinc::cpu_time_ = 0.0;
968 double Boinc::working_set_size_ = 0.0;
970 
971 // for checking progress
973 int Boinc::decoy_count_ = 0;
975 
976 // for watchdog
977 bool Boinc::worker_running_ = false;
978 
979 // default project prefs
980 // don't change these defaults or boinc users may get upset
981 // these are user changable preferences
985 
986 } // namespace boinc
987 } // namespace protocols
988 
989 #endif