[boinc] 01/03: Imported Upstream version 7.4.1+dfsg
Gianfranco Costamagna
locutusofborg-guest at moszumanska.debian.org
Mon Jun 16 12:10:13 UTC 2014
This is an automated email from the git hooks/post-receive script.
locutusofborg-guest pushed a commit to annotated tag debian/7.4.1+dfsg-1exp1
in repository boinc.
commit 06163b74dc60f750cfd4cb137ae9adcc0b60bef6
Author: Gianfranco Costamagna <costamagnagianfranco at yahoo.it>
Date: Mon Jun 16 12:38:22 2014 +0200
Imported Upstream version 7.4.1+dfsg
---
android/BOINC/AndroidManifest.xml | 6 +-
android/BOINC/res/values/strings.xml | 9 +-
...ardDialog.java => BoincNotExclusiveDialog.java} | 23 +--
.../edu/berkeley/boinc/ProjectDetailsFragment.java | 6 +-
.../src/edu/berkeley/boinc/SplashActivity.java | 27 ++-
.../edu/berkeley/boinc/client/DeviceStatus.java | 2 +-
.../src/edu/berkeley/boinc/client/IMonitor.aidl | 1 +
.../src/edu/berkeley/boinc/client/Monitor.java | 104 ++++------
.../src/edu/berkeley/boinc/mutex/BoincMutex.java | 65 +++++++
api/boinc_api.cpp | 17 +-
api/boinc_api.h | 4 +-
client/app.h | 4 +-
client/app_config.cpp | 1 +
client/app_config.h | 4 +
client/app_control.cpp | 46 +++--
client/client_state.cpp | 7 +
client/coproc_sched.cpp | 17 +-
client/cpu_sched.cpp | 26 ++-
client/cs_apps.cpp | 2 +
client/cs_notice.cpp | 15 ++
client/cs_notice.h | 3 +
client/gpu_opencl.cpp | 4 +-
client/log_flags.cpp | 2 +-
client/makefile_sim | 1 +
client/project.cpp | 11 +-
client/project.h | 8 +-
client/rr_sim.cpp | 6 +-
client/sim.cpp | 15 +-
client/time_stats.cpp | 65 ++++++-
client/work_fetch.cpp | 62 +++---
clientgui/BOINCBaseView.cpp | 57 ++++--
clientgui/BOINCBaseView.h | 7 +-
clientgui/BOINCGUIApp.cpp | 22 ---
clientgui/BOINCGUIApp.h | 2 +-
clientgui/BOINCListCtrl.cpp | 42 +++-
clientgui/BOINCListCtrl.h | 36 +++-
clientgui/DlgDiagnosticLogFlags.cpp | 5 -
clientgui/DlgItemProperties.cpp | 3 +
clientgui/LogBOINC.cpp | 6 +-
clientgui/ViewProjects.cpp | 12 +-
clientgui/ViewStatistics.cpp | 6 +-
clientgui/ViewTransfers.cpp | 8 +-
clientgui/ViewWork.cpp | 11 +-
clientgui/common/wxPieCtrl.cpp | 125 ++++++------
clientgui/common/wxPieCtrl.h | 12 +-
clientgui/stdwx.h | 1 +
configure.ac | 8 +-
db/boinc_db.cpp | 60 +++++-
db/boinc_db.h | 22 +++
db/boinc_db_types.h | 31 +++
db/db_base.cpp | 2 +
db/schema.sql | 1 +
doc/index.php | 3 +
doc/projects.inc | 2 +-
doc/versions.inc | 44 ++---
html/Makefile.am | 2 +-
html/bt/README | 6 +-
html/inc/db_ops.inc | 8 +-
html/inc/forum_db.inc | 4 +
html/inc/sandbox.inc | 1 +
html/inc/text_transform.inc | 21 +-
html/inc/util.inc | 1 +
html/ops/db_update.php | 9 +
html/ops/delete_spammers.php | 111 ++++++++---
html/ops/manage_app_versions.php | 21 +-
html/ops/manage_apps.php | 4 +-
html/ops/mass_email_script.php | 2 +-
html/ops/remind.php | 2 +-
html/ops/white.css | 213 +++++++++++++++------
html/user/ffmail_action.php | 4 +-
html/user/img/75pct_white.png | Bin 178 -> 172 bytes
html/user/img/blue_gradient.png | Bin 840 -> 469 bytes
html/user/img/boinc_fade_600.png | Bin 32331 -> 26979 bytes
html/user/img/boincstats_icon.png | Bin 2115 -> 2099 bytes
html/user/img/bronze.png | Bin 25286 -> 22929 bytes
html/user/img/emphasized_post.png | Bin 214 -> 197 bytes
html/user/img/filtered_post.png | Bin 274 -> 231 bytes
html/user/img/freedc_icon.png | Bin 2537 -> 2422 bytes
html/user/img/gold.png | Bin 23661 -> 21354 bytes
html/user/img/google-button.png | Bin 6617 -> 5616 bytes
html/user/img/gray_gradient.png | Bin 7908 -> 7583 bytes
html/user/img/head_20.png | Bin 3113 -> 360 bytes
html/user/img/hidden.png | Bin 313 -> 250 bytes
html/user/img/paypal_logo.png | Bin 2869 -> 1409 bytes
html/user/img/pct_1.png | Bin 25621 -> 23430 bytes
html/user/img/pct_25.png | Bin 27756 -> 25460 bytes
html/user/img/pct_5.png | Bin 24966 -> 22861 bytes
html/user/img/pm.png | Bin 224 -> 200 bytes
html/user/img/post.png | Bin 2937 -> 220 bytes
html/user/img/rate_negative.png | Bin 220 -> 186 bytes
html/user/img/rate_positive.png | Bin 257 -> 186 bytes
html/user/img/report_post.png | Bin 241 -> 183 bytes
html/user/img/silver.png | Bin 23273 -> 20509 bytes
html/user/img/sticky_locked_post.png | Bin 373 -> 297 bytes
html/user/img/sticky_post.png | Bin 403 -> 330 bytes
html/user/img/unread_locked.png | Bin 213 -> 207 bytes
html/user/img/unread_post.png | Bin 296 -> 204 bytes
html/user/img/yahoo-button.png | Bin 5938 -> 4784 bytes
html/user/rss_main.php | 2 +-
html/user/show_user.php | 3 -
lib/cc_config.cpp | 2 +-
lib/common_defs.h | 23 ++-
lib/coproc.h | 4 +-
lib/filesys.cpp | 48 ++---
lib/gui_rpc_client.h | 2 +
lib/gui_rpc_client_ops.cpp | 10 +
lib/gui_rpc_client_print.cpp | 15 +-
lib/procinfo_unix.cpp | 23 ++-
lib/shmem.cpp | 1 +
py/Boinc/database.py | 3 +-
samples/vboxwrapper/vbox.cpp | 125 +++++++-----
samples/vboxwrapper/vbox.h | 20 +-
samples/vboxwrapper/vboxwrapper.cpp | 211 +++++++++++---------
samples/vboxwrapper/vboxwrapper_win.h | 16 ++
samples/vboxwrapper/vboxwrapper_win.rc | 79 ++++++++
samples/wrapper/wrapper.cpp | 22 ++-
samples/wrapper/wrapper_win.h | 16 ++
samples/wrapper/wrapper_win.rc | 79 ++++++++
sched/Makefile.am | 2 +
sched/db_dump.cpp | 110 ++++++++++-
sched/db_purge.cpp | 2 +
sched/feeder.cpp | 4 +
sched/file_deleter.cpp | 111 ++++++++---
sched/file_upload_handler.cpp | 8 +-
sched/handle_request.cpp | 4 +-
sched/plan_class_spec.cpp | 205 ++++++++++++++------
sched/plan_class_spec.h | 4 +
sched/pshelper | 3 +
sched/sched_array.cpp | 107 +----------
sched/sched_array.h | 1 -
sched/sched_assign.cpp | 80 ++++++--
sched/sched_check.cpp | 6 +-
sched/sched_check.h | 1 +
sched/sched_locality.cpp | 99 ++++++++--
sched/sched_main.cpp | 8 +-
sched/sched_nci.cpp | 188 ++++++++++++++++++
sched/{sched_array.h => sched_nci.h} | 3 +-
sched/sched_resend.cpp | 20 ++
sched/sched_result.cpp | 12 +-
sched/sched_score.cpp | 2 +-
sched/sched_send.cpp | 83 +++++++-
sched/sched_send.h | 1 -
sched/sched_version.cpp | 9 +
sched/start | 16 +-
sched/transitioner.cpp | 8 +
sched/validator.cpp | 169 ++++++++++------
tools/backend_lib.cpp | 2 +-
tools/create_work.cpp | 26 ++-
tools/update_versions | 5 +-
version.h.in | 6 +
150 files changed, 2486 insertions(+), 1023 deletions(-)
diff --git a/android/BOINC/AndroidManifest.xml b/android/BOINC/AndroidManifest.xml
index e4e15a1..c22c378 100644
--- a/android/BOINC/AndroidManifest.xml
+++ b/android/BOINC/AndroidManifest.xml
@@ -20,8 +20,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="edu.berkeley.boinc"
android:installLocation="internalOnly"
- android:versionCode="72"
- android:versionName="7.3.19" > <!-- installation on SD card would break boot receiver -->
+ android:versionCode="76"
+ android:versionName="7.4.1" > <!-- installation on SD card would break boot receiver -->
<!-- Add Google Play store metadata informing the store we can run on tablets and other large screen devices -->
@@ -138,7 +138,7 @@
<data android:path="edu.berkeley.boinc" android:scheme="package" />
</intent-filter>
</receiver>
- <activity android:name=".ForwardDialog" android:theme="@android:style/Theme.Dialog">
+ <activity android:name=".BoincNotExclusiveDialog" android:theme="@android:style/Theme.Dialog">
</activity>
</application>
diff --git a/android/BOINC/res/values/strings.xml b/android/BOINC/res/values/strings.xml
index 5552794..f0fa05f 100644
--- a/android/BOINC/res/values/strings.xml
+++ b/android/BOINC/res/values/strings.xml
@@ -378,10 +378,9 @@
<string name="notice_notification_single_header">New notice from</string> <!-- e.g. New notice from SETI at HOME -->
<string name="notice_notification_multiple_header">new notices</string> <!-- e.g. 3 new notices -->
- <!-- Power To Give compitability -->
- <string name="ptg_dialog_header">Power To Give detected</string>
- <string name="ptg_dialog_text">Your device has installed Power To Give. Do you want to launch Power To Give?</string>
- <string name="ptg_dialog_launch">Launch</string>
- <string name="ptg_dialog_exit">Exit</string>
+ <!-- multi BOINC compitability -->
+ <string name="nonexcl_dialog_header">Volunteer computing app detected</string>
+ <string name="nonexcl_dialog_text">Another volunteer computing app is running on this device. Only one version can run at a time.</string>
+ <string name="nonexcl_dialog_exit">Exit</string>
</resources>
diff --git a/android/BOINC/src/edu/berkeley/boinc/ForwardDialog.java b/android/BOINC/src/edu/berkeley/boinc/BoincNotExclusiveDialog.java
similarity index 62%
rename from android/BOINC/src/edu/berkeley/boinc/ForwardDialog.java
rename to android/BOINC/src/edu/berkeley/boinc/BoincNotExclusiveDialog.java
index bba7715..cf165db 100644
--- a/android/BOINC/src/edu/berkeley/boinc/ForwardDialog.java
+++ b/android/BOINC/src/edu/berkeley/boinc/BoincNotExclusiveDialog.java
@@ -21,34 +21,23 @@ package edu.berkeley.boinc;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
-import android.content.Intent;
import android.os.Bundle;
/**
- * Shows dialog to forward or exit if other BONIC based application detected on device.
+ * Shows dialog to exit, if another BONIC based application detected on device.
*/
-public class ForwardDialog extends Activity {
+public class BoincNotExclusiveDialog extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setMessage(getString(R.string.ptg_dialog_text))
+ builder.setMessage(getString(R.string.nonexcl_dialog_text))
.setCancelable(false)
- .setTitle(getString(R.string.ptg_dialog_header))
- .setPositiveButton(getString(R.string.ptg_dialog_launch), new DialogInterface.OnClickListener() {
+ .setTitle(getString(R.string.nonexcl_dialog_header))
+ .setNeutralButton(getString(R.string.nonexcl_dialog_exit), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
- Intent startPTGIntent = new Intent();
- startPTGIntent.setClassName("com.htc.ptg", "com.htc.ptg.SplashActivity");
- startActivity(startPTGIntent);
finish();
- }
- })
- .setNegativeButton(getString(R.string.ptg_dialog_exit), new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- //BOINCActivity.this.finish();
- finish();
- }
+ }
}).show();
-
}
}
diff --git a/android/BOINC/src/edu/berkeley/boinc/ProjectDetailsFragment.java b/android/BOINC/src/edu/berkeley/boinc/ProjectDetailsFragment.java
index 7d63bc7..f011f18 100644
--- a/android/BOINC/src/edu/berkeley/boinc/ProjectDetailsFragment.java
+++ b/android/BOINC/src/edu/berkeley/boinc/ProjectDetailsFragment.java
@@ -140,6 +140,10 @@ public class ProjectDetailsFragment extends Fragment {
@Override
public void onPrepareOptionsMenu(Menu menu) {
+
+ super.onPrepareOptionsMenu(menu);
+ if(project == null) return;
+
// no new tasks, adapt based on status
MenuItem nnt = menu.findItem(R.id.projects_control_nonewtasks);
if(project.dont_request_more_work) nnt.setTitle(R.string.projects_control_allownewtasks);
@@ -153,8 +157,6 @@ public class ProjectDetailsFragment extends Fragment {
// detach, only show when project not managed
MenuItem remove = menu.findItem(R.id.projects_control_remove);
if(project.attached_via_acct_mgr) remove.setVisible(false);
-
- super.onPrepareOptionsMenu(menu);
}
@Override
diff --git a/android/BOINC/src/edu/berkeley/boinc/SplashActivity.java b/android/BOINC/src/edu/berkeley/boinc/SplashActivity.java
index 2e548d9..7899ee0 100644
--- a/android/BOINC/src/edu/berkeley/boinc/SplashActivity.java
+++ b/android/BOINC/src/edu/berkeley/boinc/SplashActivity.java
@@ -31,7 +31,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -60,6 +59,8 @@ public class SplashActivity extends Activity {
mIsBound = true;
monitor = IMonitor.Stub.asInterface(service);
try {
+ // check whether BOINC was able to acquire mutex
+ if(!monitor.boincMutexAcquired()) showNotExclusiveDialog();
// read log level from monitor preferences and adjust accordingly
Logging.setLogLevel(monitor.getLogLevel());
} catch (RemoteException e) {Log.w(Logging.TAG, "initializing log level failed.");}
@@ -114,21 +115,6 @@ public class SplashActivity extends Activity {
//initialize logging with highest verbosity, read actual value when monitor connected.
Logging.setLogLevel(5);
-
- // check whether PTG is installed, if not, do not start.
- // this check has to be similar to client.Monitor.onCreate()
- try {
- getPackageManager().getPackageInfo("com.htc.ptg", 0);
- if ("com.android.vending".equals(getPackageManager().getInstallerPackageName("com.htc.ptg")) // check if installed through PlayStore
- || (getPackageManager().getPackageInfo("com.htc.ptg", 0).applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == ApplicationInfo.FLAG_SYSTEM) { // check if pre-installed
- if(Logging.ERROR) Log.e(Logging.TAG, "SplashActivity: PTG found, show forward dialog.");
- Intent startPTGIntent = new Intent();
- startPTGIntent.setClassName("edu.berkeley.boinc", "edu.berkeley.boinc.ForwardDialog");
- startActivity(startPTGIntent);
- finish();
- return;
- } else if(Logging.WARNING) Log.w(Logging.TAG,"SplashActivity: com.htc.ptg found, but unknown vendor, start BOINC...");
- } catch (Exception ex) {} // NOP Package not found exception.
//bind monitor service
doBindService();
@@ -181,4 +167,13 @@ public class SplashActivity extends Activity {
mIsBound = false;
}
}
+
+ private void showNotExclusiveDialog() {
+ if(Logging.ERROR) Log.e(Logging.TAG, "SplashActivity: another BOINC app found, show dialog.");
+ Intent notExclusiveDialogIntent = new Intent();
+ notExclusiveDialogIntent.setClassName("edu.berkeley.boinc", "edu.berkeley.boinc.BoincNotExclusiveDialog");
+ startActivity(notExclusiveDialogIntent);
+ finish();
+ return;
+ }
}
diff --git a/android/BOINC/src/edu/berkeley/boinc/client/DeviceStatus.java b/android/BOINC/src/edu/berkeley/boinc/client/DeviceStatus.java
index f532cbb..1153f42 100644
--- a/android/BOINC/src/edu/berkeley/boinc/client/DeviceStatus.java
+++ b/android/BOINC/src/edu/berkeley/boinc/client/DeviceStatus.java
@@ -190,7 +190,7 @@ public class DeviceStatus {
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
if(level == -1 || scale == -1) throw new Exception("battery level parsing error");
int batteryPct = (int) ((level / (float) scale) * 100); // always rounds down
- if(batteryPct < 1 || batteryPct > 100) throw new Exception("battery level parsing error");
+ if(batteryPct < 0 || batteryPct > 100) throw new Exception("battery level parsing error");
if(batteryPct != status.battery_charge_pct) {
status.battery_charge_pct = batteryPct;
change = true;
diff --git a/android/BOINC/src/edu/berkeley/boinc/client/IMonitor.aidl b/android/BOINC/src/edu/berkeley/boinc/client/IMonitor.aidl
index 77be728..b999800 100644
--- a/android/BOINC/src/edu/berkeley/boinc/client/IMonitor.aidl
+++ b/android/BOINC/src/edu/berkeley/boinc/client/IMonitor.aidl
@@ -60,6 +60,7 @@ List<ProjectInfo> getAttachableProjects(); // clientInterface.getAttachableProj
ProjectInfo getProjectInfo(String url); // clientInterface.getProjectInfo(String url);
/////// general //////////////////////////////////////////
+boolean boincMutexAcquired(); // implment: call Monitor.boincMutexAcquired();
void forceRefresh(); // implement: call Monitor.forceRefresh();
boolean isStationaryDeviceSuspected(); // implement: call Monitor.getDeviceStatus().isStationaryDevice();
int getBatteryChargeStatus(); // implement: return getDeviceStatus().getStatus().battery_charge_pct;
diff --git a/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java b/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java
index ae6a91c..8a3ab22 100644
--- a/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java
+++ b/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java
@@ -34,15 +34,11 @@ import java.util.List;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
-
-import android.app.NotificationManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Build;
@@ -51,7 +47,7 @@ import android.os.PowerManager;
import android.os.RemoteException;
import android.util.Log;
import edu.berkeley.boinc.R;
-import edu.berkeley.boinc.SplashActivity;
+import edu.berkeley.boinc.mutex.BoincMutex;
import edu.berkeley.boinc.rpc.AccountIn;
import edu.berkeley.boinc.rpc.AccountOut;
import edu.berkeley.boinc.rpc.AcctMgrRPCReply;
@@ -78,14 +74,13 @@ import edu.berkeley.boinc.rpc.AcctMgrInfo;
*/
public class Monitor extends Service {
+ private static BoincMutex mutex = new BoincMutex(); // holds the BOINC mutex, only compute if acquired
private static ClientStatus clientStatus; //holds the status of the client as determined by the Monitor
private static AppPreferences appPrefs; //hold the status of the app, controlled by AppPreferences
private static DeviceStatus deviceStatus; // holds the status of the device, i.e. status information that can only be obtained trough Java APIs
public ClientInterfaceImplementation clientInterface = new ClientInterfaceImplementation(); //provides functions for interaction with client via rpc
- public static Boolean monitorActive = false;
-
// XML defined variables, populated in onCreate
private String fileNameClient;
private String fileNameCLI;
@@ -100,13 +95,13 @@ public class Monitor extends Service {
private Timer updateTimer = new Timer(true); // schedules frequent client status update
private TimerTask statusUpdateTask = new StatusUpdateTimerTask();
- private boolean updateBroadcastEnabled = true;
+ private boolean updateBroadcastEnabled = false;
private Integer screenOffStatusOmitCounter = 0;
// screen on/off updated by screenOnOffBroadcastReceiver
private boolean screenOn = false;
- private boolean forceReinstall = true; // for debugging purposes //TODO
+ private boolean forceReinstall = false; // for debugging purposes //TODO
@Override
public IBinder onBind(Intent intent) {
@@ -119,27 +114,6 @@ public class Monitor extends Service {
Log.d(Logging.TAG,"Monitor onCreate()");
- // check whether PTG is installed, if so, do not start service.
- // this check has to be similar to SpalshActivity.onCreate()
- try {
- getPackageManager().getPackageInfo("com.htc.ptg", 0);
- if ("com.android.vending".equals(getPackageManager().getInstallerPackageName("com.htc.ptg")) // check if installed through PlayStore
- || (getPackageManager().getPackageInfo("com.htc.ptg", 0).applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == ApplicationInfo.FLAG_SYSTEM) { // check if pre-installed
- Log.e(Logging.TAG,"Monitor onCreate(): PTG found, do not start.");
- stopSelf();
- return;
- } else Log.w(Logging.TAG,"Monitor.onCreate(): com.htc.ptg found, but unknown vendor, start...");
- }
- catch (NameNotFoundException ex) {
- // catch exception and then skip once Power To Give is not found.
- }
-
- // register listener for installation of incompatible apps
- IntentFilter packageAddedIF = new IntentFilter();
- packageAddedIF.addAction(Intent.ACTION_PACKAGE_ADDED);
- packageAddedIF.addDataScheme("package");
- registerReceiver(packageAddedReceiver, packageAddedIF);
-
// populate attributes with XML resource values
boincWorkingDir = getString(R.string.client_path);
fileNameClient = getString(R.string.client_name);
@@ -171,11 +145,6 @@ public class Monitor extends Service {
IntentFilter offFilter = new IntentFilter (Intent.ACTION_SCREEN_OFF);
registerReceiver(screenOnOffReceiver, onFilter);
registerReceiver(screenOnOffReceiver, offFilter);
-
- // register and start update task
- // using .scheduleAtFixedRate() can cause a series of bunched-up runs
- // when previous executions are delayed (e.g. during clientSetup() )
- updateTimer.schedule(statusUpdateTask, 0, clientStatusInterval);
}
@Override
@@ -190,7 +159,6 @@ public class Monitor extends Service {
clientInterface.close();
try {
- unregisterReceiver(packageAddedReceiver);
// remove screen on/off receiver
unregisterReceiver(screenOnOffReceiver);
} catch (Exception ex) {}
@@ -198,6 +166,8 @@ public class Monitor extends Service {
updateBroadcastEnabled = false; // prevent broadcast from currently running update task
updateTimer.cancel(); // cancel task
+ mutex.release(); // release BOINC mutex
+
// release locks, if held.
try {
clientStatus.setWakeLock(false);
@@ -207,8 +177,19 @@ public class Monitor extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
- //this gets called after startService(intent) (either by BootReceiver or AndroidBOINCActivity, depending on the user's autostart configuration)
+ //this gets called after startService(intent) (either by BootReceiver or SplashActivity, depending on the user's autostart configuration)
if(Logging.ERROR) Log.d(Logging.TAG, "Monitor onStartCommand()");
+
+ // try to acquire BOINC mutex
+ // run here in order to recover, if mutex holding app gets closed.
+ if(!updateBroadcastEnabled && mutex.acquire()) {
+ updateBroadcastEnabled = true;
+ // register and start update task
+ // using .scheduleAtFixedRate() can cause a series of bunched-up runs
+ // when previous executions are delayed (e.g. during clientSetup() )
+ updateTimer.schedule(statusUpdateTask, 0, clientStatusInterval);
+ }
+ if(!mutex.acquired) if(Logging.ERROR) Log.e(Logging.TAG, "Monitor.onStartCommand: mutex acquisition failed, do not start BOINC.");
// execute action if one is explicitly requested (e.g. from notification)
if(intent != null) {
@@ -278,10 +259,20 @@ public class Monitor extends Service {
// --end-- singleton getter
// public methods for Activities
+ /**
+ * Indicates whether service was able to obtain BOINC mutex.
+ * If not, BOINC has not started and all other calls will fail.
+ * @return BOINC mutex acquisition successful
+ */
+ public boolean boincMutexAcquired() {
+ return mutex.acquired;
+ }
+
/**
* Force refresh of client status data model, will fire Broadcast upon success.
*/
public void forceRefresh() {
+ if(!mutex.acquired) return; // do not try to update if client is not running
if(Logging.DEBUG) Log.d(Logging.TAG,"forceRefresh()");
try{
updateTimer.schedule(new StatusUpdateTimerTask(), 0);
@@ -885,40 +876,6 @@ public class Monitor extends Service {
}
}
};
-
- /**
- * broadcast receiver to detect added/installed packages. If package not compatible with BOINC
- * shutdown service.
- */
- BroadcastReceiver packageAddedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)) {
- if (intent.hasExtra(Intent.EXTRA_UID)) {
- int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
- if (uid > -1) {
- String[] packages = context.getPackageManager().getPackagesForUid(uid);
- for (String pkg : packages) {
- if (pkg.equals("com.htc.ptg")) {
- if(Logging.ERROR) Log.d(Logging.TAG,"packageAddedReceiver: PTG added, stop Monitor...");
- // cancel notifications
- NotificationManager nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
- nm.cancelAll();
- // cancel async task
- statusUpdateTask.cancel();
- // open exit splash activity. implies destruction (and unbound) of all others
- Intent exitSplash = new Intent(getApplicationContext(),SplashActivity.class);
- exitSplash.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- startActivity(exitSplash);
- stopSelf();
- break;
- }
- }
- }
- }
- }
- }
- };
// --end-- broadcast receiver
// async tasks
@@ -1290,6 +1247,11 @@ public class Monitor extends Service {
public ProjectInfo getProjectInfo(String url) throws RemoteException {
return clientInterface.getProjectInfo(url);
}
+
+ @Override
+ public boolean boincMutexAcquired() throws RemoteException {
+ return mutex.acquired;
+ }
};
// --end-- remote service
}
diff --git a/android/BOINC/src/edu/berkeley/boinc/mutex/BoincMutex.java b/android/BOINC/src/edu/berkeley/boinc/mutex/BoincMutex.java
new file mode 100644
index 0000000..72761a2
--- /dev/null
+++ b/android/BOINC/src/edu/berkeley/boinc/mutex/BoincMutex.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * This file is part of BOINC.
+ * http://boinc.berkeley.edu
+ * Copyright (C) 2014 University of California
+ *
+ * BOINC is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * BOINC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with BOINC. If not, see <http://www.gnu.org/licenses/>.
+ ******************************************************************************/
+package edu.berkeley.boinc.mutex;
+
+import java.io.IOException;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+
+/**
+ * Mediates usage of device for volunteer computing. Acquire this lock before executing computations.
+ * Indicates whether device is already in use by another BOINC-based Android application.
+ *
+ * @author Joachim Fritzsch
+ */
+public class BoincMutex {
+
+ private static final String boincMutex = "boinc_mutex";
+ private LocalSocket socket = new LocalSocket();
+ public boolean acquired = false;
+
+ /**
+ * Tries to acquire BOINC mutex. Only run computations on device, if this function returned true.
+ * Mutex is freed automatically when application closes, to release earlier, call release()
+ *
+ * @return mutex acquisition successful
+ */
+ public boolean acquire() {
+ if(socket.isBound()) return true;
+ try {
+ socket.bind(new LocalSocketAddress(boincMutex));
+ acquired = true;
+ } catch (IOException e) {}
+ return socket.isBound();
+ }
+
+ /**
+ * Releases BOINC mutex. Re-acquire mutex before resuming computation.
+ */
+ public void release() {
+ if(socket.isBound()) {
+ try {
+ socket.close();
+ acquired = false;
+ } catch (IOException e) {}
+ }
+ }
+
+
+}
diff --git a/api/boinc_api.cpp b/api/boinc_api.cpp
index 77602d9..f24f8c2 100644
--- a/api/boinc_api.cpp
+++ b/api/boinc_api.cpp
@@ -137,7 +137,7 @@ static volatile bool have_new_trickle_up = false;
static volatile bool have_trickle_down = true;
// on first call, scan slot dir for msgs
static volatile int heartbeat_giveup_count;
- // interrupt count value at which to give up on core client
+ // interrupt count value at which to give up on client
#ifdef _WIN32
static volatile int nrunning_ticks = 0;
#endif
@@ -175,7 +175,7 @@ int app_min_checkpoint_period = 0;
// to the client, and of checkpoint enabling.
#define HEARTBEAT_GIVEUP_SECS 30
#define HEARTBEAT_GIVEUP_COUNT ((int)(HEARTBEAT_GIVEUP_SECS/TIMER_PERIOD))
- // quit if no heartbeat from core in this #interrupts
+ // quit if no heartbeat from client in this #interrupts
#define LOCKFILE_TIMEOUT_PERIOD 35
// quit if we cannot aquire slot lock file in this #secs after startup
@@ -356,7 +356,7 @@ double boinc_worker_thread_cpu_time() {
return cpu;
}
-// Communicate to the core client (via shared mem)
+// Communicate to the client (via shared mem)
// the current CPU time and fraction done.
// NOTE: various bugs could cause some of these FP numbers to be enormous,
// possibly overflowing the buffer.
@@ -678,7 +678,7 @@ static void send_trickle_up_msg() {
}
}
-// NOTE: a non-zero status tells the core client that we're exiting with
+// NOTE: a non-zero status tells the client that we're exiting with
// an "unrecoverable error", which will be reported back to server.
// A zero exit-status tells the client we've successfully finished the result.
//
@@ -703,7 +703,7 @@ int boinc_finish(int status) {
return 0; // never reached
}
-int boinc_temporary_exit(int delay, const char* reason) {
+int boinc_temporary_exit(int delay, const char* reason, bool is_notice) {
FILE* f = fopen(TEMPORARY_EXIT_FILE, "w");
if (!f) {
return ERR_FOPEN;
@@ -711,6 +711,9 @@ int boinc_temporary_exit(int delay, const char* reason) {
fprintf(f, "%d\n", delay);
if (reason) {
fprintf(f, "%s\n", reason);
+ if (is_notice) {
+ fprintf(f, "notice\n");
+ }
}
fclose(f);
boinc_exit(0);
@@ -1128,7 +1131,7 @@ static void timer_handler() {
if (!boinc_status.suspended) {
running_interrupt_count++;
}
- // handle messages from the core client
+ // handle messages from the client
//
if (app_client_shm) {
if (options.check_heartbeat) {
@@ -1157,7 +1160,7 @@ static void timer_handler() {
}
}
- // see if the core client has died, which means we need to die too
+ // see if the client has died, which means we need to die too
// (unless we're in a critical section)
//
if (in_critical_section==0 && options.check_heartbeat) {
diff --git a/api/boinc_api.h b/api/boinc_api.h
index 1ffbf33..9c56df3 100644
--- a/api/boinc_api.h
+++ b/api/boinc_api.h
@@ -142,7 +142,9 @@ extern int boinc_report_app_status_aux(
double cpu_time, double checkpoint_cpu_time, double _fraction_done,
int other_pid, double bytes_sent, double bytes_received
);
-extern int boinc_temporary_exit(int delay, const char* reason=NULL);
+extern int boinc_temporary_exit(
+ int delay, const char* reason=NULL, bool is_notice=false
+);
/////////// API ENDS HERE
diff --git a/client/app.h b/client/app.h
index 8221f8c..03263bf 100644
--- a/client/app.h
+++ b/client/app.h
@@ -261,7 +261,7 @@ struct ACTIVE_TASK {
void handle_exited_app(int stat);
#endif
void handle_premature_exit(bool&);
- void handle_temporary_exit(bool&, double, const char*);
+ void handle_temporary_exit(bool&, double, const char*, bool);
bool check_max_disk_exceeded();
@@ -271,7 +271,7 @@ struct ACTIVE_TASK {
double est_dur();
int read_stderr_file();
bool finish_file_present();
- bool temporary_exit_file_present(double&, char*);
+ bool temporary_exit_file_present(double&, char*, bool&);
void init_app_init_data(APP_INIT_DATA&);
int write_app_init_file(APP_INIT_DATA&);
int move_trickle_file();
diff --git a/client/app_config.cpp b/client/app_config.cpp
index 13b2a75..e65bee0 100644
--- a/client/app_config.cpp
+++ b/client/app_config.cpp
@@ -191,6 +191,7 @@ void max_concurrent_init() {
// It will be restored on next scheduler RPC.
//
static void clear_app_config(PROJECT* p) {
+ p->app_configs.clear();
for (unsigned int i=0; i<gstate.apps.size(); i++) {
APP* app = gstate.apps[i];
if (app->project != p) continue;
diff --git a/client/app_config.h b/client/app_config.h
index d2e9eac..81c6c11 100644
--- a/client/app_config.h
+++ b/client/app_config.h
@@ -54,6 +54,10 @@ struct APP_CONFIGS {
int parse(XML_PARSER&, PROJECT*);
int parse_file(FILE*, PROJECT*);
void config_app_versions(PROJECT*, bool show_warnings);
+ void clear() {
+ app_configs.clear();
+ app_version_configs.clear();
+ }
};
extern bool have_max_concurrent;
diff --git a/client/app_control.cpp b/client/app_control.cpp
index ad143ea..a8f6010 100644
--- a/client/app_control.cpp
+++ b/client/app_control.cpp
@@ -357,7 +357,7 @@ void ACTIVE_TASK::handle_premature_exit(bool& will_restart) {
// handle a temporary exit
//
void ACTIVE_TASK::handle_temporary_exit(
- bool& will_restart, double backoff, const char* reason
+ bool& will_restart, double backoff, const char* reason, bool is_notice
) {
premature_exit_count++;
if (premature_exit_count > 100) {
@@ -367,10 +367,16 @@ void ACTIVE_TASK::handle_temporary_exit(
gstate.report_result_error(*result, "too many boinc_temporary_exit()s");
result->set_state(RESULT_ABORTED, "handle_temporary_exit");
} else {
- if (log_flags.task_debug) {
- msg_printf(result->project, MSG_INFO,
- "[task] task called temporary_exit(%f, %s)", backoff, reason
+ if (is_notice) {
+ msg_printf(result->project, MSG_USER_ALERT,
+ "Can't run task: %s", reason
);
+ } else {
+ if (log_flags.task_debug) {
+ msg_printf(result->project, MSG_INFO,
+ "[task] task called temporary_exit(%f, %s)", backoff, reason
+ );
+ }
}
will_restart = true;
result->schedule_backoff = gstate.now + backoff;
@@ -442,10 +448,11 @@ void ACTIVE_TASK::handle_exited_app(int stat) {
break;
}
double x;
+ bool is_notice;
char buf[256];
strcpy(buf, "");
- if (temporary_exit_file_present(x, buf)) {
- handle_temporary_exit(will_restart, x, buf);
+ if (temporary_exit_file_present(x, buf, is_notice)) {
+ handle_temporary_exit(will_restart, x, buf, is_notice);
} else {
handle_premature_exit(will_restart);
}
@@ -489,16 +496,9 @@ void ACTIVE_TASK::handle_exited_app(int stat) {
double x;
char buf[256];
- if (temporary_exit_file_present(x, buf)) {
- if (log_flags.task_debug) {
- msg_printf(result->project, MSG_INFO,
- "[task] task called temporary_exit(%f)", x
- );
- }
- set_task_state(PROCESS_UNINITIALIZED, "temporary exit");
- will_restart = true;
- result->schedule_backoff = gstate.now + x;
- safe_strcpy(result->schedule_backoff_reason, buf);
+ bool is_notice;
+ if (temporary_exit_file_present(x, buf, is_notice)) {
+ handle_temporary_exit(will_restart, x, buf, is_notice);
} else {
if (log_flags.task_debug) {
msg_printf(result->project, MSG_INFO,
@@ -591,8 +591,10 @@ bool ACTIVE_TASK::finish_file_present() {
return (boinc_file_exists(path) != 0);
}
-bool ACTIVE_TASK::temporary_exit_file_present(double& x, char* buf) {
- char path[MAXPATHLEN];
+bool ACTIVE_TASK::temporary_exit_file_present(
+ double& x, char* buf, bool& is_notice
+) {
+ char path[MAXPATHLEN], buf2[256];
sprintf(path, "%s/%s", slot_dir, TEMPORARY_EXIT_FILE);
FILE* f = fopen(path, "r");
if (!f) return false;
@@ -607,6 +609,12 @@ bool ACTIVE_TASK::temporary_exit_file_present(double& x, char* buf) {
(void) fgets(buf, 256, f); // read the \n
(void) fgets(buf, 256, f);
strip_whitespace(buf);
+ is_notice = false;
+ if (fgets(buf2, 256, f)) {
+ if (strstr(buf2, "notice")) {
+ is_notice = true;
+ }
+ }
fclose(f);
return true;
}
@@ -1089,6 +1097,8 @@ int ACTIVE_TASK_SET::abort_project(PROJECT* project) {
while (task_iter != active_tasks.end()) {
atp = *task_iter;
if (atp->result->project == project) {
+ client_clean_out_dir(atp->slot_dir, "abort_project()");
+ remove_project_owned_dir(atp->slot_dir);
task_iter = active_tasks.erase(task_iter);
delete atp;
} else {
diff --git a/client/client_state.cpp b/client/client_state.cpp
index dde7acf..e333eb4 100644
--- a/client/client_state.cpp
+++ b/client/client_state.cpp
@@ -1956,6 +1956,7 @@ int CLIENT_STATE::reset_project(PROJECT* project, bool detaching) {
// - delete all file infos
// - delete account file
// - delete project directory
+// - delete various per-project files
//
int CLIENT_STATE::detach_project(PROJECT* project) {
vector<PROJECT*>::iterator project_iter;
@@ -2030,6 +2031,12 @@ int CLIENT_STATE::detach_project(PROJECT* project) {
);
}
+ // remove miscellaneous per-project files
+ //
+ //job_log_filename(*project, path, sizeof(path));
+ //boinc_delete_file(path);
+ delete_project_notice_files(project);
+
rss_feeds.update_feed_list();
delete project;
diff --git a/client/coproc_sched.cpp b/client/coproc_sched.cpp
index 5459992..14babce 100644
--- a/client/coproc_sched.cpp
+++ b/client/coproc_sched.cpp
@@ -27,6 +27,13 @@
using std::vector;
+#if 0
+#define COPROC_DEBUG(x) x
+#else
+#define COPROC_DEBUG(X)
+#endif
+
+
////////// Coprocessor scheduling ////////////////
//
// theory of operation:
@@ -73,9 +80,15 @@ using std::vector;
// - OpenCL availability (relevant if use_all_gpus set)
//
static inline bool can_use_gpu(RESULT* rp, COPROC* cp, int i) {
- if (gpu_excluded(rp->app, *cp, i)) return false;
+ if (gpu_excluded(rp->app, *cp, i)) {
+ COPROC_DEBUG(msg_printf(rp->project, MSG_INFO, "GPU %d is excluded for %s", i, rp->name));
+ return false;
+ }
if (rp->avp->is_opencl()) {
- if (!cp->have_opencls[i]) return false;
+ if (!cp->instance_has_opencl[i]) {
+ COPROC_DEBUG(msg_printf(rp->project, MSG_INFO, "GPU %d can't do OpenCL for %s", i, rp->name));
+ return false;
+ }
}
return true;
}
diff --git a/client/cpu_sched.cpp b/client/cpu_sched.cpp
index 29eb03b..dd4177d 100644
--- a/client/cpu_sched.cpp
+++ b/client/cpu_sched.cpp
@@ -167,26 +167,22 @@ struct PROC_RESOURCES {
// we've decided to add this to the runnable list; update bookkeeping
//
- void schedule(RESULT* rp, ACTIVE_TASK* atp, const char* description) {
+ void schedule(RESULT* rp, const char* description) {
if (log_flags.cpu_sched_debug) {
msg_printf(rp->project, MSG_INFO,
- "[cpu_sched_debug] scheduling %s (%s) (prio %f)",
+ "[cpu_sched_debug] add to run list: %s (%s) (prio %f)",
rp->name, description,
rp->project->sched_priority
);
}
if (rp->uses_coprocs()) {
- // if this job is currently running,
- // and the resource type has exclusions,
- // don't reserve instances;
- // This allows more jobs in the run list
- // and avoids a starvation case
+ // if the resource type has exclusions, don't reserve instances.
+ // It means that the run list will include all jobs
+ // for that resource type.
+ // Inefficient, but necessary to avoid starvation cases.
//
int rt = rp->avp->gpu_usage.rsc_type;
- bool dont_reserve =
- rsc_work_fetch[rt].has_exclusions
- && atp != NULL
- && atp->is_gpu_task_running();
+ bool dont_reserve = rsc_work_fetch[rt].has_exclusions;
if (!dont_reserve) {
reserve_coprocs(*rp);
}
@@ -820,7 +816,7 @@ void add_coproc_jobs(
rp->already_selected = true;
atp = gstate.lookup_active_task_by_result(rp);
if (!proc_rsc.can_schedule(rp, atp)) continue;
- proc_rsc.schedule(rp, atp, "coprocessor job, EDF");
+ proc_rsc.schedule(rp, "coprocessor job, EDF");
rp->project->rsc_pwf[rsc_type].deadlines_missed_copy--;
rp->edf_scheduled = true;
run_list.push_back(rp);
@@ -837,7 +833,7 @@ void add_coproc_jobs(
rp->already_selected = true;
atp = gstate.lookup_active_task_by_result(rp);
if (!proc_rsc.can_schedule(rp, atp)) continue;
- proc_rsc.schedule(rp, atp, "coprocessor job, FIFO");
+ proc_rsc.schedule(rp, "coprocessor job, FIFO");
run_list.push_back(rp);
}
}
@@ -921,7 +917,7 @@ void CLIENT_STATE::make_run_list(vector<RESULT*>& run_list) {
rp->already_selected = true;
atp = lookup_active_task_by_result(rp);
if (!proc_rsc.can_schedule(rp, atp)) continue;
- proc_rsc.schedule(rp, atp, "CPU job, EDF");
+ proc_rsc.schedule(rp, "CPU job, EDF");
rp->project->rsc_pwf[0].deadlines_missed_copy--;
rp->edf_scheduled = true;
run_list.push_back(rp);
@@ -938,7 +934,7 @@ void CLIENT_STATE::make_run_list(vector<RESULT*>& run_list) {
if (!rp) break;
atp = lookup_active_task_by_result(rp);
if (!proc_rsc.can_schedule(rp, atp)) continue;
- proc_rsc.schedule(rp, atp, "CPU job, priority order");
+ proc_rsc.schedule(rp, "CPU job, priority order");
run_list.push_back(rp);
}
diff --git a/client/cs_apps.cpp b/client/cs_apps.cpp
index 88f0d4a..0fb8b77 100644
--- a/client/cs_apps.cpp
+++ b/client/cs_apps.cpp
@@ -190,6 +190,7 @@ int CLIENT_STATE::app_finished(ACTIVE_TASK& at) {
default:
rp->set_state(RESULT_COMPUTE_ERROR, "CS::app_finished");
}
+ rp->project->njobs_error++;
} else {
#ifdef SIM
rp->set_state(RESULT_FILES_UPLOADED, "CS::app_finished");
@@ -200,6 +201,7 @@ int CLIENT_STATE::app_finished(ACTIVE_TASK& at) {
rp->append_log_record();
#endif
rp->project->update_duration_correction_factor(&at);
+ rp->project->njobs_success++;
}
double elapsed_time = now - rec_interval_start;
diff --git a/client/cs_notice.cpp b/client/cs_notice.cpp
index 4482b06..fd39a14 100644
--- a/client/cs_notice.cpp
+++ b/client/cs_notice.cpp
@@ -704,6 +704,14 @@ int RSS_FEED::parse_items(XML_PARSER& xp, int& nitems) {
return func_ret;
}
+void RSS_FEED::delete_files() {
+ char path[MAXPATHLEN];
+ feed_file_name(path);
+ boinc_delete_file(path);
+ archive_file_name(path);
+ boinc_delete_file(path);
+}
+
///////////// RSS_FEED_OP ////////////////
RSS_FEED_OP::RSS_FEED_OP() {
@@ -914,6 +922,7 @@ void RSS_FEEDS::update_feed_list() {
rf.url
);
}
+ rf.delete_files();
iter = feeds.erase(iter);
}
}
@@ -928,3 +937,9 @@ void RSS_FEEDS::write_feed_list() {
write_rss_feed_descs(fout, feeds);
fclose(f);
}
+
+void delete_project_notice_files(PROJECT* p) {
+ char path[MAXPATHLEN];
+ project_feed_list_file_name(p, path);
+ boinc_delete_file(path);
+}
diff --git a/client/cs_notice.h b/client/cs_notice.h
index 8e1d5b4..20e388d 100644
--- a/client/cs_notice.h
+++ b/client/cs_notice.h
@@ -111,6 +111,7 @@ struct RSS_FEED {
void feed_file_name(char*);
void archive_file_name(char*);
int read_archive_file();
+ void delete_files();
};
struct RSS_FEED_OP: public GUI_HTTP_OP {
@@ -144,4 +145,6 @@ int parse_rss_feed_descs(XML_PARSER&, std::vector<RSS_FEED>&);
void handle_sr_feeds(std::vector<RSS_FEED>&, struct PROJ_AM*);
// process the feeds in a scheduler reply
+void delete_project_notice_files(PROJECT*);
+
#endif
diff --git a/client/gpu_opencl.cpp b/client/gpu_opencl.cpp
index ba30333..3902b3a 100644
--- a/client/gpu_opencl.cpp
+++ b/client/gpu_opencl.cpp
@@ -824,7 +824,7 @@ void COPROC::merge_opencl(
opencls[j].is_used = COPROC_USED;
opencl_device_indexes[opencl_device_count] = opencls[j].opencl_device_index;
opencl_device_ids[opencl_device_count++] = opencls[j].device_id;
- have_opencls[i] = true;
+ instance_has_opencl[i] = true;
}
}
}
@@ -874,7 +874,7 @@ void COPROC::find_best_opencls(
continue;
}
if (use_all || !opencl_compare(opencls[i], opencl_prop, true)) {
- have_opencls[count] = true;
+ instance_has_opencl[count] = true;
device_nums[count++] = opencls[i].device_num;
opencl_device_indexes[opencl_device_count] = opencls[i].opencl_device_index;
opencl_device_ids[opencl_device_count++] = opencls[i].device_id;
diff --git a/client/log_flags.cpp b/client/log_flags.cpp
index 6df25fa..5d49170 100644
--- a/client/log_flags.cpp
+++ b/client/log_flags.cpp
@@ -300,7 +300,7 @@ int CC_CONFIG::parse_options_client(XML_PARSER& xp) {
"Can't parse <coproc> element in cc_config.xml"
);
}
- retval = coprocs.add(c);
+ retval = config_coprocs.add(c);
if (retval) {
msg_printf_notice(NULL, false, NULL,
"Duplicate <coproc> element in cc_config.xml"
diff --git a/client/makefile_sim b/client/makefile_sim
index b259144..f504d9d 100644
--- a/client/makefile_sim
+++ b/client/makefile_sim
@@ -13,6 +13,7 @@ OBJS = \
client_msgs.o \
client_state.o \
client_types.o \
+ coproc_sched.o \
cpu_sched.o \
cs_account.o \
cs_apps.o \
diff --git a/client/project.cpp b/client/project.cpp
index 3eca55c..906c05e 100644
--- a/client/project.cpp
+++ b/client/project.cpp
@@ -104,6 +104,8 @@ void PROJECT::init() {
possibly_backed_off = false;
nuploading_results = 0;
too_many_uploading_results = false;
+ njobs_success = 0;
+ njobs_error = 0;
#ifdef SIM
idle_time = 0;
@@ -310,6 +312,8 @@ int PROJECT::parse_state(XML_PARSER& xp) {
continue;
}
if (xp.parse_double("desired_disk_usage", desired_disk_usage)) continue;
+ if (xp.parse_int("njobs_success", njobs_success)) continue;
+ if (xp.parse_int("njobs_error", njobs_error)) continue;
#ifdef SIM
if (xp.match_tag("available")) {
available.parse(xp, "/available");
@@ -365,13 +369,14 @@ int PROJECT::write_state(MIOFILE& out, bool gui_rpc) {
" <next_rpc_time>%f</next_rpc_time>\n"
" <rec>%f</rec>\n"
" <rec_time>%f</rec_time>\n"
-
" <resource_share>%f</resource_share>\n"
" <desired_disk_usage>%f</desired_disk_usage>\n"
" <duration_correction_factor>%f</duration_correction_factor>\n"
" <sched_rpc_pending>%d</sched_rpc_pending>\n"
" <send_time_stats_log>%d</send_time_stats_log>\n"
" <send_job_log>%d</send_job_log>\n"
+ " <njobs_success>%d</njobs_success>\n"
+ " <njobs_error>%d</njobs_error>\n"
"%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
master_url,
project_name,
@@ -404,6 +409,8 @@ int PROJECT::write_state(MIOFILE& out, bool gui_rpc) {
sched_rpc_pending,
send_time_stats_log,
send_job_log,
+ njobs_success,
+ njobs_error,
anonymous_platform?" <anonymous_platform/>\n":"",
master_url_fetch_pending?" <master_url_fetch_pending/>\n":"",
trickle_up_pending?" <trickle_up_pending/>\n":"",
@@ -558,6 +565,8 @@ void PROJECT::copy_state_fields(PROJECT& p) {
}
desired_disk_usage = p.desired_disk_usage;
use_symlinks = p.use_symlinks;
+ njobs_success = p.njobs_success;
+ njobs_error = p.njobs_error;
}
// Write project statistic to GUI RPC reply
diff --git a/client/project.h b/client/project.h
index 43258f4..70cb63b 100644
--- a/client/project.h
+++ b/client/project.h
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
-// Copyright (C) 2012 University of California
+// Copyright (C) 2014 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -184,6 +184,7 @@ struct PROJECT : PROJ_AM {
std::vector<FILE_REF> user_files;
std::vector<FILE_REF> project_files;
// files not specific to apps or work - e.g. icons
+
int parse_preferences_for_user_files();
void write_project_files(MIOFILE&);
void link_project_files();
@@ -293,6 +294,11 @@ struct PROJECT : PROJ_AM {
//
APP_CONFIGS app_configs;
+ // job counting
+ //
+ int njobs_success;
+ int njobs_error;
+
PROJECT();
~PROJECT(){}
void init();
diff --git a/client/rr_sim.cpp b/client/rr_sim.cpp
index 484d284..9027669 100644
--- a/client/rr_sim.cpp
+++ b/client/rr_sim.cpp
@@ -23,8 +23,10 @@
// - deadline misses (per-project count, per-result flag)
// Deadline misses are not counted for tasks
// that are too large to run in RAM right now.
-// - resource shortfalls (per-project and total)
-// - counts of resources idle now
+// - for each resource type (in RSC_WORK_FETCH):
+// - shortfall
+// - nidle_now: # of idle instances
+// - sim_excluded_instances: bitmap of instances idle because of exclusions
//
// For coprocessors, we saturate the resource if possible;
// i.e. with 2 GPUs, we'd let a 1-GPU app and a 2-GPU app run together.
diff --git a/client/sim.cpp b/client/sim.cpp
index ae5361c..4bf0c3c 100644
--- a/client/sim.cpp
+++ b/client/sim.cpp
@@ -1049,7 +1049,7 @@ static void write_inputs() {
include_empty_projects?"yes":"no"
);
fprintf(f,
- "REC half-life: %f\n", config.rec_half_life
+ "REC half-life: %f\n", cc_config.rec_half_life
);
fprintf(f,
"Simulation duration: %f\nTime step: %f\n",
@@ -1087,7 +1087,7 @@ void simulate() {
gstate.global_prefs.cpu_scheduling_period(),
cpu_sched_rr_only?"yes":"no",
server_uses_workload?"yes":"no",
- config.rec_half_life
+ cc_config.rec_half_life
);
fprintf(summary_file, "Jobs\n");
for (int i=0; i<gstate.results.size(); i++) {
@@ -1391,7 +1391,7 @@ void do_client_simulation() {
FILE* f;
sprintf(buf, "%s%s", infile_prefix, CONFIG_FILE);
- config.defaults();
+ cc_config.defaults();
read_config_file(true, buf);
log_flags.init();
@@ -1429,7 +1429,7 @@ void do_client_simulation() {
}
}
- config.show();
+ cc_config.show();
log_flags.show();
sprintf(buf, "%s%s", infile_prefix, GLOBAL_PREFS_FILE_NAME);
@@ -1442,12 +1442,15 @@ void do_client_simulation() {
SUMMARY_FNAME, LOG_FNAME
);
- // fill in GPU device nums
+ // fill in GPU device nums and OpenCL flags
//
for (int i=0; i<coprocs.n_rsc; i++) {
COPROC& cp = coprocs.coprocs[i];
for (int j=0; j<cp.count; j++) {
cp.device_nums[j] = j;
+ if (cp.have_opencl) {
+ cp.instance_has_opencl[j] = true;
+ }
}
}
process_gpu_exclusions();
@@ -1544,7 +1547,7 @@ int main(int argc, char** argv) {
} else if (!strcmp(opt, "--include_empty_projects")) {
include_empty_projects = true;
} else if (!strcmp(opt, "--rec_half_life")) {
- config.rec_half_life = atof(argv[i++]);
+ cc_config.rec_half_life = atof(argv[i++]);
} else {
usage(argv[0]);
}
diff --git a/client/time_stats.cpp b/client/time_stats.cpp
index fa23d85..1bd2754 100644
--- a/client/time_stats.cpp
+++ b/client/time_stats.cpp
@@ -76,18 +76,31 @@ int get_connected_state() {
const float ALPHA = (SECONDS_PER_DAY*10);
//const float ALPHA = 60; // for testing
+// called before the client state file is parsed
+//
void CLIENT_TIME_STATS::init() {
- last_update = 0;
- first = true;
+ // members of TIME_STATS
+ now = 0;
on_frac = 1;
connected_frac = 1;
+ cpu_and_network_available_frac = 1;
active_frac = 1;
gpu_active_frac = 1;
- cpu_and_network_available_frac = 1;
client_start_time = gstate.now;
previous_uptime = 0;
+ session_active_duration = 0;
+ session_gpu_active_duration = 0;
+ total_start_time = 0;
+ total_duration = 0;
+ total_active_duration = 0;
+ total_gpu_active_duration = 0;
+
+ // members of CLIENT_TIME_STATS
+ first = true;
previous_connected_state = CONNECTED_STATE_UNINITIALIZED;
+ last_update = 0;
inactive_start = 0;
+
trim_stats_log();
time_stats_log = NULL;
}
@@ -157,6 +170,9 @@ void CLIENT_TIME_STATS::update(int suspend_reason, int _gpu_suspend_reason) {
bool is_active = (suspend_reason == 0) || (suspend_reason == SUSPEND_REASON_CPU_THROTTLE);
bool is_gpu_active = is_active && !_gpu_suspend_reason;
+ if (total_start_time == 0) {
+ total_start_time = gstate.now;
+ }
if (last_update == 0) {
// this is the first time this client has executed.
// Assume that everything is active
@@ -247,12 +263,15 @@ void CLIENT_TIME_STATS::update(int suspend_reason, int _gpu_suspend_reason) {
}
active_frac *= w2;
+ total_duration += dt;
if (is_active) {
active_frac += w1;
if (inactive_start) {
inactive_start = 0;
log_append("proc_start", gstate.now);
}
+ session_active_duration += dt;
+ total_active_duration += dt;
} else if (inactive_start == 0){
inactive_start = gstate.now;
log_append("proc_stop", gstate.now);
@@ -261,6 +280,8 @@ void CLIENT_TIME_STATS::update(int suspend_reason, int _gpu_suspend_reason) {
gpu_active_frac *= w2;
if (is_gpu_active) {
gpu_active_frac += w1;
+ session_gpu_active_duration += dt;
+ total_gpu_active_duration += dt;
}
//msg_printf(NULL, MSG_INFO, "is_active %d, active_frac %f", is_active, active_frac);
@@ -268,15 +289,20 @@ void CLIENT_TIME_STATS::update(int suspend_reason, int _gpu_suspend_reason) {
last_update = gstate.now;
if (log_flags.time_debug) {
msg_printf(0, MSG_INFO,
- "[time] dt %f w2 %f on %f; active %f; gpu_active %f; conn %f, cpu_and_net_avail %f",
- dt, w2, on_frac, active_frac, gpu_active_frac, connected_frac,
+ "[time] dt %f susp_reason %d gpu_susp_reason %d",
+ dt, suspend_reason, _gpu_suspend_reason
+ );
+ msg_printf(0, MSG_INFO,
+ "[time] w2 %f on %f; active %f; gpu_active %f; conn %f, cpu_and_net_avail %f",
+ w2, on_frac, active_frac, gpu_active_frac, connected_frac,
cpu_and_network_available_frac
);
}
}
}
-// Write XML based time statistics
+// Write time statistics
+// to_remote means GUI RPC reply; else writing client state file
//
int CLIENT_TIME_STATS::write(MIOFILE& out, bool to_remote) {
out.printf(
@@ -287,22 +313,37 @@ int CLIENT_TIME_STATS::write(MIOFILE& out, bool to_remote) {
" <active_frac>%f</active_frac>\n"
" <gpu_active_frac>%f</gpu_active_frac>\n"
" <client_start_time>%f</client_start_time>\n"
- " <previous_uptime>%f</previous_uptime>\n",
+ " <total_start_time>%f</total_start_time>\n"
+ " <total_duration>%f</total_duration>\n"
+ " <total_active_duration>%f</total_active_duration>\n"
+ " <total_gpu_active_duration>%f</total_gpu_active_duration>\n",
on_frac,
connected_frac,
cpu_and_network_available_frac,
active_frac,
gpu_active_frac,
client_start_time,
- gstate.now - client_start_time
+ total_start_time,
+ total_duration,
+ total_active_duration,
+ total_gpu_active_duration
);
if (to_remote) {
out.printf(
- " <now>%f</now>\n", gstate.now
+ " <now>%f</now>\n"
+ " <previous_uptime>%f</previous_uptime>\n"
+ " <session_active_duration>%f</session_active_duration>\n"
+ " <session_gpu_active_duration>%f</session_gpu_active_duration>\n",
+ gstate.now,
+ previous_uptime,
+ session_active_duration,
+ session_gpu_active_duration
);
} else {
out.printf(
+ " <previous_uptime>%f</previous_uptime>\n"
" <last_update>%f</last_update>\n",
+ gstate.now - client_start_time,
last_update
);
}
@@ -310,7 +351,7 @@ int CLIENT_TIME_STATS::write(MIOFILE& out, bool to_remote) {
return 0;
}
-// Parse XML based time statistics, usually from client_state.xml
+// Parse XML based time statistics from client_state.xml
//
int CLIENT_TIME_STATS::parse(XML_PARSER& xp) {
double x;
@@ -394,6 +435,10 @@ int CLIENT_TIME_STATS::parse(XML_PARSER& xp) {
}
if (xp.parse_double("client_start_time", x)) continue;
if (xp.parse_double("previous_uptime", previous_uptime)) continue;
+ if (xp.parse_double("total_start_time", total_start_time)) continue;
+ if (xp.parse_double("total_duration", total_duration)) continue;
+ if (xp.parse_double("total_active_duration", total_active_duration)) continue;
+ if (xp.parse_double("total_gpu_active_duration", total_gpu_active_duration)) continue;
if (log_flags.unparsed_xml) {
msg_printf(0, MSG_INFO,
"[unparsed_xml] TIME_STATS::parse(): unrecognized: %s\n",
diff --git a/client/work_fetch.cpp b/client/work_fetch.cpp
index 31ce772..ee7ed06 100644
--- a/client/work_fetch.cpp
+++ b/client/work_fetch.cpp
@@ -34,9 +34,9 @@
#include "work_fetch.h"
#if 0
-#define DEBUG(x) x
+#define WF_DEBUG(x) x
#else
-#define DEBUG(X)
+#define WF_DEBUG(X)
#endif
using std::vector;
@@ -254,7 +254,7 @@ void RSC_WORK_FETCH::set_request_excluded(PROJECT* p) {
n++;
}
}
- DEBUG(msg_printf(p, MSG_INFO, "set_request_excluded() %d %d %d", sim_excluded_instances, pwf.non_excluded_instances, n));
+ WF_DEBUG(msg_printf(p, MSG_INFO, "set_request_excluded() %d %d %d", sim_excluded_instances, pwf.non_excluded_instances, n));
req_instances = n;
if (p->resource_share == 0 || cc_config.fetch_minimal_work) {
req_secs = 1;
@@ -270,7 +270,9 @@ void RSC_WORK_FETCH::print_state(const char* name) {
shortfall, nidle_now, saturated_time,
busy_time_estimator.get_busy_time()
);
- //msg_printf(0, MSG_INFO, "[work_fetch] sim used inst %d sim excl inst %d", sim_used_instances, sim_excluded_instances);
+ msg_printf(0, MSG_INFO, "[work_fetch] sim used inst %d sim excl inst %d",
+ sim_used_instances, sim_excluded_instances
+ );
for (unsigned int i=0; i<gstate.projects.size(); i++) {
char buf[256];
PROJECT* p = gstate.projects[i];
@@ -467,7 +469,7 @@ bool WORK_FETCH::requested_work() {
// Decide if we should "piggyback" a work fetch request.
//
void WORK_FETCH::piggyback_work_request(PROJECT* p) {
- DEBUG(msg_printf(p, MSG_INFO, "piggyback_work_request()");)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "piggyback_work_request()");)
clear_request();
if (cc_config.fetch_minimal_work && gstate.had_or_requested_work) return;
if (p->non_cpu_intensive) {
@@ -502,7 +504,7 @@ void WORK_FETCH::piggyback_work_request(PROJECT* p) {
// If not, and the resource needs topping off, do so
//
for (int i=0; i<coprocs.n_rsc; i++) {
- DEBUG(msg_printf(p, MSG_INFO, "piggyback: resource %s", rsc_name_long(i));)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "piggyback: resource %s", rsc_name_long(i));)
RSC_WORK_FETCH& rwf = rsc_work_fetch[i];
if (i && !gpus_usable) {
rwf.dont_fetch_reason = DONT_FETCH_GPUS_NOT_USABLE;
@@ -510,7 +512,7 @@ void WORK_FETCH::piggyback_work_request(PROJECT* p) {
}
rwf.dont_fetch_reason = rwf.cant_fetch(p);
if (rwf.dont_fetch_reason) {
- DEBUG(msg_printf(p, MSG_INFO, "piggyback: can't fetch %s", rsc_name_long(i));)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "piggyback: can't fetch %s", rsc_name_long(i));)
continue;
}
bool buffer_low = (rwf.saturated_time < gstate.work_buf_total());
@@ -519,7 +521,7 @@ void WORK_FETCH::piggyback_work_request(PROJECT* p) {
need_work = true;
}
if (!need_work) {
- DEBUG(msg_printf(p, MSG_INFO, "piggyback: don't need %s", rsc_name_long(i));)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "piggyback: don't need %s", rsc_name_long(i));)
rwf.dont_fetch_reason = DONT_FETCH_BUFFER_FULL;
continue;
}
@@ -530,11 +532,11 @@ void WORK_FETCH::piggyback_work_request(PROJECT* p) {
if (p2 == p) break;
if (p2->sched_priority == p->sched_priority) continue;
if (p2->pwf.cant_fetch_work_reason) {
- DEBUG(msg_printf(p, MSG_INFO, "piggyback: %s can't fetch work", p2->project_name);)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "piggyback: %s can't fetch work", p2->project_name);)
continue;
}
if (!rwf.cant_fetch(p2) && !rwf.backed_off(p2)) {
- DEBUG(msg_printf(p, MSG_INFO, "piggyback: better proj %s", p2->project_name);)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "piggyback: better proj %s", p2->project_name);)
break;
}
}
@@ -543,7 +545,7 @@ void WORK_FETCH::piggyback_work_request(PROJECT* p) {
continue;
}
}
- DEBUG(msg_printf(p, MSG_INFO, "piggyback: requesting %s", rsc_name_long(i));)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "piggyback: requesting %s", rsc_name_long(i));)
if (buffer_low) {
rwf.set_request(p);
} else {
@@ -579,7 +581,7 @@ static bool higher_priority(PROJECT *p1, PROJECT *p2) {
//
bool RSC_WORK_FETCH::backed_off(PROJECT* p) {
if (project_state(p).backoff_time > gstate.now) {
- DEBUG(msg_printf(p, MSG_INFO, "skip: backoff");)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "skip: backoff");)
return true;
}
return false;
@@ -594,7 +596,7 @@ int RSC_WORK_FETCH::cant_fetch(PROJECT *p) {
//
int reason = dont_fetch(p, rsc_type);
if (reason) {
- DEBUG(msg_printf(p, MSG_INFO, "skip: dont_fetch");)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "skip: dont_fetch");)
return reason;
}
@@ -603,8 +605,8 @@ int RSC_WORK_FETCH::cant_fetch(PROJECT *p) {
// if project has zero resource share,
// only fetch work if a device is idle
//
- if (p->resource_share == 0 && nidle_now == 0) {
- DEBUG(msg_printf(p, MSG_INFO, "skip: zero share");)
+ if (p->resource_share == 0 && nidle_now == 0 && sim_excluded_instances==0) {
+ WF_DEBUG(msg_printf(p, MSG_INFO, "skip: zero share");)
return DONT_FETCH_ZERO_SHARE;
}
@@ -628,13 +630,13 @@ int RSC_WORK_FETCH::cant_fetch(PROJECT *p) {
if (rpwf.n_runnable_jobs >= n_not_excluded
&& rpwf.queue_est > (gstate.work_buf_min() * n_not_excluded)/ninstances
) {
- DEBUG(msg_printf(p, MSG_INFO, "skip: too much work");)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "skip: too much work");)
return DONT_FETCH_BUFFER_FULL;
}
}
if (rpwf.anon_skip) {
- DEBUG(msg_printf(p, MSG_INFO, "skip: anon");)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "skip: anon");)
return DONT_FETCH_NO_APPS;
}
return 0;
@@ -647,7 +649,7 @@ bool RSC_WORK_FETCH::uses_starved_excluded_instances(PROJECT* p) {
RSC_PROJECT_WORK_FETCH& rpwf = project_state(p);
if (!sim_excluded_instances) return false;
if ((sim_excluded_instances & rpwf.non_excluded_instances) == 0) {
- DEBUG(msg_printf(p, MSG_INFO, "skip: excl");)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "skip: excl");)
return false;
}
return true;
@@ -722,9 +724,9 @@ PROJECT* WORK_FETCH::choose_project() {
bool found = false;
for (unsigned int j=0; j<gstate.projects.size(); j++) {
p = gstate.projects[j];
- DEBUG(msg_printf(p, MSG_INFO, "scanning");)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "scanning");)
if (p->pwf.cant_fetch_work_reason) {
- DEBUG(msg_printf(p, MSG_INFO, "skip: cfwr %d", p->pwf.cant_fetch_work_reason);)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "skip: cfwr %d", p->pwf.cant_fetch_work_reason);)
continue;
}
@@ -743,18 +745,18 @@ PROJECT* WORK_FETCH::choose_project() {
if (!rwf.found_project) {
rwf.found_project = p;
}
- DEBUG(msg_printf(p, MSG_INFO, "can fetch %s", rsc_name_long(i));)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "can fetch %s", rsc_name_long(i));)
} else {
- DEBUG(msg_printf(p, MSG_INFO, "can't fetch %s", rsc_name_long(i));)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "can't fetch %s", rsc_name_long(i));)
continue;
}
if (rwf.saturated_time < gstate.work_buf_min()) {
- DEBUG(msg_printf(p, MSG_INFO, "%s needs work - buffer low", rsc_name_long(i));)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "%s needs work - buffer low", rsc_name_long(i));)
rsc_index = i;
break;
}
if (rwf.has_exclusions && rwf.uses_starved_excluded_instances(p)) {
- DEBUG(msg_printf(p, MSG_INFO, "%s needs work - excluded instance starved", rsc_name_long(i));)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "%s needs work - excluded instance starved", rsc_name_long(i));)
rsc_index = i;
break;
}
@@ -773,12 +775,12 @@ PROJECT* WORK_FETCH::choose_project() {
if (i && !gpus_usable) continue;
RSC_WORK_FETCH& rwf = rsc_work_fetch[i];
bool buffer_low;
- DEBUG(msg_printf(p, MSG_INFO, "checking %s", rsc_name_long(i));)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "checking %s", rsc_name_long(i));)
if (i == rsc_index) {
buffer_low = (rwf.saturated_time < gstate.work_buf_min());
} else {
if (rwf.found_project && rwf.found_project != p) {
- DEBUG(msg_printf(p, MSG_INFO, "%s not high prio proj", rsc_name_long(i));)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "%s not high prio proj", rsc_name_long(i));)
continue;
}
buffer_low = (rwf.saturated_time < gstate.work_buf_total());
@@ -787,21 +789,21 @@ PROJECT* WORK_FETCH::choose_project() {
need_work = true;
}
if (!need_work) {
- DEBUG(msg_printf(p, MSG_INFO, "%s don't need", rsc_name_long(i));)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "%s don't need", rsc_name_long(i));)
continue;
}
int reason = rwf.cant_fetch(p);
if (reason) {
- DEBUG(msg_printf(p, MSG_INFO, "%s can't fetch", rsc_name_long(i));)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "%s can't fetch", rsc_name_long(i));)
continue;
}
}
if (buffer_low) {
rwf.set_request(p);
- DEBUG(msg_printf(p, MSG_INFO, "%s set_request: %f", rsc_name_long(i), rwf.req_secs);)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "%s set_request: %f", rsc_name_long(i), rwf.req_secs);)
} else {
rwf.set_request_excluded(p);
- DEBUG(msg_printf(p, MSG_INFO, "%s set_request_excluded: %f", rsc_name_long(i), rwf.req_secs);)
+ WF_DEBUG(msg_printf(p, MSG_INFO, "%s set_request_excluded: %f", rsc_name_long(i), rwf.req_secs);)
}
if (rwf.req_secs > 0) {
any_request = true;
diff --git a/clientgui/BOINCBaseView.cpp b/clientgui/BOINCBaseView.cpp
index 2a0316b..287577a 100644
--- a/clientgui/BOINCBaseView.cpp
+++ b/clientgui/BOINCBaseView.cpp
@@ -34,7 +34,6 @@
IMPLEMENT_DYNAMIC_CLASS(CBOINCBaseView, wxPanel)
-
CBOINCBaseView::CBOINCBaseView() {}
CBOINCBaseView::CBOINCBaseView(wxNotebook* pNotebook) :
@@ -47,6 +46,9 @@ CBOINCBaseView::CBOINCBaseView(wxNotebook* pNotebook) :
m_bIgnoreUIEvents = false;
m_bNeedSort = false;
+ m_iPreviousSelectionCount = 0;
+ m_lPreviousFirstSelection = -1;
+
//
// Setup View
//
@@ -70,6 +72,9 @@ CBOINCBaseView::CBOINCBaseView(wxNotebook* pNotebook, wxWindowID iTaskWindowID,
m_bForceUpdateSelection = true;
m_bIgnoreUIEvents = false;
+ m_iPreviousSelectionCount = 0;
+ m_lPreviousFirstSelection = -1;
+
//
// Setup View
//
@@ -331,6 +336,9 @@ bool CBOINCBaseView::OnRestoreState(wxConfigBase* pConfig) {
}
+// We don't use this because multiple selection virtual
+// wxListCtrl does not generate selection events for
+// shift-click; see OnCheckSelectionChanged() below.
void CBOINCBaseView::OnListSelected(wxListEvent& event) {
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseView::OnListSelected - Function Begin"));
@@ -344,6 +352,9 @@ void CBOINCBaseView::OnListSelected(wxListEvent& event) {
}
+// We don't use this because multiple selection virtual
+// wxListCtrl does generates deselection events only for
+// control-click; see OnCheckSelectionChanged() below.
void CBOINCBaseView::OnListDeselected(wxListEvent& event) {
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseView::OnListDeselected - Function Begin"));
@@ -357,19 +368,39 @@ void CBOINCBaseView::OnListDeselected(wxListEvent& event) {
}
-// Work around a bug (feature?) in virtual list control
-// which does not send deselection events
-void CBOINCBaseView::OnCacheHint(wxListEvent& event) {
- static int oldSelectionCount = 0;
- int newSelectionCount = m_pListPane->GetSelectedItemCount();
+void CBOINCBaseView::OnCheckSelectionChanged(CCheckSelectionChangedEvent& ) {
+ CheckSelectionChanged();
+}
+
+
+void CBOINCBaseView::OnCacheHint(wxListEvent& ) {
+ CheckSelectionChanged();
+}
- if (newSelectionCount < oldSelectionCount) {
- wxListEvent leDeselectedEvent(wxEVT_COMMAND_LIST_ITEM_DESELECTED, m_windowId);
- leDeselectedEvent.SetEventObject(this);
- OnListDeselected(leDeselectedEvent);
+
+// Work around features in multiple selection virtual wxListCtrl:
+// * It does not send deselection events (except ctrl-click).
+// * It does not send selection events if you add to selection
+// using Shift_Click.
+//
+// We currently handle all selections and deselections here.
+// On the Mac, this is called due to an event posted by CBOINCListCtrl::OnMouseDown().
+// On Windows, it is called due to a EVT_LIST_CACHE_HINT from wxListCtrl.
+void CBOINCBaseView::CheckSelectionChanged() {
+ int newSelectionCount = m_pListPane->GetSelectedItemCount();
+ long currentSelection = m_pListPane->GetFirstSelected();
+
+ if ((newSelectionCount != m_iPreviousSelectionCount) ||
+ (currentSelection != m_lPreviousFirstSelection)
+ ) {
+ if (!m_bIgnoreUIEvents) {
+ m_bForceUpdateSelection = true;
+ UpdateSelection();
+ }
}
- oldSelectionCount = newSelectionCount;
- event.Skip();
+
+ m_iPreviousSelectionCount = newSelectionCount;
+ m_lPreviousFirstSelection = currentSelection;
}
@@ -471,6 +502,8 @@ void CBOINCBaseView::OnColClick(wxListEvent& event) {
wxArrayInt selections;
int i, j, m;
+ if (newSortColumn < 0) return; // Clicked past last column
+
item.SetMask(wxLIST_MASK_IMAGE);
if (newSortColumn == m_iSortColumn) {
m_bReverseSort = !m_bReverseSort;
diff --git a/clientgui/BOINCBaseView.h b/clientgui/BOINCBaseView.h
index 5ac6f52..0b40d84 100644
--- a/clientgui/BOINCBaseView.h
+++ b/clientgui/BOINCBaseView.h
@@ -22,11 +22,13 @@
#pragma interface "BOINCBaseView.cpp"
#endif
+
#define DEFAULT_TASK_FLAGS wxTAB_TRAVERSAL | wxADJUST_MINSIZE | wxFULL_REPAINT_ON_RESIZE
#define DEFAULT_LIST_FLAGS wxLC_REPORT | wxLC_VIRTUAL | wxLC_HRULES
class CBOINCTaskCtrl;
class CBOINCListCtrl;
+class CCheckSelectionChangedEvent;
struct PROJECT;
@@ -147,6 +149,8 @@ protected:
virtual void OnListSelected( wxListEvent& event );
virtual void OnListDeselected( wxListEvent& event );
virtual void OnCacheHint(wxListEvent& event);
+ virtual void OnCheckSelectionChanged(CCheckSelectionChangedEvent& event);
+ virtual void CheckSelectionChanged();
virtual wxString OnListGetItemText( long item, long column ) const;
virtual int OnListGetItemImage( long item ) const;
@@ -190,6 +194,8 @@ protected:
bool m_bIgnoreUIEvents;
bool m_bNeedSort;
+ int m_iPreviousSelectionCount;
+ long m_lPreviousFirstSelection;
int m_iProgressColumn;
wxImageList * m_SortArrows;
@@ -200,6 +206,5 @@ protected:
CBOINCListCtrl* m_pListPane;
};
-
#endif
diff --git a/clientgui/BOINCGUIApp.cpp b/clientgui/BOINCGUIApp.cpp
index 2cbc4da..e2d578f 100644
--- a/clientgui/BOINCGUIApp.cpp
+++ b/clientgui/BOINCGUIApp.cpp
@@ -98,23 +98,6 @@ OSErr QuitAppleEventHandler( const AppleEvent *appleEvt, AppleEvent* reply, UInt
#endif
-void BOINCAssertHandler(const wxString &file, int line, const wxString &func, const wxString &cond, const wxString &msg) {
- wxLogTrace(
- wxT("Assert"),
- wxT("ASSERT: %s:%d - %s - %s - %s"),
- file.IsEmpty() ? wxT("<NULL>") : file.c_str(),
- line,
- func.IsEmpty() ? wxT("<NULL>") : func.c_str(),
- cond.IsEmpty() ? wxT("<NULL>") : cond.c_str(),
- msg.IsEmpty() ? wxT("<NULL>") : msg.c_str()
- );
-
- if (wxIsDebuggerRunning()) {
- wxTrap();
- }
-}
-
-
DEFINE_EVENT_TYPE(wxEVT_RPC_FINISHED)
IMPLEMENT_APP(CBOINCGUIApp)
@@ -277,15 +260,10 @@ bool CBOINCGUIApp::OnInit() {
diagnostics_init(dwDiagnosticsFlags, "stdoutgui", "stderrgui");
-#ifdef _NDEBUG
- wxSetAssertHandler(BOINCAssertHandler);
-#endif
-
// Enable Logging and Trace Masks
m_pLog = new wxLogBOINC();
wxLog::SetActiveTarget(m_pLog);
- m_pLog->AddTraceMask(wxT("Assert"));
m_pLog->AddTraceMask(wxT("Function Start/End"));
m_pLog->AddTraceMask(wxT("Function Status"));
diff --git a/clientgui/BOINCGUIApp.h b/clientgui/BOINCGUIApp.h
index 79e6370..6a0c46f 100644
--- a/clientgui/BOINCGUIApp.h
+++ b/clientgui/BOINCGUIApp.h
@@ -207,7 +207,7 @@ public:
//
void HideThisApp(void);
-#if !wxCHECK_VERSION(3,0,0)
+#if !wxCHECK_VERSION(3,0,1)
// This should be fixed after wxCocoa 3.0.0:
// http://trac.wxwidgets.org/ticket/16156
diff --git a/clientgui/BOINCListCtrl.cpp b/clientgui/BOINCListCtrl.cpp
index 85369d9..0b1dd14 100644
--- a/clientgui/BOINCListCtrl.cpp
+++ b/clientgui/BOINCListCtrl.cpp
@@ -25,21 +25,27 @@
#include "Events.h"
-#if USE_NATIVE_LISTCONTROL
+DEFINE_EVENT_TYPE(wxEVT_CHECK_SELECTION_CHANGED)
+#if USE_NATIVE_LISTCONTROL
DEFINE_EVENT_TYPE(wxEVT_DRAW_PROGRESSBAR)
+#endif
BEGIN_EVENT_TABLE(CBOINCListCtrl, LISTCTRL_BASE)
- EVT_DRAW_PROGRESSBAR(CBOINCListCtrl::OnDrawProgressBar)
-END_EVENT_TABLE()
+#if USE_NATIVE_LISTCONTROL
+ EVT_DRAW_PROGRESSBAR(CBOINCListCtrl::OnDrawProgressBar)
#else
+#ifdef __WXMAC__
+ EVT_SIZE(CBOINCListCtrl::OnSize) // In MacAccessibility.mm
+#endif
+#endif
-BEGIN_EVENT_TABLE(CBOINCListCtrl, LISTCTRL_BASE)
- EVT_SIZE(CBOINCListCtrl::OnSize)
+#if ! USE_LIST_CACHE_HINT
+ EVT_LEFT_DOWN(CBOINCListCtrl::OnMouseDown)
+#endif
END_EVENT_TABLE()
-#endif
BEGIN_EVENT_TABLE(MyEvtHandler, wxEvtHandler)
EVT_PAINT(MyEvtHandler::OnPaint)
@@ -236,7 +242,7 @@ void CBOINCListCtrl::DrawProgressBars()
wxClientDC dc(this);
m_bProgressBarEventPending = false;
#else
- wxClientDC dc(GetMainWin()); // Available only in wxGenericListCtrl
+ wxWindowDC dc(GetMainWin()); // Available only in wxGenericListCtrl
#endif
if (progressColumn < 0) {
@@ -383,6 +389,28 @@ void MyEvtHandler::OnPaint(wxPaintEvent & event)
#endif
+#if ! USE_LIST_CACHE_HINT
+
+// Work around features in multiple selection virtual wxListCtrl:
+// * It does not send deselection events (except ctrl-click).
+// * It does not send selection events if you add to selection
+// using Shift_Click.
+//
+// Post a special event. This will allow this mouse event to
+// propogate through the chain to complete any selection or
+// deselection operatiion, then the special event will trigger
+// CBOINCBaseView::OnCheckSelectionChanged() to respond to the
+// selection change, if any.
+//
+void CBOINCListCtrl::OnMouseDown(wxMouseEvent& event) {
+ CCheckSelectionChangedEvent newEvent(wxEVT_CHECK_SELECTION_CHANGED, this);
+ m_pParentView->GetEventHandler()->AddPendingEvent(newEvent);
+ event.Skip();
+}
+
+#endif
+
+
// To reduce flicker, refresh only changed columns (except
// on Mac, which is double-buffered to eliminate flicker.)
void CBOINCListCtrl::RefreshCell(int row, int col) {
diff --git a/clientgui/BOINCListCtrl.h b/clientgui/BOINCListCtrl.h
index f0cc34e..00296cd 100644
--- a/clientgui/BOINCListCtrl.h
+++ b/clientgui/BOINCListCtrl.h
@@ -22,12 +22,26 @@
#pragma interface "BOINCListCtrl.cpp"
#endif
-#if defined(__WXMSW__) || defined(__WXGTK__)
+#ifdef __WXMSW__
#define USE_NATIVE_LISTCONTROL 1
#else
#define USE_NATIVE_LISTCONTROL 0
#endif
+
+// Virtual wxListCtrl does not reliably generate selection and
+// deselection events, so we must check for these differently.
+// We get more events than we need using EVT_LIST_CACHE_HINT,
+// so testing on mouse events is more efficient, but it doesn't
+// work on Windows.
+#ifdef __WXMSW__
+// On Windows, check for selection / deselection on EVT_LIST_CACHE_HINT.
+#define USE_LIST_CACHE_HINT 1
+#else
+// On Mac & Linux, check for selection / deselection on EVT_LEFT_DOWN.
+#define USE_LIST_CACHE_HINT 0
+#endif
+
#if USE_NATIVE_LISTCONTROL
#define LISTCTRL_BASE wxListCtrl
#include "wx/listctrl.h"
@@ -77,6 +91,10 @@ private:
CBOINCBaseView* m_pParentView;
wxArrayInt m_iRowsNeedingProgressBars;
+#if ! USE_LIST_CACHE_HINT
+ void OnMouseDown(wxMouseEvent& event);
+#endif
+
#if USE_NATIVE_LISTCONTROL
public:
void PostDrawProgressBarEvent();
@@ -117,12 +135,28 @@ public:
virtual wxEvent * Clone() const { return new CDrawProgressBarEvent(*this); }
};
+class CCheckSelectionChangedEvent : public wxEvent
+{
+public:
+ CCheckSelectionChangedEvent(wxEventType evtType, CBOINCListCtrl* myCtrl)
+ : wxEvent(-1, evtType)
+ {
+ SetEventObject(myCtrl);
+ }
+
+ virtual wxEvent * Clone() const { return new CCheckSelectionChangedEvent(*this); }
+};
+
+
BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE( wxEVT_DRAW_PROGRESSBAR, 12000 )
+DECLARE_EVENT_TYPE( wxEVT_CHECK_SELECTION_CHANGED, 12002 )
END_DECLARE_EVENT_TYPES()
#define EVT_DRAW_PROGRESSBAR(fn) DECLARE_EVENT_TABLE_ENTRY(wxEVT_DRAW_PROGRESSBAR, -1, -1, (wxObjectEventFunction) (wxEventFunction) &fn, NULL),
+#define EVT_CHECK_SELECTION_CHANGED(fn) DECLARE_EVENT_TABLE_ENTRY(wxEVT_CHECK_SELECTION_CHANGED, -1, -1, (wxObjectEventFunction) (wxEventFunction) &fn, NULL),
+
// Define a custom event handler
class MyEvtHandler : public wxEvtHandler
diff --git a/clientgui/DlgDiagnosticLogFlags.cpp b/clientgui/DlgDiagnosticLogFlags.cpp
index 50f6538..67ca0bc 100755
--- a/clientgui/DlgDiagnosticLogFlags.cpp
+++ b/clientgui/DlgDiagnosticLogFlags.cpp
@@ -258,11 +258,6 @@ void CDlgDiagnosticLogFlags::OnOK(wxCommandEvent& event) {
void CDlgDiagnosticLogFlags::OnSetDefaults(wxCommandEvent& ) {
- CMainDocument* pDoc = wxGetApp().GetDocument();
-
- wxASSERT(pDoc);
- wxASSERT(wxDynamicCast(pDoc, CMainDocument));
-
log_flags.init();
m_checkboxSizer->Clear(true);
diff --git a/clientgui/DlgItemProperties.cpp b/clientgui/DlgItemProperties.cpp
index 77fba6a..791922e 100644
--- a/clientgui/DlgItemProperties.cpp
+++ b/clientgui/DlgItemProperties.cpp
@@ -256,6 +256,9 @@ void CDlgItemProperties::renderInfos(PROJECT* project_in) {
if (project->ended) {
addProperty(_("Ended"), _("yes"));
}
+ addProperty(_("Tasks completed"), wxString::Format(wxT("%d"), project->njobs_success));
+ addProperty(_("Tasks failed"), wxString::Format(wxT("%d"), project->njobs_error));
+
addSection(_("Credit"));
addProperty(_("User"),
wxString::Format(
diff --git a/clientgui/LogBOINC.cpp b/clientgui/LogBOINC.cpp
index 4192b20..fe4f52a 100644
--- a/clientgui/LogBOINC.cpp
+++ b/clientgui/LogBOINC.cpp
@@ -25,8 +25,8 @@
#include "LogBOINC.h"
-wxLogBOINC::wxLogBOINC() {
- m_fp = stdout;
+wxLogBOINC::wxLogBOINC() : wxLogStderr(stdout)
+{
}
void wxLogBOINC::DoLogText(const wxString& msg) {
@@ -36,6 +36,8 @@ void wxLogBOINC::DoLogText(const wxString& msg) {
strDebug += wxT("\r\n");
diagnostics_trace_to_debugger(strDebug.mb_str());
#endif
+#ifdef __WXDEBUG__
wxLogStderr::DoLogText(msg);
+#endif
}
diff --git a/clientgui/ViewProjects.cpp b/clientgui/ViewProjects.cpp
index f39d178..48fd599 100644
--- a/clientgui/ViewProjects.cpp
+++ b/clientgui/ViewProjects.cpp
@@ -82,10 +82,16 @@ BEGIN_EVENT_TABLE (CViewProjects, CBOINCBaseView)
EVT_BUTTON(ID_TASK_PROJECT_DETACH, CViewProjects::OnProjectDetach)
EVT_BUTTON(ID_TASK_PROJECT_SHOW_PROPERTIES, CViewProjects::OnShowItemProperties)
EVT_CUSTOM_RANGE(wxEVT_COMMAND_BUTTON_CLICKED, ID_TASK_PROJECT_WEB_PROJDEF_MIN, ID_TASK_PROJECT_WEB_PROJDEF_MAX, CViewProjects::OnProjectWebsiteClicked)
- EVT_LIST_ITEM_SELECTED(ID_LIST_PROJECTSVIEW, CViewProjects::OnListSelected)
- EVT_LIST_ITEM_DESELECTED(ID_LIST_PROJECTSVIEW, CViewProjects::OnListDeselected)
- EVT_LIST_COL_CLICK(ID_LIST_PROJECTSVIEW, CViewProjects::OnColClick)
+// We currently handle EVT_LIST_CACHE_HINT on Windows or
+// EVT_CHECK_SELECTION_CHANGED on Mac instead of EVT_LIST_ITEM_SELECTED
+// or EVT_LIST_ITEM_DESELECTED. See CBOINCBaseView::OnCacheHint() for info.
+#if USE_LIST_CACHE_HINT
EVT_LIST_CACHE_HINT(ID_LIST_PROJECTSVIEW, CViewProjects::OnCacheHint)
+#else
+ EVT_CHECK_SELECTION_CHANGED(CViewProjects::OnCheckSelectionChanged)
+#endif
+ EVT_CHECK_SELECTION_CHANGED(CViewProjects::OnCheckSelectionChanged)
+ EVT_LIST_COL_CLICK(ID_LIST_PROJECTSVIEW, CViewProjects::OnColClick)
EVT_LIST_COL_END_DRAG(ID_LIST_PROJECTSVIEW, CViewProjects::OnColResize)
END_EVENT_TABLE ()
diff --git a/clientgui/ViewStatistics.cpp b/clientgui/ViewStatistics.cpp
index 5990771..04b2aa2 100644
--- a/clientgui/ViewStatistics.cpp
+++ b/clientgui/ViewStatistics.cpp
@@ -500,7 +500,7 @@ void CPaintStatistics::DrawLegend(wxDC &dc, PROJECTS* proj, CMainDocument* pDoc,
const wxCoord buffer_y1 = 3;
const wxCoord buffer_x1 = 3;
int count = -1;
- int project_count = -1;
+// int project_count = -1;
wxCoord w_temp = 0, h_temp = 0, des_temp = 0, lead_temp = 0;
wxCoord x0 = 0;
wxCoord y0 = 0;
@@ -599,7 +599,7 @@ void CPaintStatistics::DrawLegend(wxDC &dc, PROJECTS* proj, CMainDocument* pDoc,
m_scrollBar->SetThumbPosition(m_Legend_Shift);
// Legend Shift (end)
//---------------
- project_count = count;
+// project_count = count;
count = -1;
m_WorkSpace_X_end -= double(project_name_max_width) + m_Space_for_scrollbar;
@@ -1940,8 +1940,6 @@ BEGIN_EVENT_TABLE (CViewStatistics, CBOINCBaseView)
EVT_BUTTON(ID_TASK_STATISTICS_NEXTPROJECT, CViewStatistics::OnStatisticsNextProject)
EVT_BUTTON(ID_TASK_STATISTICS_PREVPROJECT, CViewStatistics::OnStatisticsPrevProject)
EVT_BUTTON(ID_TASK_STATISTICS_HIDEPROJLIST, CViewStatistics::OnShowHideProjectList)
- EVT_LIST_ITEM_SELECTED(ID_LIST_STATISTICSVIEW, CViewStatistics::OnListSelected)
- EVT_LIST_ITEM_DESELECTED(ID_LIST_STATISTICSVIEW, CViewStatistics::OnListDeselected)
END_EVENT_TABLE ()
CViewStatistics::CViewStatistics()
diff --git a/clientgui/ViewTransfers.cpp b/clientgui/ViewTransfers.cpp
index 16c4ac2..dad3dab 100644
--- a/clientgui/ViewTransfers.cpp
+++ b/clientgui/ViewTransfers.cpp
@@ -68,10 +68,12 @@ IMPLEMENT_DYNAMIC_CLASS(CViewTransfers, CBOINCBaseView)
BEGIN_EVENT_TABLE (CViewTransfers, CBOINCBaseView)
EVT_BUTTON(ID_TASK_TRANSFERS_RETRYNOW, CViewTransfers::OnTransfersRetryNow)
EVT_BUTTON(ID_TASK_TRANSFERS_ABORT, CViewTransfers::OnTransfersAbort)
- EVT_LIST_ITEM_SELECTED(ID_LIST_TRANSFERSVIEW, CViewTransfers::OnListSelected)
- EVT_LIST_ITEM_DESELECTED(ID_LIST_TRANSFERSVIEW, CViewTransfers::OnListDeselected)
- EVT_LIST_COL_CLICK(ID_LIST_TRANSFERSVIEW, CViewTransfers::OnColClick)
+#if USE_LIST_CACHE_HINT
EVT_LIST_CACHE_HINT(ID_LIST_TRANSFERSVIEW, CViewTransfers::OnCacheHint)
+#else
+ EVT_CHECK_SELECTION_CHANGED(CViewTransfers::OnCheckSelectionChanged)
+#endif
+ EVT_LIST_COL_CLICK(ID_LIST_TRANSFERSVIEW, CViewTransfers::OnColClick)
EVT_LIST_COL_END_DRAG(ID_LIST_TRANSFERSVIEW, CViewTransfers::OnColResize)
END_EVENT_TABLE ()
diff --git a/clientgui/ViewWork.cpp b/clientgui/ViewWork.cpp
index 0bd32c4..84f763a 100644
--- a/clientgui/ViewWork.cpp
+++ b/clientgui/ViewWork.cpp
@@ -89,10 +89,15 @@ BEGIN_EVENT_TABLE (CViewWork, CBOINCBaseView)
EVT_BUTTON(ID_TASK_SHOW_PROPERTIES, CViewWork::OnShowItemProperties)
EVT_BUTTON(ID_TASK_ACTIVE_ONLY, CViewWork::OnActiveTasksOnly)
EVT_CUSTOM_RANGE(wxEVT_COMMAND_BUTTON_CLICKED, ID_TASK_PROJECT_WEB_PROJDEF_MIN, ID_TASK_PROJECT_WEB_PROJDEF_MAX, CViewWork::OnProjectWebsiteClicked)
- EVT_LIST_ITEM_SELECTED(ID_LIST_WORKVIEW, CViewWork::OnListSelected)
- EVT_LIST_ITEM_DESELECTED(ID_LIST_WORKVIEW, CViewWork::OnListDeselected)
- EVT_LIST_COL_CLICK(ID_LIST_WORKVIEW, CViewWork::OnColClick)
+// We currently handle EVT_LIST_CACHE_HINT on Windows or
+// EVT_CHECK_SELECTION_CHANGED on Mac instead of EVT_LIST_ITEM_SELECTED
+// or EVT_LIST_ITEM_DESELECTED. See CBOINCBaseView::OnCacheHint() for info.
+#if USE_LIST_CACHE_HINT
EVT_LIST_CACHE_HINT(ID_LIST_WORKVIEW, CViewWork::OnCacheHint)
+#else
+ EVT_CHECK_SELECTION_CHANGED(CViewWork::OnCheckSelectionChanged)
+#endif
+ EVT_LIST_COL_CLICK(ID_LIST_WORKVIEW, CViewWork::OnColClick)
EVT_LIST_COL_END_DRAG(ID_LIST_WORKVIEW, CViewWork::OnColResize)
END_EVENT_TABLE ()
diff --git a/clientgui/common/wxPieCtrl.cpp b/clientgui/common/wxPieCtrl.cpp
index 077b2fa..7915142 100644
--- a/clientgui/common/wxPieCtrl.cpp
+++ b/clientgui/common/wxPieCtrl.cpp
@@ -40,6 +40,7 @@ BEGIN_EVENT_TABLE(wxPieCtrl, wxWindow)
EVT_ERASE_BACKGROUND(wxPieCtrl::OnEraseBackground)
EVT_SIZE(wxPieCtrl::OnSize)
EVT_MOTION(wxPieCtrl::OnMouseMove)
+ EVT_SCROLL(wxPieCtrl::OnLegendScroll)
END_EVENT_TABLE()
/* constructor */
@@ -62,6 +63,13 @@ wxPieCtrl::wxPieCtrl(wxWindow * parent, wxWindowID id, wxPoint pos,
m_LegendVerBorder = 10;
m_szTitle = _("Pie Ctrl");
+ m_firstlabelToDraw = 0;
+ m_scrollBar = new wxScrollBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL);
+ int h;
+ m_scrollBar->GetSize(&m_Scrollbar_width, &h);
+ m_scrollBar->SetScrollbar(0, 1, 1, 1);
+ m_scrollBar->Hide();
+
SetSizer(NULL);
SetSize(sz);
m_CanvasBitmap.Create(1,1);
@@ -73,6 +81,9 @@ wxPieCtrl::wxPieCtrl(wxWindow * parent, wxWindowID id, wxPoint pos,
}
wxPieCtrl::~wxPieCtrl() {
+ if (m_scrollBar) {
+ delete m_scrollBar;
+ }
#ifdef __WXMAC__
m_fauxResourcesView = NULL;
RemoveMacAccessibilitySupport();
@@ -153,6 +164,11 @@ void wxPieCtrl::OnPaint(wxPaintEvent & /*event*/)
Draw(pdc);
}
+void wxPieCtrl::OnLegendScroll(wxScrollEvent& event) {
+ Refresh();
+ event.Skip();
+}
+
/* internal methods */
/* gets the pie part on coords using pixel colour */
int wxPieCtrl::GetCoveredPiePart(int x,int y) {
@@ -217,19 +233,11 @@ void wxPieCtrl::DrawParts(wxRect& pieRect)
if(m_Series.Count() == 1)
{
m_CanvasDC.SetBrush(wxBrush(m_Series[0].GetColour()));
-#if (defined(__WXMAC__) && wxCHECK_VERSION(2,8,2))
- DrawEllipticArc(pieRect.GetLeft(),
- pieRect.GetTop(),
- pieRect.GetRight()-pieRect.GetLeft(),
- pieRect.GetBottom()-pieRect.GetTop(),
- 0,360);
-#else
m_CanvasDC.DrawEllipticArc(pieRect.GetLeft(),
pieRect.GetTop(),
pieRect.GetRight()-pieRect.GetLeft(),
pieRect.GetBottom()-pieRect.GetTop(),
0,360);
-#endif
}
else {
GetPartAngles(angles);
@@ -282,19 +290,11 @@ void wxPieCtrl::DrawParts(wxRect& pieRect)
t2 = intAngles[i];
#endif
if(t1 != t2) {
-#if (defined(__WXMAC__) && wxCHECK_VERSION(2,8,2))
- DrawEllipticArc(pieRect.GetLeft(),
- pieRect.GetTop(),
- pieRect.GetRight()-pieRect.GetLeft(),
- pieRect.GetBottom()-pieRect.GetTop(),
- t1,t2);
-#else
m_CanvasDC.DrawEllipticArc(pieRect.GetLeft(),
pieRect.GetTop(),
pieRect.GetRight()-pieRect.GetLeft(),
pieRect.GetBottom()-pieRect.GetTop(),
t1,t2);
-#endif
}
}
}
@@ -304,9 +304,11 @@ void wxPieCtrl::DrawParts(wxRect& pieRect)
void wxPieCtrl::DrawLegend(int left, int top)
{
- unsigned int i;
+ int i;
int dy(m_LegendVerBorder),tw,th=0,titlew,titleh;
-
+ int totalNumLabels = (int)m_Series.Count();
+ int vertSpaceForLabels,maxVisibleLabels,lastLabelToDraw,numSteps;
+
// First determine the size of the legend box
m_CanvasDC.SetFont(m_TitleFont);
m_CanvasDC.GetTextExtent(m_szTitle,&titlew,&titleh);
@@ -316,7 +318,7 @@ void wxPieCtrl::DrawLegend(int left, int top)
m_CanvasDC.SetFont(m_LabelFont);
int maxwidth(titlew + 2*m_legendHorBorder + 15);
- for(i = 0; i < m_Series.Count(); i++)
+ for(i = 0; i < totalNumLabels; i++)
{
m_CanvasDC.GetTextExtent(m_Series[i].GetLabel(), &tw, &th);
dy += (th+3);
@@ -324,7 +326,8 @@ void wxPieCtrl::DrawLegend(int left, int top)
}
dy += m_LegendVerBorder;
- int right(left+maxwidth-1), bottom(top+dy-1);
+ int right = left+maxwidth-1;
+ int bottom = std::min(top+dy-1, GetSize().GetHeight()-top);
if(! IsTransparent())
{
@@ -332,16 +335,39 @@ void wxPieCtrl::DrawLegend(int left, int top)
m_CanvasDC.DrawRectangle(left, top, maxwidth, dy);
}
+ vertSpaceForLabels = GetSize().GetHeight() - (2*top) - m_LegendVerBorder - (titleh+10);
+ maxVisibleLabels = std::min((vertSpaceForLabels/(th+3)), totalNumLabels);
+ numSteps = totalNumLabels - maxVisibleLabels + 1;
+ if (numSteps < 2) {
+ m_scrollBar->Hide();
+ m_scrollBar->SetThumbPosition(0);
+ m_firstlabelToDraw = 0;
+ lastLabelToDraw = totalNumLabels - 1;
+ } else {
+ m_scrollBar->SetSize(GetSize().GetWidth() - m_Scrollbar_width, 0,
+ m_Scrollbar_width, GetSize().GetHeight(), 0);
+ m_firstlabelToDraw = m_scrollBar->GetThumbPosition();
+ lastLabelToDraw = m_firstlabelToDraw + maxVisibleLabels - 1;
+ if (lastLabelToDraw >= totalNumLabels) {
+ lastLabelToDraw = totalNumLabels - 1;
+ m_firstlabelToDraw = totalNumLabels - maxVisibleLabels;
+ }
+
+ m_scrollBar->SetScrollbar(m_firstlabelToDraw, 1, numSteps, 1);
+ m_scrollBar->Show((maxVisibleLabels > 0));
+ }
+
// Now draw the legend title
dy = m_LegendVerBorder+titleh+5;
m_CanvasDC.SetFont(m_TitleFont);
m_CanvasDC.SetTextForeground(m_TitleColour);
m_CanvasDC.DrawText(m_szTitle,left+m_legendHorBorder+2,top+m_LegendVerBorder+2);
-
+ dy += 5;
// Draw the legend items
m_CanvasDC.SetFont(m_LabelFont);
+
m_CanvasDC.SetTextForeground(m_LabelColour);
- for(i = 0; i < m_Series.Count(); i++)
+ for(i = m_firstlabelToDraw; i <= lastLabelToDraw; i++)
{
m_CanvasDC.SetBrush(wxBrush(m_Series[i].GetColour()));
m_CanvasDC.DrawCircle(left+m_legendHorBorder+5, top+dy+th/2, 5);
@@ -352,11 +378,14 @@ void wxPieCtrl::DrawLegend(int left, int top)
// Draw the legend frame
wxPen savedPen = m_CanvasDC.GetPen();
m_CanvasDC.SetPen(*wxGREY_PEN);
- m_CanvasDC.DrawLine(left,top,right,top); // top
- m_CanvasDC.DrawLine(left,bottom,left,top); // left
+ m_CanvasDC.DrawLine(left,top,right,top); // top
+ m_CanvasDC.DrawLine(left,bottom,left,top); // left
m_CanvasDC.SetPen(*wxWHITE_PEN);
- m_CanvasDC.DrawLine(left,bottom,right,bottom); // bottom
- m_CanvasDC.DrawLine(right,top,right,bottom); // right
+ m_CanvasDC.DrawLine(left,bottom,right,bottom); // bottom
+ m_CanvasDC.DrawLine(right,top,right,bottom); // right
+ m_CanvasDC.SetPen(*wxBLACK_PEN);
+ m_CanvasDC.DrawLine(left+1,top+m_LegendVerBorder+titleh+7,
+ right-1,top+m_LegendVerBorder+titleh+7); // divider
m_CanvasDC.SetPen(savedPen);
}
@@ -378,9 +407,6 @@ void wxPieCtrl::Draw(wxPaintDC & pdc)
pieRect.SetBottom(pieRect.GetTop() + maxL);
pieRect.SetRight(pieRect.GetLeft() + maxL);
-#if ! wxCHECK_VERSION(2,8,0)
- m_CanvasDC.BeginDrawing();
-#endif
m_CanvasDC.SetBackground(wxBrush(m_BackColour));
m_CanvasDC.Clear();
if(m_Series.Count())
@@ -391,58 +417,17 @@ void wxPieCtrl::Draw(wxPaintDC & pdc)
else {
//no data, draw an black circle
m_CanvasDC.SetBrush(*wxBLACK_BRUSH);
-#if (defined(__WXMAC__) && wxCHECK_VERSION(2,8,2))
- DrawEllipticArc(pieRect.GetLeft(),
- pieRect.GetTop(),
- pieRect.GetRight()-pieRect.GetLeft(),
- pieRect.GetBottom()-pieRect.GetTop(),
- 0,360);
-#else
m_CanvasDC.DrawEllipticArc(pieRect.GetLeft(),
pieRect.GetTop(),
pieRect.GetRight()-pieRect.GetLeft(),
pieRect.GetBottom()-pieRect.GetTop(),
0,360);
-#endif
}
-#if ! wxCHECK_VERSION(2,8,0)
- m_CanvasDC.EndDrawing();
-#endif
m_CanRepaint = false;
}
pdc.Blit(0,0,bgW,bgH,&m_CanvasDC,0,0);
}
-#if (defined(__WXMAC__) && wxCHECK_VERSION(2,8,2))
-
-// wxGCDC::DoDrawEllipticArc() is broken on wxMac 2.8.2 and
-// later so we have adapted the correct code (from 2.8.1) here
-static inline double DegToRad(double deg)
-{
- return (deg * M_PI) / 180.0;
-}
-
-void wxPieCtrl::DrawEllipticArc( wxCoord x, wxCoord y, wxCoord w, wxCoord h,
- double sa, double ea )
-{
- wxGraphicsContext* graphicContext = m_CanvasDC.GetGraphicsContext();
- wxGraphicsPath path = graphicContext->CreatePath();
- graphicContext->PushState();
- graphicContext->Translate(x+w/2,y+h/2);
- wxDouble factor = ((wxDouble) w) / h;
- graphicContext->Scale( factor , 1.0);
- if (sa!=ea)
- path.MoveToPoint(0,0);
- // since these angles (ea,sa) are measured counter-clockwise, we invert them to
- // get clockwise angles
- path.AddArc( 0, 0, h/2 , DegToRad(-sa) , DegToRad(-ea), sa > ea );
- if (sa!=ea)
- path.AddLineToPoint(0,0);
- graphicContext->DrawPath( path );
- graphicContext->PopState();
-}
-#endif
-
void wxPieCtrl::Refresh(bool eraseBackground, const wxRect* rect)
{
m_CanRepaint = true;
diff --git a/clientgui/common/wxPieCtrl.h b/clientgui/common/wxPieCtrl.h
index caad080..78fcb30 100644
--- a/clientgui/common/wxPieCtrl.h
+++ b/clientgui/common/wxPieCtrl.h
@@ -84,7 +84,10 @@ protected:
unsigned int m_legendHorBorder;
unsigned int m_LegendVerBorder;
wxString m_szTitle;
-
+ wxScrollBar* m_scrollBar;
+ int m_Scrollbar_width;
+ int m_firstlabelToDraw;
+
//internal methods
void GetPartAngles(wxArrayDouble & angles);
void RecreateCanvas();
@@ -92,10 +95,6 @@ protected:
void DrawParts(wxRect& pieRect);
void DrawLegend(int left, int top);
void Draw(wxPaintDC & pdc);
-#if (defined(__WXMAC__) && wxCHECK_VERSION(2,8,2))
- void DrawEllipticArc( wxCoord x, wxCoord y, wxCoord w, wxCoord h,
- double sa, double ea );
-#endif
public:
/// An array of wxPiePart objects for storing information about sectors
wxPieSeries m_Series;
@@ -148,7 +147,8 @@ public:
void OnSize(wxSizeEvent & event);
void OnMouseMove(wxMouseEvent& ev);
void OnEraseBackground(wxEraseEvent & /*event*/);
-
+ void OnLegendScroll(wxScrollEvent& event);
+
#ifdef __WXMAC__
private:
void SetupMacAccessibilitySupport();
diff --git a/clientgui/stdwx.h b/clientgui/stdwx.h
index c46d91a..82eee92 100644
--- a/clientgui/stdwx.h
+++ b/clientgui/stdwx.h
@@ -45,6 +45,7 @@
#define HAVE_GMTIME_R 1
#endif
+
#include <wx/wx.h>
#include <wx/config.h> // configuration support
#include <wx/debug.h> // diagnostics support
diff --git a/configure.ac b/configure.ac
index 8b8b84a..288cd42 100644
--- a/configure.ac
+++ b/configure.ac
@@ -6,11 +6,17 @@ dnl not sure exactly what the minimum version is (but 2.13 wont work)
AC_PREREQ(2.58)
dnl Set the BOINC version here. You can also use the set-version script.
-AC_INIT(BOINC, 7.3.19)
+AC_INIT(BOINC, 7.4.1)
AC_CONFIG_MACRO_DIR([m4])
LIBBOINC_VERSION=`echo ${PACKAGE_VERSION} | sed 's/\./:/g'`
AC_SUBST([LIBBOINC_VERSION])
+WRAPPER_RELEASE=26010
+AC_SUBST([WRAPPER_RELEASE])
+
+VBOXWRAPPER_RELEASE=26089
+AC_SUBST([VBOXWRAPPER_RELEASE])
+
AC_CANONICAL_TARGET
dnl generate .tar.gz, .tar.bz2, .zip
diff --git a/db/boinc_db.cpp b/db/boinc_db.cpp
index 3af5871..d448c7b 100644
--- a/db/boinc_db.cpp
+++ b/db/boinc_db.cpp
@@ -94,6 +94,9 @@ void SCHED_TRIGGER_ITEM::clear() {
void FILESET_SCHED_TRIGGER_ITEM::clear() {memset(this, 0, sizeof(*this));}
void VDA_FILE::clear() {memset(this, 0, sizeof(*this));}
void VDA_CHUNK_HOST::clear() {memset(this, 0, sizeof(*this));}
+void BADGE::clear() {memset(this, 0, sizeof(*this));}
+void BADGE_USER::clear() {memset(this, 0, sizeof(*this));}
+void BADGE_TEAM::clear() {memset(this, 0, sizeof(*this));}
DB_PLATFORM::DB_PLATFORM(DB_CONN* dc) :
DB_BASE("platform", dc?dc:&boinc_db){}
@@ -161,6 +164,12 @@ DB_VDA_FILE::DB_VDA_FILE(DB_CONN* dc) :
DB_BASE("vda_file", dc?dc:&boinc_db){}
DB_VDA_CHUNK_HOST::DB_VDA_CHUNK_HOST(DB_CONN* dc) :
DB_BASE("vda_chunk_host", dc?dc:&boinc_db){}
+DB_BADGE::DB_BADGE(DB_CONN* dc) :
+ DB_BASE("badge", dc?dc:&boinc_db){}
+DB_BADGE_USER::DB_BADGE_USER(DB_CONN* dc) :
+ DB_BASE("badge_user", dc?dc:&boinc_db){}
+DB_BADGE_TEAM::DB_BADGE_TEAM(DB_CONN* dc) :
+ DB_BASE("badge_team", dc?dc:&boinc_db){}
int DB_PLATFORM::get_id() {return id;}
int DB_APP::get_id() {return id;}
@@ -272,7 +281,8 @@ void DB_APP_VERSION::db_print(char* buf){
"pfc_avg=%.15e, "
"pfc_scale=%.15e, "
"expavg_credit=%.15e, "
- "expavg_time=%.15e ",
+ "expavg_time=%.15e, "
+ "beta=%d ",
create_time,
appid,
version_num,
@@ -286,7 +296,8 @@ void DB_APP_VERSION::db_print(char* buf){
pfc.avg,
pfc_scale,
expavg_credit,
- expavg_time
+ expavg_time,
+ beta
);
}
@@ -308,6 +319,7 @@ void DB_APP_VERSION::db_parse(MYSQL_ROW &r) {
pfc_scale = atof(r[i++]);
expavg_credit = atof(r[i++]);
expavg_time = atof(r[i++]);
+ beta = atoi(r[i++]);
}
void DB_USER::db_print(char* buf){
@@ -1485,6 +1497,7 @@ void TRANSITIONER_ITEM::parse(MYSQL_ROW& r) {
res_hostid = safe_atoi(r[i++]);
res_received_time = safe_atoi(r[i++]);
res_app_version_id = safe_atoi(r[i++]);
+ res_exit_status = safe_atoi(r[i++]);
}
int DB_TRANSITIONER_ITEM_SET::enumerate(
@@ -1542,7 +1555,8 @@ int DB_TRANSITIONER_ITEM_SET::enumerate(
" res.sent_time, "
" res.hostid, "
" res.received_time, "
- " res.app_version_id "
+ " res.app_version_id, "
+ " res.exit_status "
"FROM "
" workunit AS wu "
" LEFT JOIN result AS res ON wu.id = res.workunitid "
@@ -1715,6 +1729,7 @@ void VALIDATOR_ITEM::parse(MYSQL_ROW& r) {
int DB_VALIDATOR_ITEM_SET::enumerate(
int appid, int nresult_limit,
int wu_id_modulus, int wu_id_remainder,
+ int wu_id_min, int wu_id_max,
std::vector<VALIDATOR_ITEM>& items
) {
int retval;
@@ -1731,6 +1746,12 @@ int DB_VALIDATOR_ITEM_SET::enumerate(
} else {
strcpy(mod_clause, "");
}
+ if (wu_id_min) {
+ sprintf(mod_clause+(strlen(mod_clause)), " and wu.id >= %d", wu_id_min);
+ }
+ if (wu_id_max) {
+ sprintf(mod_clause+(strlen(mod_clause)), " and wu.id <= %d", wu_id_max);
+ }
sprintf(query,
"SELECT "
@@ -2600,4 +2621,37 @@ void DB_VDA_CHUNK_HOST::db_parse(MYSQL_ROW &r) {
transfer_send_time = atof(r[i++]);
}
+void DB_BADGE::db_parse(MYSQL_ROW &r) {
+ int i=0;
+ clear();
+ id = atoi(r[i++]);
+ create_time = atof(r[i++]);
+ type = atoi(r[i++]);
+ strcpy2(name, r[i++]);
+ strcpy2(title, r[i++]);
+ strcpy2(description, r[i++]);
+ strcpy2(image_url, r[i++]);
+ strcpy2(level, r[i++]);
+ strcpy2(tags, r[i++]);
+ strcpy2(sql_rule, r[i++]);
+}
+
+void DB_BADGE_USER::db_parse(MYSQL_ROW &r) {
+ int i=0;
+ clear();
+ badge_id = atoi(r[i++]);
+ user_id = atoi(r[i++]);
+ create_time = atof(r[i++]);
+ reassign_time = atof(r[i++]);
+}
+
+void DB_BADGE_TEAM::db_parse(MYSQL_ROW &r) {
+ int i=0;
+ clear();
+ badge_id = atoi(r[i++]);
+ team_id = atoi(r[i++]);
+ create_time = atof(r[i++]);
+ reassign_time = atof(r[i++]);
+}
+
const char *BOINC_RCSID_ac374386c8 = "$Id$";
diff --git a/db/boinc_db.h b/db/boinc_db.h
index bb9ba61..79c2d6a 100644
--- a/db/boinc_db.h
+++ b/db/boinc_db.h
@@ -67,6 +67,7 @@ struct TRANSITIONER_ITEM {
int res_hostid;
int res_received_time;
int res_app_version_id;
+ int res_exit_status;
void clear();
void parse(MYSQL_ROW&);
@@ -265,6 +266,8 @@ public:
int nresult_limit,
int wu_id_modulus,
int wu_id_remainder,
+ int wu_id_min,
+ int wu_id_max,
std::vector<VALIDATOR_ITEM>& items
);
int update_result(RESULT&);
@@ -509,4 +512,23 @@ struct DB_VDA_CHUNK_HOST : public DB_BASE, public VDA_CHUNK_HOST {
void db_parse(MYSQL_ROW &row);
};
+struct DB_BADGE : public DB_BASE, public BADGE {
+ DB_BADGE(DB_CONN* p=0);
+ int get_id() {return id;};
+ void db_print(char*){};
+ void db_parse(MYSQL_ROW&);
+};
+
+struct DB_BADGE_USER : public DB_BASE, public BADGE_USER {
+ DB_BADGE_USER(DB_CONN* p=0);
+ void db_print(char*){};
+ void db_parse(MYSQL_ROW&);
+};
+
+struct DB_BADGE_TEAM : public DB_BASE, public BADGE_TEAM {
+ DB_BADGE_TEAM(DB_CONN* p=0);
+ void db_print(char*){};
+ void db_parse(MYSQL_ROW&);
+};
+
#endif
diff --git a/db/boinc_db_types.h b/db/boinc_db_types.h
index 5778ac4..e4dde79 100644
--- a/db/boinc_db_types.h
+++ b/db/boinc_db_types.h
@@ -134,6 +134,7 @@ struct APP_VERSION {
// relative to that of the most efficient version
double expavg_credit;
double expavg_time;
+ bool beta;
// the following used by scheduler, not in DB
//
@@ -761,4 +762,34 @@ struct VDA_CHUNK_HOST {
void print_status(int level);
};
+struct BADGE {
+ int id;
+ double create_time;
+ int type;
+ char name[256];
+ char title[256];
+ char description[256];
+ char image_url[256];
+ char level[256];
+ char tags[256];
+ char sql_rule[256];
+ void clear();
+};
+
+struct BADGE_USER {
+ int badge_id;
+ int user_id;
+ double create_time;
+ double reassign_time;
+ void clear();
+};
+
+struct BADGE_TEAM {
+ int badge_id;
+ int team_id;
+ double create_time;
+ double reassign_time;
+ void clear();
+};
+
#endif
diff --git a/db/db_base.cpp b/db/db_base.cpp
index 66e596a..8a0906a 100644
--- a/db/db_base.cpp
+++ b/db/db_base.cpp
@@ -359,6 +359,8 @@ int DB_BASE::update_fields_noid(const char* set_clause, const char* where_clause
return 0;
}
+// Note: AFAIK, if one enumeration is active you can't do another one
+//
int DB_BASE::enumerate(const char* clause, bool use_use_result) {
int x;
char query[MAX_QUERY_LEN];
diff --git a/db/schema.sql b/db/schema.sql
index b4af601..ffe703f 100644
--- a/db/schema.sql
+++ b/db/schema.sql
@@ -75,6 +75,7 @@ create table app_version (
pfc_scale double not null default 0,
expavg_credit double not null default 0,
expavg_time double not null default 0,
+ beta tinyint not null default 0,
primary key (id)
) engine=InnoDB;
diff --git a/doc/index.php b/doc/index.php
index d15bf09..05c8900 100644
--- a/doc/index.php
+++ b/doc/index.php
@@ -107,6 +107,9 @@ function show_participate() {
"<a href=\"http://bam.boincstats.com/\">",
"</a>"
)."
+ <p>
+ For Android devices, download the BOINC or <a href=http://www.htc.com/www/go/power-to-give-faqs/>HTC Power To Give</a> app from the Google Play Store.
+ <img align=right valign=top height=50 src=images/htc-power-to-give.jpg>
</td></tr>
";
}
diff --git a/doc/projects.inc b/doc/projects.inc
index bde3112..0ee41e5 100644
--- a/doc/projects.inc
+++ b/doc/projects.inc
@@ -160,7 +160,7 @@ $biomed = array(
"http://boinc.umiacs.umd.edu/",
tra("University of Maryland Center for Bioinformatics and Computational Biology"),
tra("Life science research"),
- tra("The Lattice Project supplies computing power to scientists at the University of Maryland studying evolutionary relationships based on DNA sequence data; bacterial, plasmid, and virus protein sequences; and biological diversity in nature reserves. "),
+ tra("The Lattice Project, developed and administered at the University of Maryland, supplies computing power to researchers worldwide who are studying evolutionary relationships using DNA and protein sequence data."),
"lattice.gif",
"",
"Study evolution using genetics"
diff --git a/doc/versions.inc b/doc/versions.inc
index 0cfe542..af85221 100644
--- a/doc/versions.inc
+++ b/doc/versions.inc
@@ -44,12 +44,12 @@ function linux_info() {
}
$w74dev = array(
- "num"=>"7.3.11",
+ "num"=>"7.3.19",
"status"=>"Development version",
- "file"=>"boinc_7.3.11_windows_intelx86.exe",
- "vbox_file"=>"boinc_7.3.11_windows_intelx86_vbox.exe",
- "vbox_version"=>"4.2.16",
- "date"=>"10 Mar 2014",
+ "file"=>"boinc_7.3.19_windows_intelx86.exe",
+ "vbox_file"=>"boinc_7.3.19_windows_intelx86_vbox.exe",
+ "vbox_version"=>"4.3.8",
+ "date"=>"15 May 2014",
"type"=>"win_new",
);
@@ -88,12 +88,12 @@ $w5816 = array(
);
$w74x64dev = array(
- "num"=>"7.3.11",
+ "num"=>"7.3.19",
"status"=>"Development version",
- "file"=>"boinc_7.3.11_windows_x86_64.exe",
- "vbox_file"=>"boinc_7.3.11_windows_x86_64_vbox.exe",
- "vbox_version"=>"4.2.16",
- "date"=>"10 Mar 2014",
+ "file"=>"boinc_7.3.19_windows_x86_64.exe",
+ "vbox_file"=>"boinc_7.3.19_windows_x86_64_vbox.exe",
+ "vbox_version"=>"4.3.8",
+ "date"=>"15 May 2014",
"type"=>"win_new",
);
@@ -116,10 +116,10 @@ $w70x64 = array(
);
$m74dev = array(
- "num"=>"7.3.13",
+ "num"=>"7.3.19",
"status"=>"Development version (standard GUI)",
- "file"=>"boinc_7.3.13_macOSX_i686.zip",
- "date"=>"13 Mar 2014",
+ "file"=>"boinc_7.3.19_macOSX_i686.zip",
+ "date"=>"15 May 2014",
"type"=>"mac_advanced",
);
@@ -156,10 +156,10 @@ $m66 = array(
);
$m74cdev = array(
- "num"=>"7.3.13",
+ "num"=>"7.3.19",
"status"=>"Development version (Unix command-line version)",
- "file"=>"boinc_7.3.13_i686-apple-darwin.zip",
- "date"=>"13 Mar 2014",
+ "file"=>"boinc_7.3.19_i686-apple-darwin.zip",
+ "date"=>"15 May 2014",
"type"=>"bare_core",
);
@@ -212,10 +212,10 @@ $l66 = array(
);
$l74ubuntudev = array(
- "num"=>"7.3.11",
+ "num"=>"7.3.19",
"status"=>"Development version",
- "file"=>"boinc_7.3.11_i686-pc-linux-gnu.sh",
- "date"=>"12 Mar 2014",
+ "file"=>"boinc_7.3.19_i686-pc-linux-gnu.sh",
+ "date"=>"15 May 2014",
"type"=>"sea",
);
@@ -236,10 +236,10 @@ $l70ubuntu = array(
);
$l74ubuntux64dev = array(
- "num"=>"7.3.11",
+ "num"=>"7.3.19",
"status"=>"Development version",
- "file"=>"boinc_7.3.11_x86_64-pc-linux-gnu.sh",
- "date"=>"12 Mar 2014",
+ "file"=>"boinc_7.3.19_x86_64-pc-linux-gnu.sh",
+ "date"=>"15 May 2014",
"type"=>"sea",
);
diff --git a/html/Makefile.am b/html/Makefile.am
index 9862d8d..c27e634 100644
--- a/html/Makefile.am
+++ b/html/Makefile.am
@@ -1,4 +1,4 @@
htmldir=$(DESTDIR)/$(prefix)/share/boinc-server-maker/html
install-data-local:
mkdir -p $(htmldir)
- cp -r inc languages ops user $(htmldir)
+ cp -r $(srcdir)/inc $(srcdir)/languages $(srcdir)/ops $(srcdir)/user $(htmldir)
diff --git a/html/bt/README b/html/bt/README
index aadc421..3d49a4d 100644
--- a/html/bt/README
+++ b/html/bt/README
@@ -1,3 +1,7 @@
+NOTE: THE CODE IN THIS DIRECTORY DOESN'T CURRENTLY WORK,
+AND IS NOT BEING MAINTAINED.
+IT'S HERE IN CASE SOMEONE WANTS TO REVIVE IT.
+
-----------------------------------
Bittorrent file distribution system
-----------------------------------
@@ -40,4 +44,4 @@ technology because of its unique ability to distribute load. To name a few uses:
How do I enable this?
---------------------
-Have a look at the INSTALL file located in the same directory as this file.
\ No newline at end of file
+Have a look at the INSTALL file located in the same directory as this file.
diff --git a/html/inc/db_ops.inc b/html/inc/db_ops.inc
index 9ff7b92..ae52f71 100644
--- a/html/inc/db_ops.inc
+++ b/html/inc/db_ops.inc
@@ -703,10 +703,10 @@ function show_app_version($app_version) {
row("Version num", $app_version->version_num);
row("Platform", "<a href=\"db_action.php?table=platform&id=$app_version->platformid\">" . platform_name_by_id($app_version->platformid) . "</a>" );
row("XML doc", "<pre>".htmlspecialchars($app_version->xml_doc)."</pre>");
- row("Min_core_version", $app_version->min_core_version);
- row("Max_core_version", $app_version->max_core_version);
- //row("Message", $app_version->message);
- row("Deprecated", $app_version->deprecated);
+ row("min_core_version", $app_version->min_core_version);
+ row("max_core_version", $app_version->max_core_version);
+ row("deprecated", $app_version->deprecated);
+ row("plan_class", $app_version->plan_class);
end_table();
}
diff --git a/html/inc/forum_db.inc b/html/inc/forum_db.inc
index 0ebaa02..9bfe99b 100644
--- a/html/inc/forum_db.inc
+++ b/html/inc/forum_db.inc
@@ -171,6 +171,10 @@ class BoincForumPrefs {
$db = BoincDb::get();
return $db->delete_aux('forum_preferences', "userid=$this->userid");
}
+ static function enum($clause=null) {
+ $db = BoincDb::get();
+ return $db->enum('forum_preferences', 'BoincForumPrefs', $clause);
+ }
}
class BoincForumLogging {
diff --git a/html/inc/sandbox.inc b/html/inc/sandbox.inc
index b521767..f02fea8 100644
--- a/html/inc/sandbox.inc
+++ b/html/inc/sandbox.inc
@@ -104,6 +104,7 @@ function sandbox_file_names($user) {
if ($f == '..') continue;
$names[] = $f;
}
+ natsort($names);
return $names;
}
diff --git a/html/inc/text_transform.inc b/html/inc/text_transform.inc
index b370056..623a9d9 100644
--- a/html/inc/text_transform.inc
+++ b/html/inc/text_transform.inc
@@ -109,8 +109,8 @@ function bb2html($text, $export=false) {
$httpsregex = "(?:\"?)https\:\/\/([^\[\"<\ ]+)(?:\"?)";
// List of allowable tags
$bbtags = array (
- "@\[pre\](.*?)\[/pre\]@eis",
- "@\[code\](.*?)\[/code\]@eis",
+ "@\[pre\](.*?)\[/pre\]@is",
+ "@\[code\](.*?)\[/code\]@is",
"@\[b\](.*?)\[/b\]@is",
"@\[i\](.*?)\[/i\]@is",
"@\[u\](.*?)\[/u\]@is",
@@ -142,8 +142,8 @@ function bb2html($text, $export=false) {
// What the above tags are turned in to
if ($export) {
$htmltags = array (
- "'<pre>'.stop_recursion(remove_br('\\1')).'</pre>'",
- "'<code>'.stop_recursion('\\1').'</code>'",
+ "<pre>\\1</pre>",
+ "<code>\\1</code>",
"<b>\\1</b>",
"<i>\\1</i>",
"<u>\\1</u>",
@@ -170,8 +170,8 @@ function bb2html($text, $export=false) {
);
} else {
$htmltags = array (
- "'<div class=\"pre\">'.stop_recursion(remove_br('\\1')).'</div>'",
- "'<div class=\"code\">'.stop_recursion('\\1').'</div>'",
+ "<div class=\"pre\">\\1</div>",
+ "<div class=\"code\">\\1</div>",
"<b>\\1</b>",
"<i>\\1</i>",
"<u>\\1</u>",
@@ -218,15 +218,6 @@ function remove_br($text){
return str_replace("<br />", "", $text);
}
-
-// Stops recursion of BBCode by escaping any [ in the given text
-// @param $text The text to stop recursion in
-// @return A text that no longer can cause recursion
-//
-function stop_recursion($text){
- return str_replace("[", "[", $text);
-}
-
// Make links open in new windows.
function externalize_links($text) {
// TODO: Convert this to PCRE
diff --git a/html/inc/util.inc b/html/inc/util.inc
index 1920eca..533edcb 100644
--- a/html/inc/util.inc
+++ b/html/inc/util.inc
@@ -940,6 +940,7 @@ function redirect_to_secure_url($url) {
}
function show_badges($is_user, $item) {
+ if (DISABLE_BADGES) return;
if ($is_user) {
$bus = BoincBadgeUser::enum("user_id=$item->id");
} else {
diff --git a/html/ops/db_update.php b/html/ops/db_update.php
index a4719f0..57fa4d8 100644
--- a/html/ops/db_update.php
+++ b/html/ops/db_update.php
@@ -933,6 +933,14 @@ function update_5_3_2014() {
);
}
+function update_6_5_2014() {
+ do_query(
+ "alter table app_version
+ add beta tinyint not null
+ "
+ );
+}
+
// Updates are done automatically if you use "upgrade".
//
// If you need to do updates manually,
@@ -974,6 +982,7 @@ $db_updates = array (
array(27007, "update_3_6_2014"),
array(27008, "update_4_2_2014"),
array(27009, "update_5_3_2014"),
+ array(27010, "update_6_5_2014"),
);
?>
diff --git a/html/ops/delete_spammers.php b/html/ops/delete_spammers.php
index 8e185e2..e7cdd38 100755
--- a/html/ops/delete_spammers.php
+++ b/html/ops/delete_spammers.php
@@ -17,33 +17,50 @@
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
-// script to delete spammer accounts and profiles.
+// script to delete spammer accounts, profiles, and forum posts.
//
-// delete_spammers.php --list filename
+// delete_spammers.php [--days n] [--test] command
+//
+// commands:
+// --list filename
// "filename" contains a list of user IDs, one per line.
//
-// delete_spammers.php --auto
+// --profiles
// delete accounts that
+// - have a profile containing a link.
// - have no hosts
// - have no message-board posts
-// - have a profile containing a link.
-// NOTE: use this with caution. See delete_auto() below.
-// Run it in test mode first.
+//
+// --forums
+// delete accounts that
+// - have no hosts
+// - have message-board posts
+//
+// options:
+// --days N
+// Only delete accounts create in last N days
+// --test
+// Show what accounts would be deleted, but don't delete them
require_once("../inc/db.inc");
require_once("../inc/profile.inc");
require_once("../inc/forum.inc");
db_init();
+$days = 0;
+$test = false;
+
// delete a spammer account, and everything associated with it
//
-function delete_user($id) {
- $user = new StdClass;
- $user->id = $id;
- echo "deleting user $id\n";
+function delete_user($user) {
+ global $test;
+ echo "deleting user $user->id email $user->email_addr name $user->name\n";
+ if ($test) {
+ return;
+ }
delete_profile($user);
forum_delete_user($user);
- $q = "delete from user where id=$id";
+ $q = "delete from user where id=$user->id";
mysql_query($q);
}
@@ -53,7 +70,12 @@ function delete_list($fname) {
while ($s = fgets($f)) {
$s = trim($s);
if (!is_numeric($s)) die("bad ID $s\n");
- delete_user($s);
+ $user = BoincUser::lookup_id($s);
+ if ($user) {
+ delete_user($user);
+ } else {
+ echo "no user ID $s\n";
+ }
}
}
@@ -64,7 +86,29 @@ function has_link($x) {
return false;
}
-function delete_auto() {
+function delete_forums() {
+ global $days;
+ $prefs = BoincForumPrefs::enum("posts>0");
+ foreach ($prefs as $p) {
+ $user = BoincUser::lookup_id($p->userid);
+ if (!$user) {
+ echo "missing user $p->userid\n";
+ continue;
+ }
+ if ($days) {
+ if ($user->create_time < time() - $days*86400) continue;
+ }
+ if ($user->teamid) {
+ continue;
+ }
+ $n = BoincHost::count("userid=$p->userid");
+ if ($n) continue;
+ delete_user($user);
+ }
+}
+
+function delete_profiles() {
+ global $test, $days;
$profiles = BoincProfile::enum("");
foreach ($profiles as $p) {
if (has_link($p->response1) || has_link($p->response2)) {
@@ -74,32 +118,45 @@ function delete_auto() {
continue;
}
- // uncomment the following to delete only recent accounts
- //
- //if ($user->create_time < time() - 60*86400) continue;
+ if ($days) {
+ if ($user->create_time < time() - $days*86400) continue;
+ }
$n = BoincHost::count("userid=$p->userid");
if ($n) continue;
$n = BoincPost::count("user=$p->userid");
if ($n) continue;
- // By default, show profile but don't delete anything
- // Change 0 to 1 if you want to actually delete
- //
- if (0) {
- delete_user($user->id);
- } else {
+ delete_user($user);
+ if ($test) {
echo "------------\n$p->userid\n$p->response1\n$p->response2\n";
}
}
}
}
+function delete_banished() {
+ global $days;
+ $fps = BoincForumPrefs::enum("banished_until>0");
+ foreach ($fps as $fp) {
+ $user = BoincUser::lookup_id($fp->userid);
+ if (!$user) continue;
+ if ($user->create_time < time() - $days*86400) continue;
+ delete_user($user);
+ }
+}
+
for ($i=1; $i<$argc; $i++) {
- if ($argv[$i] == "--list") {
+ if ($argv[$i] == "--test") {
+ $test = true;
+ } else if ($argv[$i] == "--days") {
+ $days = $argv[++$i];
+ } else if ($argv[$i] == "--list") {
delete_list($argv[++$i]);
- } else if ($argv[$i] == "--auto") {
- delete_auto();
+ } else if ($argv[$i] == "--profiles") {
+ delete_profiles();
+ } else if ($argv[$i] == "--forums") {
+ delete_forums();
} else if ($argv[$i] == "--id_range") {
$id1 = $argv[++$i];
$id2 = $argv[++$i];
@@ -110,6 +167,10 @@ for ($i=1; $i<$argc; $i++) {
echo "deleting $i\n";
delete_user($i);
}
+ } else if ($argv[$i] == "--banished") {
+ delete_banished();
+ } else {
+ echo "usage: delete_spammers.php [--list file] [--id_range N M] [--auto]\n";
}
}
diff --git a/html/ops/manage_app_versions.php b/html/ops/manage_app_versions.php
index 1babf6d..7d7e103 100644
--- a/html/ops/manage_app_versions.php
+++ b/html/ops/manage_app_versions.php
@@ -25,6 +25,9 @@ function update() {
$av = BoincAppVersion::lookup_id($id);
if (!$av) error_page("no such app version");
+ $n = post_str("beta", true) ? 1 : 0;
+ $av->update("beta=$n");
+
$n = post_str("deprecated", true) ? 1 : 0;
$av->update("deprecated=$n");
@@ -50,13 +53,14 @@ function show_form() {
start_table("");
table_header(
- "ID #",
- "Application",
+ "ID #<br><span class=note>click for details</span>",
+ "Application<br><span class=note>click for details</span>",
"Version",
"Platform",
"Plan Class",
"minimum<br>client version",
"maximum<br>client version",
+ "beta?",
"deprecated?",
""
);
@@ -76,10 +80,10 @@ function show_form() {
echo "<tr class=row$i><form action=manage_app_versions.php method=POST>\n";
$i = 1-$i;
echo "<input type=hidden name=id value=$av->id>";
- echo " <TD>$f1 $av->id $f2</TD>\n";
+ echo " <TD>$f1 <a href=db_action.php?table=app_version&id=$av->id>$av->id</a> $f2</TD>\n";
$app = $apps[$av->appid];
- echo " <TD>$f1 $app->name $f2</TD>\n";
+ echo " <TD>$f1 <a href=app_details.php?appid=$app->id>$app->name</a> $f2</TD>\n";
echo " <TD>$f1 $av->version_num $f2</TD>\n";
@@ -88,15 +92,20 @@ function show_form() {
echo " <td>$f1 $av->plan_class $f2</td>\n";
- $v=$av->min_core_version;
+ $v = $av->min_core_version;
echo " <TD><input type='text' size='4' name=min_core_version value='$v'></TD>\n";
$v=$av->max_core_version;
echo " <TD><input type='text' size='4' name=max_core_version value='$v'></TD>\n";
$v='';
- if($av->deprecated) $v=' CHECKED ';
+ if ($av->beta) $v=' CHECKED ';
+ echo " <TD> <input name=beta type='checkbox' $v></TD>\n";
+
+ $v='';
+ if ($av->deprecated) $v=' CHECKED ';
echo " <TD> <input name=deprecated type='checkbox' $v></TD>\n";
+
echo "<td><input name=submit type=submit value=Update>";
echo "</tr></form>";
diff --git a/html/ops/manage_apps.php b/html/ops/manage_apps.php
index 1cbeb6f..9fe349a 100644
--- a/html/ops/manage_apps.php
+++ b/html/ops/manage_apps.php
@@ -53,8 +53,8 @@ function do_updates() {
}
function add_app() {
- $name = mysql_real_escape_string(post_str('add_name'));
- $user_friendly_name = mysql_real_escape_string(post_str('add_user_friendly_name'));
+ $name = BoincDb::escape_string(post_str('add_name'));
+ $user_friendly_name = BoincDb::escape_string(post_str('add_user_friendly_name'));
if (empty($name) || empty($user_friendly_name) ) {
error_page(
"To add a new application please supply both a brief name and a longer 'user-friendly' name.</font></p>"
diff --git a/html/ops/mass_email_script.php b/html/ops/mass_email_script.php
index 4872c69..9a3e38b 100755
--- a/html/ops/mass_email_script.php
+++ b/html/ops/mass_email_script.php
@@ -303,7 +303,7 @@ function main() {
echo 'All done!' . "\n";
}
-if (!$USE_PHPMAILER) {
+if (!function_exists('make_php_mailer')) {
echo "You must use PHPMailer.\n";
exit();
}
diff --git a/html/ops/remind.php b/html/ops/remind.php
index 2313ede..a6b88d6 100755
--- a/html/ops/remind.php
+++ b/html/ops/remind.php
@@ -312,7 +312,7 @@ function do_lapsed() {
mysql_free_result($result);
}
-if (!$USE_PHPMAILER) {
+if (!function_exists('make_php_mailer')) {
echo "You must use PHPMailer (http://phpmailer.sourceforge.net)\n";
exit();
}
diff --git a/html/ops/white.css b/html/ops/white.css
index 7da7715..f8fdaa0 100644
--- a/html/ops/white.css
+++ b/html/ops/white.css
@@ -1,108 +1,207 @@
-a:link {
- color: blue;
+a, a:link, a:visited, a:active {
+ color: #0069A1;
}
-a:visited {
- color: blue;
+body {
+ #background: #fff url("img/gray_gradient.png") repeat-x;
+ color: black;
}
-a:active {
- color: blue;
+h1 {
+ color: #203C66;
}
-body , table , input , select {
- font-family: Verdana, Arial, Sans Serif;
- font-size: small;
+hr {
+ border:0;
+ border-top: 2px solid #e8e8e8;
}
-body {
- background-color: white;
- color: black;
-}
-
-table {
- border: 0px;
+hr.news_line {
+ border: 0;
+ border-bottom: 1px solid rgb(200, 200, 200);
}
table.bordered {
- border: 1px solid black;
+ border-color: #e8e8e8;
+ border-width: 2px;
+ border-style: solid;
+ border-radius: 6px;
}
th {
- background-color: #ffffcc;
- font-weight: bold;
-}
-
-td {
- border: 1px solid white;
+ background-color: #c0c0c0;
}
td.bordered {
- border: 1px solid grey;
+ border-color: gray;
}
td.indent {
- border-left: 4px solid white;
+ border-color: #fff;
}
td.heading {
- background-color: rgb(217,217,217);
- font-weight: bold;
+ background-color: #d8d8d8;
}
td.fieldname {
- background-color: rgb(237,237,237);
+ background-color: #eee;
+ color: #333;
+}
+
+td.fieldname_error,
+td.fieldvalue_error {
+ background-color: #f88;
+}
+
+td.friend {
+ background-color: #e8e8e8;
+}
+
+.row0 {
+ background-color: #d9d9d9;
+}
+
+.row1 {
+ background-color: #eee;
+}
+
+.highlighted_row0 {
+ background-color: #b9d9f9;
+}
+
+.highlighted_row1 {
+ background-color: #ceeefe;
+}
+
+.row_hd0 {
+ background-color: #cffacf;
+}
+
+.row_hd1 {
+ background-color: #defade;
+}
+
+tr.message {
+ background-color: #e0e0e0;
+}
+
+input[type="button"],
+input[type="submit"],
+.btn,
+a.button {
+ background: #EDEDED url(img/white_grad.png) repeat-x scroll left top;
+ color: black;
+ border-color: #ccc;
+}
+
+input[type="button"]:hover,
+input[type="submit"]:hover,
+input.btn:hover,
+.button:hover ,
+.forum_toplinks a:hover {
+ border: 1px solid #555;
+ color: #0069A1;
+}
+
+input[type="text"], input[type="password"], select, textarea {
+ border-color: #bbb;
+}
+
+input[type="text"]:focus, input[type="password"]:focus, select:focus, textarea:focus {
+ border-color: #0069A1;
+}
+
+td.news {
+ background-color: #dff0ff;
+ border-color: #add8e6;
+}
+
+td.uotd {
+ background-color: #d3d3d3;
+ border-color: #eee;
}
td.category {
- border: 1px solid black;
+ background-color: #ddd;
+}
+
+td.postheader {
+ background-color: #eee;
+ color: #333;
}
-tr.row0 {
- background-color: rgb(217,217,217);
+td.postbody {
+ background-color: #fff;
}
-tr.row1 {
- background-color: rgb(237,237,237);
+td.postfooter {
+ background-color: #eee;
+ color: #333;
}
-tr.subtitle {
+tr.postseparator {
+ background-color: #c8c8c8;
+ border-color: #aaa;
+}
+
+div.authorcol {
background-color: white;
- color: black;
- font-weight: bold;
+ border-color: #c8c8c8;
}
-tr.message {
- background-color:#E0E0EF;
+.authorinfo {
+ color: #333;
}
-input , select {
- vertical-align: middle;
+.authorinfo img {
+ border-color: #a8a8a8;
}
-h1 , h2 {
- color: black;
- font-size: x-large;
- font-weight: normal;
- margin-top: 10px;
+blockquote.postbody {
+ border-left-color: #0089e1;
+ background-color: #f5fffa;
}
-h3 , h4 {
- color: black;
- font-size: small;
- font-weight: bold;
+span.news_date {
+ color: rgb(100,100,100);
+ font-size: 0.9em;
+ float: right;
+}
+
+span.highlight {
+ background-color: #ffc;
+}
+
+.code {
+ border-left-color: #caa;
}
-img {
- border: 0px;
+.error {
+ color: #f00;
}
-.title {
- font-size: small;
- font-weight: bold;
+.notice {
+ color: #090;
}
-.description {
- font-size: 80%;
- font-weight: normal;
+div.pm_preview {
+ border-width: 2px;
+ border-style: solid;
+ margin: 1em;
+ padding: 0.2em;
+ font-size: 1.1em;
+ border-bottom-width: 1px;
+ border-bottom-style: solid;
+
+ border-color: #ccc;
+ background-color: #eee;
+ border-bottom-color: #ccc;
}
+
+/* Server Status Page */
+
+td.running { background-color: #9aff4f; }
+
+td.notrunning { background-color: #ff4f4f; }
+td.disabled { background-color: #bbb; }
diff --git a/html/user/ffmail_action.php b/html/user/ffmail_action.php
index ac6b73b..bd0c682 100644
--- a/html/user/ffmail_action.php
+++ b/html/user/ffmail_action.php
@@ -67,7 +67,7 @@ if ($action=='Preview') {
$e = get_str("e$i", true);
if ($n && $e) {
$found = true;
- $mail = new PHPMailer();
+ $mail = make_php_mailer();
$mail->AddAddress($e, $n);
$mail->Subject = $subject;
if ($html) {
@@ -78,8 +78,6 @@ if ($action=='Preview') {
}
$mail->From = $uemail;
$mail->FromName = $uname;
- $mail->Host = $PHPMAILER_HOST;
- $mail->Mailer = $PHPMAILER_MAILER;
if ($mail->Send()) {
echo "<br>".tra("email sent successfully to %1", $e)."\n";
} else {
diff --git a/html/user/img/75pct_white.png b/html/user/img/75pct_white.png
index a620f0a..60432d4 100644
Binary files a/html/user/img/75pct_white.png and b/html/user/img/75pct_white.png differ
diff --git a/html/user/img/blue_gradient.png b/html/user/img/blue_gradient.png
index e169a3b..1507402 100644
Binary files a/html/user/img/blue_gradient.png and b/html/user/img/blue_gradient.png differ
diff --git a/html/user/img/boinc_fade_600.png b/html/user/img/boinc_fade_600.png
index b174eed..90bd6a1 100644
Binary files a/html/user/img/boinc_fade_600.png and b/html/user/img/boinc_fade_600.png differ
diff --git a/html/user/img/boincstats_icon.png b/html/user/img/boincstats_icon.png
index 4f44083..af741fc 100644
Binary files a/html/user/img/boincstats_icon.png and b/html/user/img/boincstats_icon.png differ
diff --git a/html/user/img/bronze.png b/html/user/img/bronze.png
index 1b5c14b..70fd2f5 100644
Binary files a/html/user/img/bronze.png and b/html/user/img/bronze.png differ
diff --git a/html/user/img/emphasized_post.png b/html/user/img/emphasized_post.png
index 876493e..690118c 100644
Binary files a/html/user/img/emphasized_post.png and b/html/user/img/emphasized_post.png differ
diff --git a/html/user/img/filtered_post.png b/html/user/img/filtered_post.png
index cc44577..87d38bc 100644
Binary files a/html/user/img/filtered_post.png and b/html/user/img/filtered_post.png differ
diff --git a/html/user/img/freedc_icon.png b/html/user/img/freedc_icon.png
index ac5b4ef..4fe74f5 100644
Binary files a/html/user/img/freedc_icon.png and b/html/user/img/freedc_icon.png differ
diff --git a/html/user/img/gold.png b/html/user/img/gold.png
index f1521de..1a5c2db 100644
Binary files a/html/user/img/gold.png and b/html/user/img/gold.png differ
diff --git a/html/user/img/google-button.png b/html/user/img/google-button.png
index f2b2cd9..168bb5a 100644
Binary files a/html/user/img/google-button.png and b/html/user/img/google-button.png differ
diff --git a/html/user/img/gray_gradient.png b/html/user/img/gray_gradient.png
index 1760407..2ed1b18 100644
Binary files a/html/user/img/gray_gradient.png and b/html/user/img/gray_gradient.png differ
diff --git a/html/user/img/head_20.png b/html/user/img/head_20.png
index 2e1f117..5189702 100644
Binary files a/html/user/img/head_20.png and b/html/user/img/head_20.png differ
diff --git a/html/user/img/hidden.png b/html/user/img/hidden.png
index 51b2b3c..1b74fec 100644
Binary files a/html/user/img/hidden.png and b/html/user/img/hidden.png differ
diff --git a/html/user/img/paypal_logo.png b/html/user/img/paypal_logo.png
index e2c75ba..7422871 100644
Binary files a/html/user/img/paypal_logo.png and b/html/user/img/paypal_logo.png differ
diff --git a/html/user/img/pct_1.png b/html/user/img/pct_1.png
index 1d10115..734067a 100644
Binary files a/html/user/img/pct_1.png and b/html/user/img/pct_1.png differ
diff --git a/html/user/img/pct_25.png b/html/user/img/pct_25.png
index dfbedb2..ed8c425 100644
Binary files a/html/user/img/pct_25.png and b/html/user/img/pct_25.png differ
diff --git a/html/user/img/pct_5.png b/html/user/img/pct_5.png
index 8c742ad..1c9f05b 100644
Binary files a/html/user/img/pct_5.png and b/html/user/img/pct_5.png differ
diff --git a/html/user/img/pm.png b/html/user/img/pm.png
index 3b98b66..b4c100f 100644
Binary files a/html/user/img/pm.png and b/html/user/img/pm.png differ
diff --git a/html/user/img/post.png b/html/user/img/post.png
index b24b105..ecd4153 100644
Binary files a/html/user/img/post.png and b/html/user/img/post.png differ
diff --git a/html/user/img/rate_negative.png b/html/user/img/rate_negative.png
index 5ae3414..baffab8 100644
Binary files a/html/user/img/rate_negative.png and b/html/user/img/rate_negative.png differ
diff --git a/html/user/img/rate_positive.png b/html/user/img/rate_positive.png
index b261639..ea82a4d 100644
Binary files a/html/user/img/rate_positive.png and b/html/user/img/rate_positive.png differ
diff --git a/html/user/img/report_post.png b/html/user/img/report_post.png
index aff8baf..e13e4ed 100644
Binary files a/html/user/img/report_post.png and b/html/user/img/report_post.png differ
diff --git a/html/user/img/silver.png b/html/user/img/silver.png
index 4fa0649..504ce27 100644
Binary files a/html/user/img/silver.png and b/html/user/img/silver.png differ
diff --git a/html/user/img/sticky_locked_post.png b/html/user/img/sticky_locked_post.png
index a754718..f389ba3 100644
Binary files a/html/user/img/sticky_locked_post.png and b/html/user/img/sticky_locked_post.png differ
diff --git a/html/user/img/sticky_post.png b/html/user/img/sticky_post.png
index e5b5bb4..cbb6bd1 100644
Binary files a/html/user/img/sticky_post.png and b/html/user/img/sticky_post.png differ
diff --git a/html/user/img/unread_locked.png b/html/user/img/unread_locked.png
index e089bae..51a7b7d 100644
Binary files a/html/user/img/unread_locked.png and b/html/user/img/unread_locked.png differ
diff --git a/html/user/img/unread_post.png b/html/user/img/unread_post.png
index 103ef00..b0dbac5 100644
Binary files a/html/user/img/unread_post.png and b/html/user/img/unread_post.png differ
diff --git a/html/user/img/yahoo-button.png b/html/user/img/yahoo-button.png
index c6d4b98..0be94b2 100644
Binary files a/html/user/img/yahoo-button.png and b/html/user/img/yahoo-button.png differ
diff --git a/html/user/rss_main.php b/html/user/rss_main.php
index eaec0ec..e86fcf0 100644
--- a/html/user/rss_main.php
+++ b/html/user/rss_main.php
@@ -25,5 +25,5 @@ if (!$forum) {
exit;
}
-forum_rss($forum->id, 0, 0, 1, 90);
+forum_rss($forum->id, 0, 1, 90);
?>
diff --git a/html/user/show_user.php b/html/user/show_user.php
index 494f425..d51c6f2 100644
--- a/html/user/show_user.php
+++ b/html/user/show_user.php
@@ -55,9 +55,6 @@ if ($format=="xml"){
show_user_xml($user, $show_hosts);
} else {
- db_init(); // need to do this in any case,
- // since show_user_summary_public() etc. accesses DB
-
// The page may be presented in many different languages,
// so here we cache the data instead
//
diff --git a/lib/cc_config.cpp b/lib/cc_config.cpp
index e9d5e40..efe486a 100644
--- a/lib/cc_config.cpp
+++ b/lib/cc_config.cpp
@@ -652,7 +652,7 @@ int CC_CONFIG::write(MIOFILE& out, LOG_FLAGS& log_flags) {
" <save_stats_days>%d</save_stats_days>\n"
" <skip_cpu_benchmarks>%d</skip_cpu_benchmarks>\n"
" <simple_gui_only>%d</simple_gui_only>\n"
- " <start_delay>%d</start_delay>\n"
+ " <start_delay>%f</start_delay>\n"
" <stderr_head>%d</stderr_head>\n"
" <suppress_net_info>%d</suppress_net_info>\n"
" <unsigned_apps_ok>%d</unsigned_apps_ok>\n"
diff --git a/lib/common_defs.h b/lib/common_defs.h
index 01142c6..a835b1d 100644
--- a/lib/common_defs.h
+++ b/lib/common_defs.h
@@ -214,9 +214,10 @@ enum BATTERY_STATE {
// result and workunit records can be purged.
struct TIME_STATS {
-// we maintain an exponentially weighted average of these quantities:
double now;
- // the client's time of day
+ // the client's current time of day
+
+ // we maintain an exponentially weighted average of these quantities:
double on_frac;
// the fraction of total time this host runs the client
double connected_frac;
@@ -234,9 +235,27 @@ struct TIME_STATS {
// (as determined by preferences, manual suspend/resume, etc.)
double gpu_active_frac;
// same, GPU
+
+ // info for the current session (i.e. run of the client)
+ //
double client_start_time;
+ // start of current session
double previous_uptime;
// duration of previous session
+ double session_active_duration;
+ // time computation enabled
+ double session_gpu_active_duration;
+ // time GPU computation enabled
+
+ // info since the client was first run
+ //
+ double total_start_time;
+ double total_duration;
+ // time BOINC client has run
+ double total_active_duration;
+ // time computation allowed
+ double total_gpu_active_duration;
+ // time GPU computation allowed
void write(MIOFILE&);
int parse(XML_PARSER&);
diff --git a/lib/coproc.h b/lib/coproc.h
index 8157cdf..792b99f 100644
--- a/lib/coproc.h
+++ b/lib/coproc.h
@@ -165,7 +165,7 @@ struct COPROC {
//
int device_nums[MAX_COPROC_INSTANCES];
int device_num; // temp used in scan process
- bool have_opencls[MAX_COPROC_INSTANCES];
+ bool instance_has_opencl[MAX_COPROC_INSTANCES];
cl_device_id opencl_device_ids[MAX_COPROC_INSTANCES];
int opencl_device_count;
int opencl_device_indexes[MAX_COPROC_INSTANCES];
@@ -206,7 +206,7 @@ struct COPROC {
available_ram = 0;
for (int i=0; i<MAX_COPROC_INSTANCES; i++) {
device_nums[i] = 0;
- have_opencls[i] = false;
+ instance_has_opencl[i] = false;
opencl_device_ids[i] = 0;
opencl_device_indexes[i] = 0;
running_graphics_app[i] = true;
diff --git a/lib/filesys.cpp b/lib/filesys.cpp
index c387495..5f23ed3 100644
--- a/lib/filesys.cpp
+++ b/lib/filesys.cpp
@@ -765,10 +765,10 @@ void relative_to_absolute(const char* relname, char* path) {
}
}
-#if defined(_WIN32) && !(defined(WXDEBUG) || defined(WXNDEBUG))
+#if defined(_WIN32)
int boinc_allocate_file(const char* path, double size) {
int retval = 0;
- HANDLE h = CreateFile(
+ HANDLE h = CreateFileA(
path,
GENERIC_WRITE,
0,
@@ -797,36 +797,24 @@ int boinc_allocate_file(const char* path, double size) {
#ifdef _WIN32
int get_filesystem_info(double &total_space, double &free_space, char*) {
char cwd[MAXPATHLEN];
+ ULARGE_INTEGER TotalNumberOfFreeBytes;
+ ULARGE_INTEGER TotalNumberOfBytes;
+ ULARGE_INTEGER FreeBytesAvailable;
+ signed __int64 uMB;
+
boinc_getcwd(cwd);
- FreeFn pGetDiskFreeSpaceEx;
- pGetDiskFreeSpaceEx = (FreeFn)GetProcAddress(
- GetModuleHandleA("kernel32.dll"), "GetDiskFreeSpaceExA"
+ GetDiskFreeSpaceExA(
+ cwd,
+ &FreeBytesAvailable,
+ &TotalNumberOfBytes,
+ &TotalNumberOfFreeBytes
);
- if (pGetDiskFreeSpaceEx) {
- ULARGE_INTEGER TotalNumberOfFreeBytes;
- ULARGE_INTEGER TotalNumberOfBytes;
- ULARGE_INTEGER FreeBytesAvailable;
- pGetDiskFreeSpaceEx(
- cwd, &FreeBytesAvailable, &TotalNumberOfBytes,
- &TotalNumberOfFreeBytes
- );
- signed __int64 uMB;
- uMB = FreeBytesAvailable.QuadPart / (1024 * 1024);
- free_space = uMB * 1024.0 * 1024.0;
- uMB = TotalNumberOfBytes.QuadPart / (1024 * 1024);
- total_space = uMB * 1024.0 * 1024.0;
- } else {
- DWORD dwSectPerClust;
- DWORD dwBytesPerSect;
- DWORD dwFreeClusters;
- DWORD dwTotalClusters;
- GetDiskFreeSpaceA(
- cwd, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters,
- &dwTotalClusters
- );
- free_space = (double)dwFreeClusters * dwSectPerClust * dwBytesPerSect;
- total_space = (double)dwTotalClusters * dwSectPerClust * dwBytesPerSect;
- }
+
+ uMB = FreeBytesAvailable.QuadPart / (1024 * 1024);
+ free_space = uMB * 1024.0 * 1024.0;
+ uMB = TotalNumberOfBytes.QuadPart / (1024 * 1024);
+ total_space = uMB * 1024.0 * 1024.0;
+
#else
int get_filesystem_info(double &total_space, double &free_space, char* path) {
#ifdef STATFS
diff --git a/lib/gui_rpc_client.h b/lib/gui_rpc_client.h
index be0650f..65a70ce 100644
--- a/lib/gui_rpc_client.h
+++ b/lib/gui_rpc_client.h
@@ -168,6 +168,8 @@ struct PROJECT {
// when the last successful scheduler RPC finished
std::vector<DAILY_STATS> statistics; // credit data over the last x days
char venue[256];
+ int njobs_success;
+ int njobs_error;
// NOTE: if you add any data items above,
// update parse(), and clear() to include them!!
diff --git a/lib/gui_rpc_client_ops.cpp b/lib/gui_rpc_client_ops.cpp
index 6bf58ad..ec45bed 100644
--- a/lib/gui_rpc_client_ops.cpp
+++ b/lib/gui_rpc_client_ops.cpp
@@ -109,6 +109,12 @@ int TIME_STATS::parse(XML_PARSER& xp) {
if (xp.parse_double("gpu_active_frac", gpu_active_frac)) continue;
if (xp.parse_double("client_start_time", client_start_time)) continue;
if (xp.parse_double("previous_uptime", previous_uptime)) continue;
+ if (xp.parse_double("session_active_duration", session_active_duration)) continue;
+ if (xp.parse_double("session_gpu_active_duration", session_gpu_active_duration)) continue;
+ if (xp.parse_double("total_start_time", total_start_time)) continue;
+ if (xp.parse_double("total_duration", total_duration)) continue;
+ if (xp.parse_double("total_active_duration", total_active_duration)) continue;
+ if (xp.parse_double("total_gpu_active_duration", total_gpu_active_duration)) continue;
}
return ERR_XML_PARSE;
}
@@ -451,6 +457,8 @@ int PROJECT::parse(XML_PARSER& xp) {
if (xp.parse_double("project_files_downloaded_time", project_files_downloaded_time)) continue;
if (xp.parse_bool("no_ati_pref", rsc_desc_cpu.no_rsc_pref)) continue;
if (xp.parse_str("venue", venue, sizeof(venue))) continue;
+ if (xp.parse_int("njobs_success", njobs_success)) continue;
+ if (xp.parse_int("njobs_error", njobs_error)) continue;
}
return ERR_XML_PARSE;
}
@@ -502,6 +510,8 @@ void PROJECT::clear() {
gui_urls.clear();
statistics.clear();
strcpy(venue, "");
+ njobs_success = 0;
+ njobs_error = 0;
}
APP::APP() {
diff --git a/lib/gui_rpc_client_print.cpp b/lib/gui_rpc_client_print.cpp
index f1bd8e2..b2472a8 100644
--- a/lib/gui_rpc_client_print.cpp
+++ b/lib/gui_rpc_client_print.cpp
@@ -100,11 +100,14 @@ void PROJECT::print() {
printf(" suspended via GUI: %s\n", suspended_via_gui?"yes":"no");
printf(" don't request more work: %s\n", dont_request_more_work?"yes":"no");
printf(" disk usage: %f\n", disk_usage);
- printf(" last RPC: %f\n", last_rpc_time);
+ time_t foo = (time_t)last_rpc_time;
+ printf(" last RPC: %s\n", ctime(&foo));
printf(" project files downloaded: %f\n", project_files_downloaded_time);
for (i=0; i<gui_urls.size(); i++) {
gui_urls[i].print();
}
+ printf(" jobs succeeded: %d\n", njobs_success);
+ printf(" jobs failed: %d\n", njobs_error);
}
void APP::print() {
@@ -224,8 +227,16 @@ void TIME_STATS::print() {
printf(" cpu_and_network_available_frac: %f\n", cpu_and_network_available_frac);
printf(" active_frac: %f\n", active_frac);
printf(" gpu_active_frac: %f\n", gpu_active_frac);
- printf(" client_start_time: %f\n", client_start_time);
+ time_t foo = (time_t)client_start_time;
+ printf(" client_start_time: %s\n", ctime(&foo));
printf(" previous_uptime: %f\n", previous_uptime);
+ printf(" session_active_duration: %f\n", session_active_duration);
+ printf(" session_gpu_active_duration: %f\n", session_gpu_active_duration);
+ foo = (time_t)total_start_time;
+ printf(" total_start_time: %s\n", ctime(&foo));
+ printf(" total_duration: %f\n", total_duration);
+ printf(" total_active_duration: %f\n", total_active_duration);
+ printf(" total_gpu_active_duration: %f\n", total_gpu_active_duration);
}
void CC_STATE::print() {
diff --git a/lib/procinfo_unix.cpp b/lib/procinfo_unix.cpp
index de9a13f..21b528a 100644
--- a/lib/procinfo_unix.cpp
+++ b/lib/procinfo_unix.cpp
@@ -57,8 +57,7 @@
using std::vector;
// see:
-// man 5 proc
-// /usr/src/linux/fs/proc/array.C
+// http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/proc/array.c
//
// Interesting note: the command part of /proc/PID/stat is the first
// 15 characters of the executable filename.
@@ -163,16 +162,13 @@ int PROC_STAT::parse(char* buf) {
return 0;
}
- // I don't see a good choice of ERR_ for this...
- //
+ //fprintf(stderr, "can't parse /proc/x/stat file: %s\n", buf);
return 1;
}
// build table of all processes in system
//
int procinfo_setup(PROC_MAP& pm) {
-
-#if HAVE_DIRENT_H
DIR *dir;
dirent *piddir;
FILE* fd;
@@ -180,10 +176,13 @@ int procinfo_setup(PROC_MAP& pm) {
char pidpath[MAXPATHLEN];
char buf[1024];
int pid = getpid();
- int retval, final_retval = 0;
+ int retval;
dir = opendir("/proc");
- if (!dir) return 0;
+ if (!dir) {
+ fprintf(stderr, "procinfo_setup(): can't open /proc\n");
+ return 0;
+ }
while (1) {
piddir = readdir(dir);
@@ -232,7 +231,9 @@ int procinfo_setup(PROC_MAP& pm) {
fclose(fd);
if (retval) {
- final_retval = retval;
+ // ps.parse() returns an error if the executable name contains ).
+ // In that case skip this process.
+ //
continue;
}
PROCINFO p;
@@ -259,8 +260,6 @@ int procinfo_setup(PROC_MAP& pm) {
#endif
}
closedir(dir);
-#endif
find_children(pm);
- return final_retval;
-
+ return 0;
}
diff --git a/lib/shmem.cpp b/lib/shmem.cpp
index b04ab11..8d90468 100644
--- a/lib/shmem.cpp
+++ b/lib/shmem.cpp
@@ -418,6 +418,7 @@ int create_shmem(key_t key, int size, gid_t gid, void** pp) {
}
if (id < 0) {
perror("shmget");
+ fprintf(stderr,"shmem size: %d\n", size);
return ERR_SHMGET;
}
diff --git a/py/Boinc/database.py b/py/Boinc/database.py
index 277e158..1d791db 100644
--- a/py/Boinc/database.py
+++ b/py/Boinc/database.py
@@ -76,7 +76,8 @@ class AppVersion(DatabaseObject):
'pfc_avg',
'pfc_scale',
'expavg_credit',
- 'expavg_time'
+ 'expavg_time',
+ 'beta'
])
class User(DatabaseObject):
diff --git a/samples/vboxwrapper/vbox.cpp b/samples/vboxwrapper/vbox.cpp
index 4be35c9..9fa5745 100644
--- a/samples/vboxwrapper/vbox.cpp
+++ b/samples/vboxwrapper/vbox.cpp
@@ -66,6 +66,10 @@ VBOX_VM::VBOX_VM() {
virtualbox_version.clear();
pFloppy = NULL;
vm_log.clear();
+ vm_log_timestamp.hours = 0;
+ vm_log_timestamp.minutes = 0;
+ vm_log_timestamp.seconds = 0;
+ vm_log_timestamp.milliseconds = 0;
vm_master_name.clear();
vm_master_description.clear();
vm_name.clear();
@@ -77,6 +81,8 @@ VBOX_VM::VBOX_VM() {
image_filename.clear();
floppy_image_filename.clear();
job_duration = 0.0;
+ current_cpu_time = 0.0;
+ minimum_checkpoint_interval = 600.0;
fraction_done_filename.clear();
suspended = false;
network_suspended = false;
@@ -392,6 +398,11 @@ void VBOX_VM::poll(bool log_state) {
// functions.
//
get_vm_log(vm_log);
+
+ //
+ // Dump any new VM Guest Log entries
+ //
+ dumpvmguestlogentries();
}
int VBOX_VM::create_vm() {
@@ -1443,57 +1454,14 @@ int VBOX_VM::restoresnapshot() {
void VBOX_VM::dumphypervisorlogs(bool include_error_logs) {
string local_system_log;
string local_vm_log;
- string local_guest_log;
string local_trace_log;
- string prefiltered_guest_log;
- string filtered_guest_log;
- string line;
- string::iterator iter;
- size_t eol_pos;
- size_t eol_prev_pos;
unsigned long vm_exit_code = 0;
get_system_log(local_system_log);
get_vm_log(local_vm_log);
get_trace_log(local_trace_log);
- get_vm_log(prefiltered_guest_log, false);
get_vm_exit_code(vm_exit_code);
- // Filter the guest log messages which are generated by applications running within
- // the guest VM from other VM log messages.
- eol_prev_pos = 0;
- eol_pos = prefiltered_guest_log.find("\n");
- while (eol_pos != string::npos) {
- line = prefiltered_guest_log.substr(eol_prev_pos, eol_pos - eol_prev_pos);
-
- if (line.find("Guest Log:") != string::npos) {
- filtered_guest_log += line + "\n";
- }
-
- eol_prev_pos = eol_pos + 1;
- eol_pos = prefiltered_guest_log.find("\n", eol_prev_pos);
- }
-
- // Take the last 16k
- if (filtered_guest_log.size() >= 16384) {
- local_guest_log = filtered_guest_log.substr(filtered_guest_log.size() - 16384, 16384);
-
- // Look for the next whole line of text.
- iter = local_guest_log.begin();
- while (iter != local_guest_log.end()) {
- if (*iter == '\n') {
- local_guest_log.erase(iter);
- break;
- }
- iter = local_guest_log.erase(iter);
- }
-
- } else {
- local_guest_log = filtered_guest_log;
- }
-
- sanitize_output(local_guest_log);
-
if (include_error_logs) {
fprintf(
stderr,
@@ -1510,14 +1478,6 @@ void VBOX_VM::dumphypervisorlogs(bool include_error_logs) {
);
}
- fprintf(
- stderr,
- "\n"
- " VM Guest Log:\n\n"
- "%s",
- local_guest_log.c_str()
- );
-
if (vm_exit_code) {
fprintf(
stderr,
@@ -1568,6 +1528,61 @@ void VBOX_VM::dumphypervisorstatusreports() {
#endif
}
+// t1 > t2
+static bool is_timestamp_newer(VBOX_TIMESTAMP& t1, VBOX_TIMESTAMP& t2) {
+ if (t1.hours > t2.hours) return true;
+ if (t1.hours < t2.hours) return false;
+ if (t1.minutes > t2.minutes) return true;
+ if (t1.minutes < t2.minutes) return false;
+ if (t1.seconds > t2.seconds) return true;
+ if (t1.seconds < t2.seconds) return false;
+ if (t1.milliseconds > t2.milliseconds) return true;
+ return false;
+}
+
+// Dump any new guest log messages which are generated by applications running within
+// the guest VM.
+void VBOX_VM::dumpvmguestlogentries() {
+ string line;
+ size_t eol_pos;
+ size_t eol_prev_pos;
+ size_t line_pos;
+ VBOX_TIMESTAMP current_timestamp;
+ string msg;
+ char buf[256];
+
+ eol_prev_pos = 0;
+ eol_pos = vm_log.find("\n");
+ while (eol_pos != string::npos) {
+ line = vm_log.substr(eol_prev_pos, eol_pos - eol_prev_pos);
+
+ line_pos = line.find("Guest Log:");
+ if (line_pos != string::npos) {
+ sscanf(
+ line.c_str(),
+ "%d:%d:%d.%d",
+ ¤t_timestamp.hours, ¤t_timestamp.minutes,
+ ¤t_timestamp.seconds, ¤t_timestamp.milliseconds
+ );
+
+ if (is_timestamp_newer(current_timestamp, vm_log_timestamp)) {
+ vm_log_timestamp = current_timestamp;
+ msg = line.substr(line_pos, line.size() - line_pos);
+
+ fprintf(
+ stderr,
+ "%s %s\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ msg.c_str()
+ );
+ }
+ }
+
+ eol_prev_pos = eol_pos + 1;
+ eol_pos = vm_log.find("\n", eol_prev_pos);
+ }
+}
+
int VBOX_VM::is_registered() {
string command;
string output;
@@ -1966,6 +1981,14 @@ int VBOX_VM::get_vm_exit_code(unsigned long& exit_code) {
return 0;
}
+double VBOX_VM::get_vm_cpu_time() {
+ double x = process_tree_cpu_time(vm_pid);
+ if (x > current_cpu_time) {
+ current_cpu_time = x;
+ }
+ return current_cpu_time;
+}
+
int VBOX_VM::get_port_forwarding_port() {
sockaddr_in addr;
BOINC_SOCKLEN_T addrsize;
diff --git a/samples/vboxwrapper/vbox.h b/samples/vboxwrapper/vbox.h
index 7229b7a..b856ab9 100644
--- a/samples/vboxwrapper/vbox.h
+++ b/samples/vboxwrapper/vbox.h
@@ -46,8 +46,18 @@
// raw floppy drive device
class FloppyIO;
+// represents a VirtualBox Guest Log Timestamp
+class VBOX_TIMESTAMP {
+public:
+ int hours;
+ int minutes;
+ int seconds;
+ int milliseconds;
+};
+
// represents a VirtualBox VM
-struct VBOX_VM {
+class VBOX_VM {
+public:
VBOX_VM();
~VBOX_VM();
@@ -63,6 +73,8 @@ struct VBOX_VM {
// last polled copy of the log file
std::string vm_log;
+ // last VM guest log entry detected
+ VBOX_TIMESTAMP vm_log_timestamp;
// unique name for the VM
std::string vm_master_name;
// unique description for the VM
@@ -86,6 +98,10 @@ struct VBOX_VM {
// maximum amount of wall-clock time this VM is allowed to run before
// considering itself done.
double job_duration;
+ // amount of CPU time consumed by the VM (note: use get_vm_cpu_time())
+ double current_cpu_time;
+ // minimum amount of time between checkpoints
+ double minimum_checkpoint_interval;
// name of file where app will write its fraction done
std::string fraction_done_filename;
// is the VM suspended?
@@ -158,6 +174,7 @@ struct VBOX_VM {
int restoresnapshot();
void dumphypervisorlogs(bool include_error_logs);
void dumphypervisorstatusreports();
+ void dumpvmguestlogentries();
int is_registered();
bool is_system_ready(std::string& message);
@@ -180,6 +197,7 @@ struct VBOX_VM {
int get_vm_network_bytes_received(double& received);
int get_vm_process_id();
int get_vm_exit_code(unsigned long& exit_code);
+ double get_vm_cpu_time();
int get_system_log(std::string& log, bool tail_only = true);
int get_vm_log(std::string& log, bool tail_only = true);
diff --git a/samples/vboxwrapper/vboxwrapper.cpp b/samples/vboxwrapper/vboxwrapper.cpp
index 6d9aa98..224b7ab 100644
--- a/samples/vboxwrapper/vboxwrapper.cpp
+++ b/samples/vboxwrapper/vboxwrapper.cpp
@@ -59,6 +59,7 @@
#include <unistd.h>
#endif
+#include "version.h"
#include "boinc_api.h"
#include "diagnostics.h"
#include "filesys.h"
@@ -157,6 +158,7 @@ int parse_job_file(VBOX_VM& vm, vector<string>& copy_to_shared) {
else if (xp.parse_string("os_name", vm.os_name)) continue;
else if (xp.parse_string("memory_size_mb", vm.memory_size_mb)) continue;
else if (xp.parse_double("job_duration", vm.job_duration)) continue;
+ else if (xp.parse_double("minimum_checkpoint_interval", vm.minimum_checkpoint_interval)) continue;
else if (xp.parse_string("fraction_done_filename", vm.fraction_done_filename)) continue;
else if (xp.parse_bool("enable_cern_dataformat", vm.enable_cern_dataformat)) continue;
else if (xp.parse_bool("enable_network", vm.enable_network)) continue;
@@ -177,25 +179,28 @@ int parse_job_file(VBOX_VM& vm, vector<string>& copy_to_shared) {
return ERR_XML_PARSE;
}
-void write_checkpoint(double cpu, VBOX_VM& vm) {
+void write_checkpoint(double elapsed, double cpu, VBOX_VM& vm) {
FILE* f = fopen(CHECKPOINT_FILENAME, "w");
if (!f) return;
- fprintf(f, "%f %d %d\n", cpu, vm.pf_host_port, vm.rd_host_port);
+ fprintf(f, "%f %f %d %d\n", elapsed, cpu, vm.pf_host_port, vm.rd_host_port);
fclose(f);
}
-void read_checkpoint(double& cpu, VBOX_VM& vm) {
+void read_checkpoint(double& elapsed, double& cpu, VBOX_VM& vm) {
double c;
+ double e;
int pf_host;
int rd_host;
+ elapsed = 0.0;
cpu = 0.0;
vm.pf_host_port = 0;
vm.rd_host_port = 0;
FILE* f = fopen(CHECKPOINT_FILENAME, "r");
if (!f) return;
- int n = fscanf(f, "%lf %d %d", &c, &pf_host, &rd_host);
+ int n = fscanf(f, "%lf %lf %d %d", &e, &c, &pf_host, &rd_host);
fclose(f);
- if (n != 3) return;
+ if (n != 4) return;
+ elapsed = e;
cpu = c;
vm.pf_host_port = pf_host;
vm.rd_host_port = rd_host;
@@ -263,10 +268,10 @@ void set_throttles(APP_INIT_DATA& aid, VBOX_VM& vm) {
}
// If the Floppy device has been specified, initialize its state so that
-// it contains the contents of the init_data.xml file. In theory this
-// would allow network enabled VMs to know about proxy server configurations
-// either specified by the volunteer or automatically detected by the
-// core client.
+// it contains the contents of the init_data.xml file.
+// In theory this would allow network enabled VMs to know about
+// proxy server configurations either specified by the volunteer
+// or automatically detected by the client.
//
// CERN decided they only needed a small subset of the data and changed the
// data format to 'name=value\n' pairs. So if we are running under their
@@ -369,6 +374,7 @@ void set_remote_desktop_info(APP_INIT_DATA& /* aid */, VBOX_VM& vm) {
int main(int argc, char** argv) {
int retval;
+ int loop_iteraction = 0;
BOINC_OPTIONS boinc_options;
VBOX_VM vm;
APP_INIT_DATA aid;
@@ -376,7 +382,9 @@ int main(int argc, char** argv) {
double elapsed_time = 0;
double trickle_period = 0;
double fraction_done = 0;
- double checkpoint_cpu_time = 0;
+ double current_cpu_time = 0;
+ double starting_cpu_time = 0;
+ double last_checkpoint_time = 0;
double last_status_report_time = 0;
double last_trickle_report_time = 0;
double stopwatch_starttime = 0;
@@ -428,9 +436,11 @@ int main(int argc, char** argv) {
//
fprintf(
stderr,
- "%s %s: starting\n",
+ "%s vboxwrapper (%d.%d.%d): starting\n",
vboxwrapper_msg_prefix(buf, sizeof(buf)),
- argv[0]
+ BOINC_MAJOR_VERSION,
+ BOINC_MINOR_VERSION,
+ VBOXWRAPPER_RELEASE
);
// Log important information
@@ -536,7 +546,7 @@ int main(int argc, char** argv) {
// Record which mode VirtualBox should be started in.
//
- if (aid.vbox_window) {
+ if (aid.vbox_window || boinc_is_standalone()) {
fprintf(
stderr,
"%s Detected: Headless Mode Disabled\n",
@@ -556,12 +566,12 @@ int main(int argc, char** argv) {
buf,
buf
);
- boinc_temporary_exit(86400, "Incompatible confgiuration detected.");
+ boinc_temporary_exit(86400, "Incompatible configuration detected.");
}
// Check against known incompatible versions of VirtualBox.
- // NOTE: Incompatible in this case means that VirtualBox 4.2.6 crashes during snapshot operations
- // and 4.2.18 fails to restore from snapshots properly.
+ // VirtualBox 4.2.6 crashes during snapshot operations
+ // and 4.2.18 fails to restore from snapshots properly.
//
if ((vm.virtualbox_version.find("4.2.6") != std::string::npos) ||
(vm.virtualbox_version.find("4.2.18") != std::string::npos) ||
@@ -571,7 +581,10 @@ int main(int argc, char** argv) {
"%s Incompatible version of VirtualBox detected. Please upgrade to a later version.\n",
vboxwrapper_msg_prefix(buf, sizeof(buf))
);
- boinc_temporary_exit(86400, "Incompatible version of VirtualBox detected.");
+ boinc_temporary_exit(86400,
+ "Incompatible version of VirtualBox detected; please upgrade.",
+ true
+ );
}
// Check to see if the system is in a state in which we expect to be able to run
@@ -600,6 +613,15 @@ int main(int argc, char** argv) {
boinc_finish(retval);
}
+ // Record which mode VirtualBox should be started in.
+ //
+ fprintf(
+ stderr,
+ "%s Detected: Minimum checkpoint interval (%f seconds)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ vm.minimum_checkpoint_interval
+ );
+
// Validate whatever configuration options we can
//
if (vm.enable_shared_directory) {
@@ -694,8 +716,9 @@ int main(int argc, char** argv) {
// Restore from checkpoint
//
- read_checkpoint(checkpoint_cpu_time, vm);
- elapsed_time = checkpoint_cpu_time;
+ read_checkpoint(elapsed_time, current_cpu_time, vm);
+ starting_cpu_time = current_cpu_time;
+ last_checkpoint_time = current_cpu_time;
// Should we even try to start things up?
//
@@ -703,7 +726,7 @@ int main(int argc, char** argv) {
return EXIT_TIME_LIMIT_EXCEEDED;
}
- retval = vm.run((elapsed_time > 0));
+ retval = vm.run((current_cpu_time > 0));
if (retval) {
// All 'failure to start' errors are unrecoverable by default
bool unrecoverable_error = true;
@@ -781,7 +804,7 @@ int main(int argc, char** argv) {
if (!skip_cleanup) {
vm.cleanup();
}
- write_checkpoint(elapsed_time, vm);
+ write_checkpoint(elapsed_time, current_cpu_time, vm);
if (error_reason.size()) {
fprintf(
@@ -804,8 +827,8 @@ int main(int argc, char** argv) {
//
if (vm.vm_pid) {
retval = boinc_report_app_status_aux(
- elapsed_time,
- checkpoint_cpu_time,
+ current_cpu_time,
+ last_checkpoint_time,
fraction_done,
vm.vm_pid,
bytes_sent,
@@ -842,8 +865,8 @@ int main(int argc, char** argv) {
buf
);
retval = boinc_report_app_status_aux(
- elapsed_time,
- checkpoint_cpu_time,
+ current_cpu_time,
+ last_checkpoint_time,
fraction_done,
vm.vm_pid,
bytes_sent,
@@ -890,7 +913,7 @@ int main(int argc, char** argv) {
set_floppy_image(aid, vm);
set_port_forwarding_info(aid, vm);
set_remote_desktop_info(aid, vm);
- write_checkpoint(elapsed_time, vm);
+ write_checkpoint(elapsed_time, current_cpu_time, vm);
// Force throttling on our first pass through the loop
boinc_status.reread_init_data_file = true;
@@ -898,6 +921,7 @@ int main(int argc, char** argv) {
while (1) {
// Begin stopwatch timer
stopwatch_starttime = dtime();
+ loop_iteraction += 1;
// Discover the VM's current state
vm.poll();
@@ -994,65 +1018,78 @@ int main(int argc, char** argv) {
}
}
+ // Basic bookkeeping
+ //
+ if ((loop_iteraction % 10) == 0) {
+ current_cpu_time = starting_cpu_time + vm.get_vm_cpu_time();
+ }
+ if (vm.job_duration) {
+ fraction_done = elapsed_time / vm.job_duration;
+ } else if (vm.fraction_done_filename.size() > 0) {
+ read_fraction_done(fraction_done, vm);
+ }
+ if (fraction_done > 1.0) {
+ fraction_done = 1.0;
+ }
+ boinc_report_app_status(
+ current_cpu_time,
+ last_checkpoint_time,
+ fraction_done
+ );
+
+ // Dump a status report at regular intervals
+ //
+ if ((elapsed_time - last_status_report_time) >= 6000.0) {
+ last_status_report_time = elapsed_time;
+ if (vm.job_duration) {
+ fprintf(
+ stderr,
+ "%s Status Report: Job Duration: '%f'\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ vm.job_duration
+ );
+ }
+ if (elapsed_time) {
+ fprintf(
+ stderr,
+ "%s Status Report: Elapsed Time: '%f'\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ elapsed_time
+ );
+ }
+ fprintf(
+ stderr,
+ "%s Status Report: CPU Time: '%f'\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ current_cpu_time
+ );
+ if (aid.global_prefs.daily_xfer_limit_mb) {
+ fprintf(
+ stderr,
+ "%s Status Report: Network Bytes Sent (Total): '%f'\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ bytes_sent
+ );
+ fprintf(
+ stderr,
+ "%s Status Report: Network Bytes Received (Total): '%f'\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ bytes_received
+ );
+ }
+
+ vm.dumphypervisorstatusreports();
+ }
+
if (boinc_time_to_checkpoint()) {
// Only peform a VM checkpoint every ten minutes or so.
//
- if (elapsed_time >= checkpoint_cpu_time + random_checkpoint_factor + 600.0) {
+ if (elapsed_time >= last_checkpoint_time + vm.minimum_checkpoint_interval + random_checkpoint_factor) {
// Basic interleave factor is only needed once.
if (random_checkpoint_factor > 0) {
random_checkpoint_factor = 0.0;
}
- // Basic bookkeeping
- if (vm.job_duration) {
- fraction_done = elapsed_time / vm.job_duration;
- } else if (vm.fraction_done_filename.size() > 0) {
- read_fraction_done(fraction_done, vm);
- }
- if (fraction_done > 1.0) {
- fraction_done = 1.0;
- }
-
- if ((elapsed_time - last_status_report_time) >= 6000.0) {
- last_status_report_time = elapsed_time;
- if (vm.job_duration) {
- fprintf(
- stderr,
- "%s Status Report: Job Duration: '%f'\n",
- vboxwrapper_msg_prefix(buf, sizeof(buf)),
- vm.job_duration
- );
- }
- if (elapsed_time) {
- fprintf(
- stderr,
- "%s Status Report: Elapsed Time: '%f'\n",
- vboxwrapper_msg_prefix(buf, sizeof(buf)),
- elapsed_time
- );
- }
- if (aid.global_prefs.daily_xfer_limit_mb) {
- if (vm.job_duration) {
- fprintf(
- stderr,
- "%s Status Report: Network Bytes Sent (Total): '%f'\n",
- vboxwrapper_msg_prefix(buf, sizeof(buf)),
- bytes_sent
- );
- }
- if (elapsed_time) {
- fprintf(
- stderr,
- "%s Status Report: Network Bytes Received (Total): '%f'\n",
- vboxwrapper_msg_prefix(buf, sizeof(buf)),
- bytes_received
- );
- }
- }
-
- vm.dumphypervisorstatusreports();
- }
-
// Checkpoint
retval = vm.createsnapshot(elapsed_time);
if (retval) {
@@ -1070,13 +1107,8 @@ int main(int argc, char** argv) {
} else {
// tell BOINC we've successfully created a checkpoint.
//
- checkpoint_cpu_time = elapsed_time;
- write_checkpoint(checkpoint_cpu_time, vm);
- boinc_report_app_status(
- elapsed_time,
- checkpoint_cpu_time,
- fraction_done
- );
+ last_checkpoint_time = elapsed_time;
+ write_checkpoint(elapsed_time, current_cpu_time, vm);
boinc_checkpoint_completed();
}
}
@@ -1084,12 +1116,12 @@ int main(int argc, char** argv) {
if (trickle_period) {
if ((elapsed_time - last_trickle_report_time) >= trickle_period) {
+ last_trickle_report_time = elapsed_time;
fprintf(
stderr,
"%s Status Report: Trickle-Up Event.\n",
vboxwrapper_msg_prefix(buf, sizeof(buf))
);
- last_trickle_report_time = elapsed_time;
sprintf(buf,
"<cpu_time>%f</cpu_time>", last_trickle_report_time
);
@@ -1119,6 +1151,13 @@ int main(int argc, char** argv) {
boinc_parse_init_data_file();
boinc_get_init_data_p(&aid);
set_throttles(aid, vm);
+
+ fprintf(
+ stderr,
+ "%s Checkpoint Interval is now %d seconds.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ (int)aid.checkpoint_period
+ );
}
// if the VM has a maximum amount of time it is allowed to run,
@@ -1180,7 +1219,7 @@ int main(int argc, char** argv) {
if (report_net_usage) {
retval = boinc_report_app_status_aux(
elapsed_time,
- checkpoint_cpu_time,
+ last_checkpoint_time,
fraction_done,
vm.vm_pid,
bytes_sent,
diff --git a/samples/vboxwrapper/vboxwrapper_win.h b/samples/vboxwrapper/vboxwrapper_win.h
new file mode 100644
index 0000000..751fad7
--- /dev/null
+++ b/samples/vboxwrapper/vboxwrapper_win.h
@@ -0,0 +1,16 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by boinc_ss.rc
+//
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_3D_CONTROLS 1
+#define _APS_NEXT_RESOURCE_VALUE 109
+#define _APS_NEXT_COMMAND_VALUE 40000
+#define _APS_NEXT_CONTROL_VALUE 1010
+#define _APS_NEXT_SYMED_VALUE 102
+#endif
+#endif
diff --git a/samples/vboxwrapper/vboxwrapper_win.rc b/samples/vboxwrapper/vboxwrapper_win.rc
new file mode 100644
index 0000000..00370fb
--- /dev/null
+++ b/samples/vboxwrapper/vboxwrapper_win.rc
@@ -0,0 +1,79 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "vboxwrapper_win.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winresrc.h"
+#include "version.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Neutral resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU)
+#ifdef _WIN32
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION BOINC_MAJOR_VERSION,BOINC_MINOR_VERSION,VBOXWRAPPER_RELEASE,0
+ PRODUCTVERSION BOINC_MAJOR_VERSION,BOINC_MINOR_VERSION,VBOXWRAPPER_RELEASE,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Space Sciences Laboratory"
+ VALUE "FileDescription", "BOINC VirtualBox Wrapper"
+ VALUE "FileVersion", BOINC_MAJOR_VERSION "." BOINC_MINOR_VERSION "." VBOXWRAPPER_RELEASE "\0"
+ VALUE "InternalName", "vboxwrapper"
+ VALUE "LegalCopyright", "� 2011-2014 University of California"
+ VALUE "OriginalFilename", "vboxwrapper.exe"
+ VALUE "ProductName", "BOINC VirtualBox Wrapper"
+ VALUE "ProductVersion", BOINC_MAJOR_VERSION "." BOINC_MINOR_VERSION "." VBOXWRAPPER_RELEASE "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+#endif
diff --git a/samples/wrapper/wrapper.cpp b/samples/wrapper/wrapper.cpp
index f80a042..42ff04c 100644
--- a/samples/wrapper/wrapper.cpp
+++ b/samples/wrapper/wrapper.cpp
@@ -21,7 +21,7 @@
// Handles:
// - suspend/resume/quit/abort
// - reporting CPU time
-// - loss of heartbeat from core client
+// - loss of heartbeat from client
// - checkpointing
// (at the level of task; or potentially within task)
//
@@ -52,6 +52,7 @@
#include <unistd.h>
#endif
+#include "version.h"
#include "boinc_api.h"
#include "boinc_zip.h"
#include "diagnostics.h"
@@ -83,6 +84,7 @@ inline void debug_msg(const char* x) {
using std::vector;
using std::string;
int nthreads = 1;
+int gpu_device_num = -1;
struct TASK {
string application;
@@ -232,6 +234,7 @@ void str_replace_all(char* buf, const char* s1, const char* s2) {
// macro-substitute strings from job.xml
// $PROJECT_DIR -> project directory
// $NTHREADS --> --nthreads arg if present, else 1
+// $GPU_DEVICE_NUM --> gpu_device_num from init_data.xml, or --device arg
//
void macro_substitute(char* buf) {
const char* pd = strlen(aid.project_dir)?aid.project_dir:".";
@@ -239,6 +242,14 @@ void macro_substitute(char* buf) {
char nt[256];
sprintf(nt, "%d", nthreads);
str_replace_all(buf, "$NTHREADS", nt);
+
+ if (aid.gpu_device_num >= 0) {
+ gpu_device_num = aid.gpu_device_num;
+ }
+ if (gpu_device_num >= 0) {
+ sprintf(nt, "%d", gpu_device_num);
+ str_replace_all(buf, "$GPU_DEVICE_NUM", nt);
+ }
}
// make a list of files in the slot directory,
@@ -958,6 +969,8 @@ int main(int argc, char** argv) {
for (int j=1; j<argc; j++) {
if (!strcmp(argv[j], "--nthreads")) {
nthreads = atoi(argv[++j]);
+ } else if (!strcmp(argv[j], "--device")) {
+ gpu_device_num = atoi(argv[++j]);
}
}
@@ -993,8 +1006,11 @@ int main(int argc, char** argv) {
boinc_init_options(&options);
fprintf(stderr,
- "%s wrapper: starting\n",
- boinc_msg_prefix(buf, sizeof(buf))
+ "%s wrapper (%d.%d.%d): starting\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ BOINC_MAJOR_VERSION,
+ BOINC_MINOR_VERSION,
+ WRAPPER_RELEASE
);
boinc_get_init_data(aid);
diff --git a/samples/wrapper/wrapper_win.h b/samples/wrapper/wrapper_win.h
new file mode 100644
index 0000000..751fad7
--- /dev/null
+++ b/samples/wrapper/wrapper_win.h
@@ -0,0 +1,16 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by boinc_ss.rc
+//
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_3D_CONTROLS 1
+#define _APS_NEXT_RESOURCE_VALUE 109
+#define _APS_NEXT_COMMAND_VALUE 40000
+#define _APS_NEXT_CONTROL_VALUE 1010
+#define _APS_NEXT_SYMED_VALUE 102
+#endif
+#endif
diff --git a/samples/wrapper/wrapper_win.rc b/samples/wrapper/wrapper_win.rc
new file mode 100644
index 0000000..c2cc384
--- /dev/null
+++ b/samples/wrapper/wrapper_win.rc
@@ -0,0 +1,79 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "wrapper_win.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winresrc.h"
+#include "version.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Neutral resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU)
+#ifdef _WIN32
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION BOINC_MAJOR_VERSION,BOINC_MINOR_VERSION,WRAPPER_RELEASE,0
+ PRODUCTVERSION BOINC_MAJOR_VERSION,BOINC_MINOR_VERSION,WRAPPER_RELEASE,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Space Sciences Laboratory"
+ VALUE "FileDescription", "BOINC Wrapper"
+ VALUE "FileVersion", BOINC_MAJOR_VERSION "." BOINC_MINOR_VERSION "." WRAPPER_RELEASE "\0"
+ VALUE "InternalName", "vboxwrapper"
+ VALUE "LegalCopyright", "� 2011-2014 University of California"
+ VALUE "OriginalFilename", "wrapper.exe"
+ VALUE "ProductName", "BOINC Wrapper"
+ VALUE "ProductVersion", BOINC_MAJOR_VERSION "." BOINC_MINOR_VERSION "." WRAPPER_RELEASE "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+#endif
diff --git a/sched/Makefile.am b/sched/Makefile.am
index 020994b..0142a32 100644
--- a/sched/Makefile.am
+++ b/sched/Makefile.am
@@ -142,6 +142,7 @@ noinst_HEADERS = \
plan_class_spec.h \
sched_main.h \
sched_locality.h \
+ sched_nci.h \
sched_score.h \
sched_send.h \
sched_shmem.h \
@@ -167,6 +168,7 @@ cgi_sources = \
sched_limit.cpp \
sched_locality.cpp \
sched_main.cpp \
+ sched_nci.cpp \
sched_resend.cpp \
sched_result.cpp \
sched_score.cpp \
diff --git a/sched/db_dump.cpp b/sched/db_dump.cpp
index 7de2440..9eca767 100644
--- a/sched/db_dump.cpp
+++ b/sched/db_dump.cpp
@@ -18,9 +18,12 @@
/// db_dump: dump database views in XML format
// see http://boinc.berkeley.edu/trac/wiki/DbDump
-// Note: this program is way more configurable than it needs to be.
-// All projects export stats in the same format,
-// as described in the default db_dump_spec.xml that is created for you.
+// Note:
+// 1) this program is way more configurable than it needs to be.
+// All projects export stats in the same format,
+// as described in the default db_dump_spec.xml that is created for you.
+// 2) should scrap this and replace it with a 100 line PHP script.
+// I'll get to this someday.
#include "config.h"
#include <cstdio>
@@ -70,6 +73,7 @@ const char* tag_name[3] = {"users", "teams", "hosts"};
int nusers, nhosts, nteams;
double total_credit;
+bool have_badges = false;
struct OUTPUT {
int recs_per_file;
@@ -462,6 +466,48 @@ void write_user(USER& user, FILE* f, bool /*detail*/) {
);
}
+void write_badge_user(char* output_dir) {
+ DB_BADGE_USER bu;
+ char path[MAXPATHLEN];
+ ZFILE* f = new ZFILE("badge_users", COMPRESSION_GZIP);
+ sprintf(path, "%s/badge_user", output_dir);
+ f->open(path);
+ while (!bu.enumerate("")) {
+ fprintf(f->f,
+ " <badge_user>\n"
+ " <user_id>%d</user_id>\n"
+ " <badge_id>%d</badge_id>\n"
+ " <create_time>%.0f</create_time>\n"
+ " </badge_user>\n",
+ bu.user_id,
+ bu.badge_id,
+ bu.create_time
+ );
+ }
+ f->close();
+}
+
+void write_badge_team(char* output_dir) {
+ DB_BADGE_TEAM bt;
+ char path[MAXPATHLEN];
+ ZFILE* f = new ZFILE("badge_teams", COMPRESSION_GZIP);
+ sprintf(path, "%s/badge_team", output_dir);
+ f->open(path);
+ while (!bt.enumerate("")) {
+ fprintf(f->f,
+ " <badge_team>\n"
+ " <team_id>%d</team_id>\n"
+ " <badge_id>%d</badge_id>\n"
+ " <create_time>%.0f</create_time>\n"
+ " </badge_team>\n",
+ bt.team_id,
+ bt.badge_id,
+ bt.create_time
+ );
+ }
+ f->close();
+}
+
void write_team(TEAM& team, FILE* f, bool detail) {
DB_USER user;
char buf[256];
@@ -595,6 +641,27 @@ int print_apps(FILE* f) {
return 0;
}
+void print_badges(FILE* f) {
+ DB_BADGE badge;
+ fprintf(f, " <badges>\n");
+ while (!badge.enumerate()) {
+ have_badges = true;
+ fprintf(f,
+ " <badge>\n"
+ " <id>%d</id>\n"
+ " <name>%s</name>\n"
+ " <title>%s</title>\n"
+ " <image_url>%s</image_url>\n"
+ " </badge>\n",
+ badge.id,
+ badge.name,
+ badge.title,
+ badge.image_url
+ );
+ }
+ fprintf(f, " </badges>\n");
+}
+
int tables_file(char* dir) {
char buf[256];
@@ -610,6 +677,7 @@ int tables_file(char* dir) {
if (nhosts) fprintf(f.f, " <nhosts_total>%d</nhosts_total>\n", nhosts);
if (total_credit) fprintf(f.f, " <total_credit>%lf</total_credit>\n", total_credit);
print_apps(f.f);
+ print_badges(f.f);
f.close();
return 0;
}
@@ -747,6 +815,7 @@ void usage(char* name) {
" --dump_spec filename Use the given config file (use ../db_dump_spec.xml)\n"
" [-d N | --debug_level] Set verbosity level (1 to 4)\n"
" [--db_host H] Use the DB server on host H\n"
+ " [--retry_period H] When can't connect to DB, retry after N sec instead of terminating\n"
" [-h | --help] Show this\n"
" [-v | --version] Show version information\n",
name
@@ -759,6 +828,7 @@ int main(int argc, char** argv) {
char* db_host = 0;
char spec_filename[256], buf[256];
FILE_LOCK file_lock;
+ int retry_period = 0;
check_stop_daemons();
setbuf(stderr, 0);
@@ -773,6 +843,13 @@ int main(int argc, char** argv) {
exit(1);
}
safe_strcpy(spec_filename, argv[i]);
+ } else if (is_arg(argv[i], "retry_period")) {
+ if (!argv[++i]) {
+ log_messages.printf(MSG_CRITICAL, "%s requires an argument\n\n", argv[--i]);
+ usage(argv[0]);
+ exit(1);
+ }
+ retry_period = atoi(argv[i]);
} else if (is_arg(argv[i], "d") || is_arg(argv[i], "debug_level")) {
if (!argv[++i]) {
log_messages.printf(MSG_CRITICAL, "%s requires an argument\n\n", argv[--i]);
@@ -837,15 +914,25 @@ int main(int argc, char** argv) {
);
exit(1);
}
- retval = boinc_db.open(
+
+ retval = boinc_mkdir(spec.output_dir);
+ if (retval) {
+ log_messages.printf(MSG_CRITICAL,
+ "boinc_mkdir(%s): %s; %s\n",
+ spec.output_dir, boincerror(retval), boinc_db.error_string()
+ );
+ exit(1);
+ }
+
+ while ((retval = boinc_db.open(
config.replica_db_name,
db_host?db_host:config.replica_db_host,
config.replica_db_user,
config.replica_db_passwd
- );
- if (retval) {
- log_messages.printf(MSG_CRITICAL, "Can't open DB\n");
- exit(1);
+ ))) {
+ log_messages.printf(MSG_CRITICAL, "Can't open DB: %d\n", retval);
+ if (retry_period == 0) exit(1);
+ boinc_sleep(retry_period);
}
retval = boinc_db.set_isolation_level(READ_UNCOMMITTED);
if (retval) {
@@ -857,13 +944,18 @@ int main(int argc, char** argv) {
boinc_mkdir(spec.output_dir);
+ tables_file(spec.output_dir);
+
unsigned int j;
for (j=0; j<spec.enumerations.size(); j++) {
ENUMERATION& e = spec.enumerations[j];
e.make_it_happen(spec.output_dir);
}
- tables_file(spec.output_dir);
+ if (have_badges) {
+ write_badge_user(spec.output_dir);
+ write_badge_team(spec.output_dir);
+ }
sprintf(buf, "cp %s %s/db_dump.xml", spec_filename, spec.output_dir);
retval = system(buf);
diff --git a/sched/db_purge.cpp b/sched/db_purge.cpp
index 3ef5173..9a443aa 100644
--- a/sched/db_purge.cpp
+++ b/sched/db_purge.cpp
@@ -309,6 +309,7 @@ int archive_result(DB_RESULT& result) {
" <opaque>%f</opaque>\n"
" <random>%d</random>\n"
" <app_version_num>%d</app_version_num>\n"
+ " <app_version_id>%d</app_version_id>\n"
" <appid>%d</appid>\n"
" <exit_status>%d</exit_status>\n"
" <teamid>%d</teamid>\n"
@@ -337,6 +338,7 @@ int archive_result(DB_RESULT& result) {
result.opaque,
result.random,
result.app_version_num,
+ result.app_version_id,
result.appid,
result.exit_status,
result.teamid,
diff --git a/sched/feeder.cpp b/sched/feeder.cpp
index 2bd61c2..72bba21 100644
--- a/sched/feeder.cpp
+++ b/sched/feeder.cpp
@@ -23,6 +23,7 @@
// [ --allapps ] interleave results from all applications uniformly
// [ --by_batch ] interleave results from all batches uniformly
// [ --random_order ] order by "random" field of result
+// [ --random_order_db ] randomize order with SQL rand(sysdate())
// [ --priority_order ] order by decreasing "priority" field of result
// [ --priority_asc ] order by increasing "priority" field of result
// [ --priority_order_create_time ]
@@ -706,6 +707,7 @@ void usage(char *name) {
" [ -d X | --debug_level X] Set log verbosity to X (1..4)\n"
" [ --allapps ] Interleave results from all applications uniformly.\n"
" [ --random_order ] order by \"random\" field of result\n"
+ " [ --random_order_db ] randomize order with SQL rand(sysdate())\n"
" [ --priority_asc ] order by increasing \"priority\" field of result\n"
" [ --priority_order ] order by decreasing \"priority\" field of result\n"
" [ --priority_order_create_time ] order by priority, then by increasing WU create time\n"
@@ -739,6 +741,8 @@ int main(int argc, char** argv) {
if (dl == 4) g_print_queries = true;
} else if (is_arg(argv[i], "random_order")) {
order_clause = "order by r1.random ";
+ } else if (is_arg(argv[i], "random_order_db")) {
+ order_clause = "order by rand(sysdate()) ";
} else if (is_arg(argv[i], "allapps")) {
all_apps = true;
} else if (is_arg(argv[i], "priority_asc")) {
diff --git a/sched/file_deleter.cpp b/sched/file_deleter.cpp
index 1d8cdc2..b53a3b5 100644
--- a/sched/file_deleter.cpp
+++ b/sched/file_deleter.cpp
@@ -62,14 +62,17 @@
#define PIDFILE "file_deleter.pid"
#define DEFAULT_SLEEP_INTERVAL 5
-#define RESULTS_PER_WU 4 // an estimate of redundancy
+#define RESULTS_PER_WU 4 // an estimate of redundancy
int id_modulus=0, id_remainder=0, appid=0;
bool dont_retry_errors = false;
bool dont_delete_batches = false;
bool do_input_files = true;
bool do_output_files = true;
+bool dry_run = false;
int sleep_interval = DEFAULT_SLEEP_INTERVAL;
+char *xml_doc_like = NULL;
+char *download_dir = NULL;
void usage(char *name) {
fprintf(stderr, "Deletes files that are no longer needed.\n\n"
@@ -94,7 +97,11 @@ void usage(char *name) {
" For debugging.\n"
" --dont_delete_batches don't delete anything with positive batch number\n"
" --input_files_only delete only input (download) files\n"
+ " --dry_run Don't update DB\n"
+ " For debugging.\n"
" --output_files_only delete only output (upload) files\n"
+ " --xml_doc_like L only process workunits where xml_doc LIKE 'L'\n"
+ " --download_dir D override download_dir from project config with D\n"
" [ -h | --help ] shows this help text\n"
" [ -v | --version ] shows version information\n",
name
@@ -108,7 +115,9 @@ void usage(char *name) {
int get_file_path(
const char *filename, char* upload_dir, int fanout, char* path
) {
- dir_hier_path(filename, upload_dir, fanout, path, true);
+ if (dir_hier_path(filename, upload_dir, fanout, path, false)) {
+ return ERR_OPENDIR;
+ }
if (boinc_file_exists(path)) {
return 0;
}
@@ -131,9 +140,12 @@ int wu_delete_files(WORKUNIT& wu) {
if (strstr(wu.name, "nodelete")) return 0;
safe_strcpy(buf, wu.xml_doc);
-
+
p = strtok(buf, "\n");
strcpy(filename, "");
+
+ // TODO: use the XML parser. Yuck!
+ //
while (p) {
if (parse_str(p, "<name>", filename, sizeof(filename))) {
} else if (match_tag(p, "<file_info>")) {
@@ -144,7 +156,7 @@ int wu_delete_files(WORKUNIT& wu) {
} else if (match_tag(p, "</file_info>")) {
if (!no_delete) {
retval = get_file_path(
- filename, config.download_dir, config.uldl_dir_fanout,
+ filename, download_dir, config.uldl_dir_fanout,
path
);
if (retval == ERR_OPENDIR) {
@@ -307,14 +319,15 @@ bool do_pass(bool retry_error) {
sprintf(buf, " and appid = %d ", appid);
strcat(clause, buf);
}
+
sprintf(buf,
"where file_delete_state=%d %s limit %d",
retry_error?FILE_DELETE_ERROR:FILE_DELETE_READY,
- clause, WUS_PER_ENUM
+ clause, RESULTS_PER_ENUM
);
- while (do_input_files) {
- retval = wu.enumerate(buf);
+ while (do_output_files) {
+ retval = result.enumerate(buf);
if (retval) {
if (retval != ERR_DB_NOT_FOUND) {
log_messages.printf(MSG_DEBUG, "DB connection lost, exiting\n");
@@ -323,43 +336,52 @@ bool do_pass(bool retry_error) {
break;
}
- if (preserve_wu_files) {
+ if (preserve_result_files) {
retval = 0;
} else {
- retval = wu_delete_files(wu);
+ retval = result_delete_files(result);
}
if (retval) {
new_state = FILE_DELETE_ERROR;
log_messages.printf(MSG_CRITICAL,
- "[WU#%u] file deletion failed: %s\n", wu.id, boincerror(retval)
+ "[RESULT#%u] file deletion failed: %s\n", result.id, boincerror(retval)
);
} else {
new_state = FILE_DELETE_DONE;
}
- if (new_state != wu.file_delete_state) {
+ if (new_state != result.file_delete_state) {
sprintf(buf, "file_delete_state=%d", new_state);
- retval = wu.update_field(buf);
+ if (dry_run) {
+ retval = 0;
+ } else {
+ retval = result.update_field(buf);
+ }
if (retval) {
log_messages.printf(MSG_CRITICAL,
- "[WU#%u] update failed: %s\n", wu.id, boincerror(retval)
+ "[RESULT#%u] update failed: %s\n", result.id, boincerror(retval)
);
} else {
log_messages.printf(MSG_DEBUG,
- "[WU#%u] file_delete_state updated\n", wu.id
+ "[RESULT#%u] file_delete_state updated\n", result.id
);
did_something = true;
}
- }
+ }
}
+ if (xml_doc_like) {
+ strcat(clause, " and xml_doc like '");
+ strcat(clause, xml_doc_like);
+ strcat(clause, "'");
+ }
sprintf(buf,
"where file_delete_state=%d %s limit %d",
retry_error?FILE_DELETE_ERROR:FILE_DELETE_READY,
- clause, RESULTS_PER_ENUM
+ clause, WUS_PER_ENUM
);
- while (do_output_files) {
- retval = result.enumerate(buf);
+ while (do_input_files) {
+ retval = wu.enumerate(buf);
if (retval) {
if (retval != ERR_DB_NOT_FOUND) {
log_messages.printf(MSG_DEBUG, "DB connection lost, exiting\n");
@@ -368,34 +390,38 @@ bool do_pass(bool retry_error) {
break;
}
- if (preserve_result_files) {
+ if (preserve_wu_files) {
retval = 0;
} else {
- retval = result_delete_files(result);
+ retval = wu_delete_files(wu);
}
if (retval) {
new_state = FILE_DELETE_ERROR;
log_messages.printf(MSG_CRITICAL,
- "[RESULT#%u] file deletion failed: %s\n", result.id, boincerror(retval)
+ "[WU#%u] file deletion failed: %s\n", wu.id, boincerror(retval)
);
} else {
new_state = FILE_DELETE_DONE;
}
- if (new_state != result.file_delete_state) {
- sprintf(buf, "file_delete_state=%d", new_state);
- retval = result.update_field(buf);
+ if (new_state != wu.file_delete_state) {
+ sprintf(buf, "file_delete_state=%d", new_state);
+ if (dry_run) {
+ retval = 0;
+ } else {
+ retval = wu.update_field(buf);
+ }
if (retval) {
log_messages.printf(MSG_CRITICAL,
- "[RESULT#%u] update failed: %s\n", result.id, boincerror(retval)
+ "[WU#%u] update failed: %s\n", wu.id, boincerror(retval)
);
} else {
log_messages.printf(MSG_DEBUG,
- "[RESULT#%u] file_delete_state updated\n", result.id
+ "[WU#%u] file_delete_state updated\n", wu.id
);
did_something = true;
}
- }
- }
+ }
+ }
return did_something;
}
@@ -422,7 +448,7 @@ int main(int argc, char** argv) {
bool one_pass = false;
int i;
DB_APP app;
-
+
check_stop_daemons();
*app.name='\0';
@@ -461,6 +487,20 @@ int main(int argc, char** argv) {
}
id_modulus = atoi(argv[++i]);
id_remainder = atoi(argv[++i]);
+ } else if (is_arg(argv[i], "download_dir")) {
+ if (!argv[++i]) {
+ log_messages.printf(MSG_CRITICAL, "%s requires an argument\n\n", argv[--i]);
+ usage(argv[0]);
+ exit(1);
+ }
+ download_dir = argv[i];
+ } else if (is_arg(argv[i], "xml_doc_like")) {
+ if (!argv[++i]) {
+ log_messages.printf(MSG_CRITICAL, "%s requires an argument\n\n", argv[--i]);
+ usage(argv[0]);
+ exit(1);
+ }
+ xml_doc_like = argv[i];
} else if (is_arg(argv[i], "dont_delete_antiques")) {
log_messages.printf(MSG_CRITICAL, "'%s' has no effect, this file deleter does no antique files deletion\n", argv[i]);
} else if (is_arg(argv[i], "antiques_deletion_dry_run")) {
@@ -473,6 +513,8 @@ int main(int argc, char** argv) {
log_messages.printf(MSG_CRITICAL, "'%s' has no effect, this file deleter does no antique files deletion\n", argv[i]);
} else if (is_arg(argv[i], "dont_delete_batches")) {
dont_delete_batches = true;
+ } else if (is_arg(argv[i], "dry_run")) {
+ dry_run = true;
} else if (is_arg(argv[i], "delete_antiques_now")) {
log_messages.printf(MSG_CRITICAL, "'%s' has no effect, this file deleter does no antique files deletion\n", argv[i]);
} else if (is_arg(argv[i], "input_files_only")) {
@@ -514,6 +556,15 @@ int main(int argc, char** argv) {
exit(1);
}
+ if (download_dir) {
+ log_messages.printf(MSG_NORMAL,
+ "Overriding download_dir '%s' from project config with command-line '%s'\n",
+ config.download_dir, download_dir
+ );
+ } else {
+ download_dir = config.download_dir;
+ }
+
log_messages.printf(MSG_NORMAL, "Starting\n");
retval = boinc_db.open(config.db_name, config.db_host, config.db_user, config.db_passwd);
@@ -530,7 +581,7 @@ int main(int argc, char** argv) {
}
if (*app.name && !appid) {
- char buf[256];
+ char buf[256];
sprintf(buf, "where name='%s'", app.name);
retval = app.lookup(buf);
if (retval) {
diff --git a/sched/file_upload_handler.cpp b/sched/file_upload_handler.cpp
index 89dcbde..a3f55b9 100644
--- a/sched/file_upload_handler.cpp
+++ b/sched/file_upload_handler.cpp
@@ -385,7 +385,9 @@ int handle_file_upload(FILE* in, R_RSA_PUBLIC_KEY& key) {
"Failed to find/create directory for file '%s' in '%s'\n",
name, config.upload_dir
);
- return return_error(ERR_TRANSIENT, "can't open file");
+ return return_error(ERR_TRANSIENT, "can't open file %s: %s",
+ name, boincerror(retval)
+ );
}
log_messages.printf(MSG_NORMAL,
"Starting upload of %s from %s [offset=%.0f, nbytes=%.0f]\n",
@@ -699,14 +701,14 @@ int main(int argc, char *argv[]) {
log_messages.pid = getpid();
log_messages.set_debug_level(config.fuh_debug_level);
-#ifndef _USING_FCGI_
if (boinc_file_exists(config.project_path("stop_upload"))) {
return_error(ERR_TRANSIENT,
"File uploads are temporarily disabled."
);
+#ifndef _USING_FCGI_
exit(1);
- }
#endif
+ }
if (!config.ignore_upload_certificates) {
retval = get_key(key);
diff --git a/sched/handle_request.cpp b/sched/handle_request.cpp
index 69c1dac..e886f70 100644
--- a/sched/handle_request.cpp
+++ b/sched/handle_request.cpp
@@ -1209,8 +1209,8 @@ void process_request(char* code_sign_key) {
int pid_with_lock = lock_sched();
if (pid_with_lock > 0) {
log_messages.printf(MSG_CRITICAL,
- "Another scheduler instance [PID=%d] is running for this host\n",
- pid_with_lock
+ "Another scheduler instance [PID=%d] is running for [HOST#%d]\n",
+ pid_with_lock, g_reply->host.id
);
} else if (pid_with_lock) {
log_messages.printf(MSG_CRITICAL,
diff --git a/sched/plan_class_spec.cpp b/sched/plan_class_spec.cpp
index 4db09b1..0370914 100644
--- a/sched/plan_class_spec.cpp
+++ b/sched/plan_class_spec.cpp
@@ -26,6 +26,28 @@
using std::string;
+
+// this returns a numerical OS version for Darwin/OSX and Windows,
+// allowing to define a numerical _range_ for these OS versions
+static double os_version_num(HOST h) {
+ if (strstr(h.os_name, "Darwin")) {
+ unsigned int a,b,c;
+ if (sscanf(h.os_version, "%u.%u.%u", &a, &b, &c) == 3) {
+ return 10000.0*a + 100.0*b + c;
+ }
+ } else if (strstr(h.os_name, "Windows")) {
+ unsigned int a,b,c,d;
+ // example: "Enterprise Server Edition, Service Pack 1, (06.01.7601.00)"
+ char*p = strrchr(h.os_version,'(');
+ if (p && (sscanf(p, "(%u.%u.%u.%u)", &a, &b, &c, &d) == 4)) {
+ return 100000000.0*a + 1000000.0*b + 100.0*c +d;
+ }
+ }
+ // could not determine numerical OS version
+ return 0.0;
+}
+
+
int PLAN_CLASS_SPECS::parse_file(const char* path) {
#ifndef _USING_FCGI_
FILE* f = fopen(path, "r");
@@ -38,6 +60,56 @@ int PLAN_CLASS_SPECS::parse_file(const char* path) {
return retval;
}
+bool PLAN_CLASS_SPEC::opencl_check(OPENCL_DEVICE_PROP& opencl_prop) {
+ if (min_opencl_version && opencl_prop.opencl_device_version_int
+ && min_opencl_version > opencl_prop.opencl_device_version_int
+ ) {
+ if (config.debug_version_select) {
+ log_messages.printf(MSG_NORMAL,
+ "[version] OpenCL device version required min: %d, supplied: %d\n",
+ min_opencl_version, opencl_prop.opencl_device_version_int
+ );
+ }
+ return false;
+ }
+
+ if (max_opencl_version && opencl_prop.opencl_device_version_int
+ && max_opencl_version < opencl_prop.opencl_device_version_int
+ ) {
+ if (config.debug_version_select) {
+ log_messages.printf(MSG_NORMAL,
+ "[version] OpenCL device version required max: %d, supplied: %d\n",
+ max_opencl_version, opencl_prop.opencl_device_version_int
+ );
+ }
+ return false;
+ }
+
+ if (min_opencl_driver_revision && opencl_prop.opencl_device_version_int
+ && min_opencl_driver_revision > opencl_prop.opencl_driver_revision
+ ) {
+ if (config.debug_version_select) {
+ log_messages.printf(MSG_NORMAL,
+ "[version] OpenCL driver revision required min: %d, supplied: %d\n",
+ min_opencl_driver_revision, opencl_prop.opencl_driver_revision
+ );
+ }
+ return false;
+ }
+
+ if (max_opencl_driver_revision && opencl_prop.opencl_device_version_int
+ && max_opencl_driver_revision < opencl_prop.opencl_driver_revision
+ ) {
+ if (config.debug_version_select) {
+ log_messages.printf(MSG_NORMAL,
+ "[version] OpenCL driver revision required max: %d, supplied: %d\n",
+ max_opencl_driver_revision, opencl_prop.opencl_driver_revision
+ );
+ }
+ return false;
+ }
+ return true;
+}
bool PLAN_CLASS_SPEC::check(SCHEDULER_REQUEST& sreq, HOST_USAGE& hu) {
COPROC* cpp = NULL;
@@ -118,8 +190,38 @@ bool PLAN_CLASS_SPEC::check(SCHEDULER_REQUEST& sreq, HOST_USAGE& hu) {
}
return false;
}
+ if (min_os_version || max_os_version) {
+ double host_os_version_num = os_version_num(sreq.host);
+ if (!host_os_version_num) {
+ if (config.debug_version_select) {
+ log_messages.printf(MSG_NORMAL,
+ "[version] plan_class_spec: Can't determine numerical OS version '%s'\n",
+ sreq.host.os_version
+ );
+ }
+ return false;
+ }
+ if (min_os_version && (host_os_version_num < min_os_version)) {
+ if (config.debug_version_select) {
+ log_messages.printf(MSG_NORMAL,
+ "[version] plan_class_spec: OS version '%s' too low (%.0f / %.0f)\n",
+ sreq.host.os_version, host_os_version_num, min_os_version
+ );
+ }
+ return false;
+ }
+ if (max_os_version && (host_os_version_num > max_os_version)) {
+ if (config.debug_version_select) {
+ log_messages.printf(MSG_NORMAL,
+ "[version] plan_class_spec: OS version '%s' too high (%.0f / %.0f)\n",
+ sreq.host.os_version, host_os_version_num, max_os_version
+ );
+ }
+ return false;
+ }
+ }
- // BOINC versions
+ // BOINC versions
//
if (min_core_client_version && sreq.core_client_version < min_core_client_version) {
if (config.debug_version_select) {
@@ -235,7 +337,7 @@ bool PLAN_CLASS_SPEC::check(SCHEDULER_REQUEST& sreq, HOST_USAGE& hu) {
project_prefs_tag, p?"true":"false"
);
}
- if (!p || regexec(&(project_prefs_regex), value, 0, NULL, 0)) {
+ if (p ? regexec(&(project_prefs_regex), value, 0, NULL, 0) : !project_prefs_default_true) {
if (config.debug_version_select) {
log_messages.printf(MSG_NORMAL,
"[version] plan_class_spec: project prefs setting '%s' value='%s' prevents using plan class.\n",
@@ -456,63 +558,43 @@ bool PLAN_CLASS_SPEC::check(SCHEDULER_REQUEST& sreq, HOST_USAGE& hu) {
}
if (opencl) {
- // check for OpenCL at all
- if (!cpp->have_opencl) {
- if (config.debug_version_select) {
- log_messages.printf(MSG_NORMAL,
- "[version] GPU doesn't support OpenCL\n"
- );
- }
- return false;
- }
-
- // OpenCL device version
- //
- if (min_opencl_version && cpp->opencl_prop.opencl_device_version_int
- && min_opencl_version > cpp->opencl_prop.opencl_device_version_int) {
- if (config.debug_version_select) {
- log_messages.printf(MSG_NORMAL,
- "[version] OpenCL device version required min: %d, supplied: %d\n",
- min_opencl_version, cpp->opencl_prop.opencl_device_version_int
- );
+ if (cpp) {
+ if (!cpp->have_opencl) {
+ if (config.debug_version_select) {
+ log_messages.printf(MSG_NORMAL,
+ "[version] GPU doesn't support OpenCL\n"
+ );
+ }
+ return false;
}
- return false;
- }
-
- if (max_opencl_version && cpp->opencl_prop.opencl_device_version_int
- && max_opencl_version < cpp->opencl_prop.opencl_device_version_int) {
- if (config.debug_version_select) {
- log_messages.printf(MSG_NORMAL,
- "[version] OpenCL device version required max: %d, supplied: %d\n",
- max_opencl_version, cpp->opencl_prop.opencl_device_version_int
- );
+ if (!opencl_check(cpp->opencl_prop)) {
+ return false;
}
- return false;
- }
-
- if (min_opencl_driver_revision && cpp->opencl_prop.opencl_device_version_int
- && min_opencl_driver_revision > cpp->opencl_prop.opencl_driver_revision) {
- if (config.debug_version_select) {
- log_messages.printf(MSG_NORMAL,
- "[version] OpenCL driver revision required min: %d, supplied: %d\n",
- min_opencl_driver_revision, cpp->opencl_prop.opencl_driver_revision
- );
+ gpu_ram = cpp->opencl_prop.global_mem_size;
+ } else {
+ // OpenCL CPU app version.
+ // The host may have several OpenCL CPU libraries.
+ // See if any of them works.
+ // TODO: there should be a way of saying which library
+ // the app version requires,
+ // or a way of conveying to the app which one to use.
+ //
+ bool found = false;
+ for (int i=0; i<sreq.host.num_opencl_cpu_platforms; i++) {
+ if (opencl_check(sreq.host.opencl_cpu_prop[i].opencl_prop)) {
+ found = true;
+ break;
+ }
}
- return false;
- }
-
- if (max_opencl_driver_revision && cpp->opencl_prop.opencl_device_version_int
- && max_opencl_driver_revision < cpp->opencl_prop.opencl_driver_revision) {
- if (config.debug_version_select) {
- log_messages.printf(MSG_NORMAL,
- "[version] OpenCL driver revision required max: %d, supplied: %d\n",
- max_opencl_driver_revision, cpp->opencl_prop.opencl_driver_revision
- );
+ if (!found) {
+ if (config.debug_version_select) {
+ log_messages.printf(MSG_NORMAL,
+ "[version] CPU doesn't support OpenCL\n"
+ );
+ }
+ return false;
}
- return false;
}
-
- gpu_ram = cpp->opencl_prop.global_mem_size;
}
// general GPU
@@ -690,6 +772,8 @@ int PLAN_CLASS_SPEC::parse(XML_PARSER& xp) {
have_host_summary_regex = true;
continue;
}
+ if (xp.parse_double("min_os_version", min_os_version)) continue;
+ if (xp.parse_double("max_os_version", max_os_version)) continue;
if (xp.parse_str("project_prefs_tag", project_prefs_tag, sizeof(project_prefs_tag))) continue;
if (xp.parse_str("project_prefs_regex", buf, sizeof(buf))) {
if (regcomp(&(project_prefs_regex), buf, REG_EXTENDED|REG_NOSUB) ) {
@@ -699,6 +783,7 @@ int PLAN_CLASS_SPEC::parse(XML_PARSER& xp) {
have_project_prefs_regex = true;
continue;
}
+ if (xp.parse_bool("project_prefs_default_true", project_prefs_default_true)) continue;
if (xp.parse_double("avg_ncpus", avg_ncpus)) continue;
if (xp.parse_double("cpu_frac", cpu_frac)) continue;
@@ -771,12 +856,15 @@ PLAN_CLASS_SPEC::PLAN_CLASS_SPEC() {
max_threads = 1;
projected_flops_scale = 1;
have_os_regex = false;
- have_host_summary_regex = false;
+ min_os_version = 0;
+ max_os_version = 0;
strcpy(project_prefs_tag, "");
have_project_prefs_regex = false;
+ project_prefs_default_true = false;
avg_ncpus = 0;
- min_core_client_version=0;
- max_core_client_version=0;
+ min_core_client_version = 0;
+ max_core_client_version = 0;
+ have_host_summary_regex = false;
cpu_frac = .1;
min_gpu_ram_mb = 0;
@@ -789,6 +877,8 @@ PLAN_CLASS_SPEC::PLAN_CLASS_SPEC() {
need_ati_libs = false;
need_amd_libs = false;
+ min_cal_target = 0;
+ max_cal_target = 0;
without_opencl = false;
min_nvidia_compcap = 0;
@@ -798,7 +888,6 @@ PLAN_CLASS_SPEC::PLAN_CLASS_SPEC() {
min_opencl_version = 0;
max_opencl_version = 0;
-
min_opencl_driver_revision = 0;
max_opencl_driver_revision = 0;
diff --git a/sched/plan_class_spec.h b/sched/plan_class_spec.h
index 9082269..d2ce867 100644
--- a/sched/plan_class_spec.h
+++ b/sched/plan_class_spec.h
@@ -37,9 +37,12 @@ struct PLAN_CLASS_SPEC {
double projected_flops_scale;
bool have_os_regex;
regex_t os_regex;
+ double min_os_version;
+ double max_os_version;
char project_prefs_tag[256];
bool have_project_prefs_regex;
regex_t project_prefs_regex;
+ bool project_prefs_default_true;
double avg_ncpus;
int min_core_client_version;
int max_core_client_version;
@@ -93,6 +96,7 @@ struct PLAN_CLASS_SPEC {
bool vm_accel_required;
int parse(XML_PARSER&);
+ bool opencl_check(OPENCL_DEVICE_PROP&);
bool check(SCHEDULER_REQUEST& sreq, HOST_USAGE& hu);
PLAN_CLASS_SPEC();
};
diff --git a/sched/pshelper b/sched/pshelper
new file mode 100755
index 0000000..042c8e1
--- /dev/null
+++ b/sched/pshelper
@@ -0,0 +1,3 @@
+#!/bin/sh
+pid=`cat "$1"` &&
+ps ww $pid | grep "^ *\($pid\) " || echo false
diff --git a/sched/sched_array.cpp b/sched/sched_array.cpp
index 7dd70f8..faedf2a 100644
--- a/sched/sched_array.cpp
+++ b/sched/sched_array.cpp
@@ -15,7 +15,9 @@
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
-// scheduler code related to sending work
+// The "old style" scheduler, where we make multiple scans through
+// the job cache.
+// This will soon be deprecated in favor of score-based scheduling.
#include <cstdlib>
#include <string>
@@ -145,7 +147,7 @@ static bool quick_check(
}
}
- // Find the app and best app_version for this host.
+ // Find the best app_version for this host.
//
bavp = get_app_version(wu, true, g_wreq->reliable_only);
if (!bavp) {
@@ -164,7 +166,7 @@ static bool quick_check(
if (g_wreq->user_apps_only &&
(!g_wreq->beta_only || config.distinct_beta_apps)
) {
- if (app_not_selected(wu)) {
+ if (app_not_selected(app->id)) {
g_wreq->no_allowed_apps_available = true;
if (config.debug_array_detail) {
log_messages.printf(MSG_NORMAL,
@@ -441,103 +443,4 @@ void send_work_old() {
}
}
-
-// try to send a job for the given app; used for non-compute-intensive apps
-//
-int send_job_for_app(APP& app) {
- int retval = 0;
- BEST_APP_VERSION* bavp;
- SCHED_DB_RESULT result;
-
- lock_sema();
- for (int i=0; i<ssp->max_wu_results; i++) {
- WU_RESULT& wu_result = ssp->wu_results[i];
- if (wu_result.state != WR_STATE_PRESENT && wu_result.state != g_pid) {
- continue;
- }
- WORKUNIT wu = wu_result.workunit;
- if (wu.appid != app.id) continue;
- if (!quick_check(wu_result, wu, bavp, &app, retval)) {
- // All jobs for a given NCI app are identical.
- // If we can't send one, we can't send any.
- //
- unlock_sema();
- log_messages.printf(MSG_NORMAL,
- "quick_check() failed for NCI job\n"
- );
- return -1;
- }
- wu_result.state = g_pid;
- unlock_sema();
- result.id = wu_result.resultid;
- wu_result.state = WR_STATE_EMPTY;
- if (result_still_sendable(result, wu)) {
- if (config.debug_send) {
- log_messages.printf(MSG_NORMAL,
- "Sending non-CPU-intensive job: %s\n", wu.name
- );
- }
- add_result_to_reply(result, wu, bavp, false);
- return 0;
- }
- log_messages.printf(MSG_NORMAL,
- "NCI job was not still sendable\n"
- );
- lock_sema();
- }
- log_messages.printf(MSG_NORMAL,
- "no sendable NCI jobs for %s\n", app.user_friendly_name
- );
- unlock_sema();
- return 1;
-}
-
-// try to send jobs for non-CPU-intensive (NCI) apps
-// for which the host doesn't have a job in progress
-//
-int send_nci() {
- int retval;
- vector<APP> nci_apps;
- char buf[1024];
-
- // make a vector of NCI apps
- //
- for (int i=0; i<ssp->napps; i++) {
- APP& app = ssp->apps[i];
- if (!app.non_cpu_intensive) continue;
- app.have_job = false;
- nci_apps.push_back(app);
- }
-
- // scan through the list of in-progress jobs,
- // flagging the associated apps as having jobs
- //
- for (unsigned int i=0; i<g_request->other_results.size(); i++) {
- DB_RESULT r;
- OTHER_RESULT &ores = g_request->other_results[i];
- sprintf(buf, "where name='%s'", ores.name);
- retval = r.lookup(buf);
- if (retval) {
- log_messages.printf(MSG_NORMAL, "No such result: %s\n", ores.name);
- continue;
- }
- APP* app = ssp->lookup_app(r.appid);
- app->have_job = true;
- }
-
- // For each NCI app w/o a job, try to send one
- //
- for (unsigned int i=0; i<nci_apps.size(); i++) {
- APP& app = nci_apps[i];
- if (app.have_job) continue;
- retval = send_job_for_app(app);
- if (retval) {
- log_messages.printf(MSG_NORMAL,
- "failed to send job for NCI app %s\n", app.user_friendly_name
- );
- }
- }
- return 0;
-}
-
const char *BOINC_RCSID_d9f764fd14="$Id$";
diff --git a/sched/sched_array.h b/sched/sched_array.h
index c1d1f4a..a87052d 100644
--- a/sched/sched_array.h
+++ b/sched/sched_array.h
@@ -16,4 +16,3 @@
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
extern void send_work_old();
-extern int send_nci();
diff --git a/sched/sched_assign.cpp b/sched/sched_assign.cpp
index 894ddfe..8933201 100644
--- a/sched/sched_assign.cpp
+++ b/sched/sched_assign.cpp
@@ -35,6 +35,7 @@
#include "error_numbers.h"
#include "filesys.h"
+#include "sched_check.h"
#include "sched_main.h"
#include "sched_msgs.h"
#include "sched_send.h"
@@ -43,6 +44,61 @@
#include "sched_assign.h"
+// The workunit is targeted to the host (or user or team).
+// Decide if we should actually send an instance
+//
+bool need_targeted_instance(WORKUNIT& wu, int hostid) {
+
+ // don't send if WU had error or was canceled
+ // (db_purge will eventually delete WU and assignment records)
+ //
+ if (wu.error_mask) {
+ return false;
+ }
+
+ // don't send if WU is validation pending or completed,
+ // or has transition pending
+ //
+ if (wu.need_validate) return false;
+ if (wu.canonical_resultid) return false;
+ if (wu.transition_time < time(0)) return false;
+
+ // See if this WU needs another instance.
+ // This replicates logic in the transitioner
+ //
+ char buf[256];
+ DB_RESULT result;
+ int nunsent=0, ninprogress=0, nsuccess=0;
+ sprintf(buf, "where workunitid=%d", wu.id);
+ while (!result.enumerate(buf)) {
+ // send at most 1 instance to a given host
+ //
+ if (result.hostid == hostid) {
+ return false;
+ }
+ switch (result.server_state) {
+ case RESULT_SERVER_STATE_INACTIVE:
+ case RESULT_SERVER_STATE_UNSENT:
+ nunsent++;
+ break;
+ case RESULT_SERVER_STATE_IN_PROGRESS:
+ ninprogress++;
+ break;
+ case RESULT_SERVER_STATE_OVER:
+ if (result.outcome == RESULT_OUTCOME_SUCCESS
+ && result.validate_state != VALIDATE_STATE_INVALID
+ ) {
+ nsuccess++;
+ }
+ break;
+ }
+ }
+ int needed = wu.target_nresults - nunsent - ninprogress - nsuccess;
+ if (needed <= 0) return false;
+
+ return true;
+}
+
// send a job for the given assignment
//
static int send_assigned_job(ASSIGNMENT& asg) {
@@ -73,7 +129,7 @@ static int send_assigned_job(ASSIGNMENT& asg) {
return retval;
}
- if (app_not_selected(wu)) {
+ if (app_not_selected(wu.appid)) {
log_messages.printf(MSG_CRITICAL,
"Assigned WU %s is for app not selected by user\n", wu.name
);
@@ -113,7 +169,7 @@ static int send_assigned_job(ASSIGNMENT& asg) {
return 0;
}
-// Send this host any broadcase jobs.
+// Send this host any broadcast jobs.
// Return true iff we sent anything
//
bool send_broadcast_jobs() {
@@ -179,7 +235,7 @@ bool send_jobs(int assign_type) {
DB_WORKUNIT wu;
int retval;
bool sent_something = false;
- char query[256], buf[256];
+ char query[256];
switch (assign_type) {
case ASSIGN_USER:
@@ -210,21 +266,9 @@ bool send_jobs(int assign_type) {
continue;
}
- // don't send if WU is validation pending or completed,
- // or has transition pending
- //
- if (wu.need_validate) continue;
- if (wu.canonical_resultid) continue;
- if (wu.transition_time < time(0)) continue;
-
- // don't send if we already sent an instance to this host
- //
- sprintf(buf, "where workunitid=%d and hostid=%d",
- asg.workunitid,
- g_reply->host.id
- );
- retval = result.lookup(buf);
- if (retval != ERR_DB_NOT_FOUND) continue;
+ if (!need_targeted_instance(wu, g_reply->host.id)) {
+ continue;
+ }
// OK, send the job
//
diff --git a/sched/sched_check.cpp b/sched/sched_check.cpp
index 43d6f35..bf0af60 100644
--- a/sched/sched_check.cpp
+++ b/sched/sched_check.cpp
@@ -45,14 +45,14 @@ const char* infeasible_string(int code) {
}
// Return true if the user has set application preferences,
-// and this job is not for a selected app
+// and excluded this app
//
-bool app_not_selected(WORKUNIT& wu) {
+bool app_not_selected(int appid) {
unsigned int i;
if (g_wreq->preferred_apps.size() == 0) return false;
for (i=0; i<g_wreq->preferred_apps.size(); i++) {
- if (wu.appid == g_wreq->preferred_apps[i].appid) {
+ if (appid == g_wreq->preferred_apps[i].appid) {
g_wreq->preferred_apps[i].work_available = true;
return false;
}
diff --git a/sched/sched_check.h b/sched/sched_check.h
index d4c7414..fb9fefe 100644
--- a/sched/sched_check.h
+++ b/sched/sched_check.h
@@ -44,5 +44,6 @@ extern int wu_is_infeasible_fast(
);
extern int slow_check(WU_RESULT&, APP*, BEST_APP_VERSION*);
extern bool result_still_sendable(DB_RESULT& result, WORKUNIT& wu);
+extern bool app_not_selected(int appid);
#endif
diff --git a/sched/sched_locality.cpp b/sched/sched_locality.cpp
index e4e60ae..7fa829d 100644
--- a/sched/sched_locality.cpp
+++ b/sched/sched_locality.cpp
@@ -288,6 +288,13 @@ static int possibly_send_result(SCHED_DB_RESULT& result) {
retval = wu.lookup_id(result.workunitid);
if (retval) return ERR_DB_NOT_FOUND;
+ // This doesn't take into account g_wreq->allow_non_preferred_apps,
+ // however Einstein at Home, which is the only project that currently uses
+ // this locality scheduler, doesn't support the respective project-specific
+ // preference setting
+ //
+ if (app_not_selected(wu.appid)) return ERR_NO_APP_VERSION;
+
bavp = get_app_version(wu, true, false);
if (!config.locality_scheduler_fraction && !bavp && is_anonymous(g_request->platforms.list[0])) {
@@ -555,9 +562,22 @@ static int send_results_for_file(
nsent = 0;
if (!work_needed(true)) {
+ if (config.debug_locality) {
+ log_messages.printf(MSG_NORMAL,
+ "[locality] send_results_for_file(): No work needed\n"
+ );
+ }
return 0;
+ } else {
+ if (config.debug_locality) {
+ log_messages.printf(MSG_NORMAL,
+ "[locality] send_results_for_file(%s)\n",
+ filename
+ );
+ }
}
+
// find largest ID of results already sent to this user for this
// file, if any. Any result that is sent will have userid field
// set, so unsent results can not be returned by this query.
@@ -905,7 +925,7 @@ static int send_new_file_work_working_set() {
}
// prototype
-static int send_old_work(int t_min, int t_max);
+static int send_old_work(int t_min, int t_max, bool locality_work_only=false);
// The host doesn't have any files for which work is available.
// Pick new file to send. Returns nonzero if no work is available.
@@ -971,10 +991,12 @@ static int send_new_file_work() {
// also eventually move 'non locality' work through and out of the
// system?
//
+// Yes, Bruce, it will. BM
+//
// This looks for work created in the range t_min < t < t_max. Use
// t_min=INT_MIN if you wish to leave off the left constraint.
//
-static int send_old_work(int t_min, int t_max) {
+static int send_old_work(int t_min, int t_max, bool locality_work_only) {
char buf[1024], filename[256];
int retval, extract_retval, nsent;
SCHED_DB_RESULT result;
@@ -997,14 +1019,24 @@ static int send_old_work(int t_min, int t_max) {
//
if (t_min != INT_MIN) {
sprintf(buf,
+#ifdef EINSTEIN_AT_HOME
+ "INNER JOIN (SELECT id FROM result USE INDEX (res_create_server_state) WHERE server_state=%d and %d<create_time and create_time<%d %s limit 1) AS single USING (id)",
+ RESULT_SERVER_STATE_UNSENT, t_min, t_max, locality_work_only?"and name>binary '%s__' and name<binary '%s__~'":""
+#else
"INNER JOIN (SELECT id FROM result WHERE server_state=%d and %d<create_time and create_time<%d limit 1) AS single USING (id)",
RESULT_SERVER_STATE_UNSENT, t_min, t_max
+#endif
);
}
else {
sprintf(buf,
+#ifdef EINSTEIN_AT_HOME
+ "INNER JOIN (SELECT id FROM result USE INDEX (res_create_server_state) WHERE server_state=%d and create_time<%d %s limit 1) AS single USING (id)",
+ RESULT_SERVER_STATE_UNSENT, t_max, locality_work_only?"and name>binary '%s__' and name<binary '%s__~'":""
+#else
"INNER JOIN (SELECT id FROM result WHERE server_state=%d and create_time<%d limit 1) AS single USING (id)",
RESULT_SERVER_STATE_UNSENT, t_max
+#endif
);
}
@@ -1154,6 +1186,16 @@ void send_work_locality() {
);
}
+ // send old work if there is any. send this only to hosts which have
+ // high-bandwidth connections, since asking dial-up users to upload
+ // (presumably large) data files is onerous.
+ //
+ if (config.locality_scheduling_send_timeout && g_request->host.n_bwdown>100000) {
+ int until=time(0)-config.locality_scheduling_send_timeout;
+ int retval_sow=send_old_work(INT_MIN, until);
+ if (!work_needed(true)) return;
+ }
+
// Look for work in order of increasing file name, or randomly?
//
if (config.locality_scheduling_sorted_order) {
@@ -1164,16 +1206,6 @@ void send_work_locality() {
j = rand()%nfiles;
}
- // send old work if there is any. send this only to hosts which have
- // high-bandwidth connections, since asking dial-up users to upload
- // (presumably large) data files is onerous.
- //
- if (config.locality_scheduling_send_timeout && g_request->host.n_bwdown>100000) {
- int until=time(0)-config.locality_scheduling_send_timeout;
- int retval_sow=send_old_work(INT_MIN, until);
- if (retval_sow==ERR_NO_APP_VERSION || retval_sow==ERR_INSUFFICIENT_RESOURCE) return;
- }
-
// send work for existing files
//
for (i=0; i<(int)g_request->file_infos.size(); i++) {
@@ -1182,7 +1214,7 @@ void send_work_locality() {
if (!work_needed(true)) break;
FILE_INFO& fi = g_request->file_infos[k];
- retval_srff=send_results_for_file(
+ retval_srff = send_results_for_file(
fi.name, nsent, false
);
@@ -1203,10 +1235,41 @@ void send_work_locality() {
);
}
#ifdef EINSTEIN_AT_HOME
- // For name matching pattern h1_XXXX.XX_S5R4
- // generate corresponding l1_XXXX.XX_S5R4 and *_S5R7 patterns and delete it also
+ // For name matching patterns h1_
+ // generate corresponding l1_ patterns and delete these also
//
- if (strlen(fi.name)==15 && !strncmp("h1_", fi.name, 3)) {
+ if ( /* files like h1_0340.30_S6GC1 */
+ ( strlen(fi.name) == 16 &&
+ !strncmp("h1_", fi.name, 3) &&
+ !strncmp("_S6GC1", fi.name + 10, 6)
+ ) ||
+ /* files like h1_0000.00_S6Directed */
+ ( strlen(fi.name) == 21 &&
+ !strncmp("h1_", fi.name, 3) &&
+ !strncmp("_S6Directed", fi.name + 10, 11)
+ ) ||
+ /* files like h1_0000.00_S6Direct */
+ ( strlen(fi.name) == 19 &&
+ !strncmp("h1_", fi.name, 3) &&
+ !strncmp("_S6Direct", fi.name + 10, 9)
+ )
+ ) {
+ FILE_INFO fil;
+ fil=fi;
+ fil.name[0]='l';
+ g_reply->file_deletes.push_back(fil);
+ if (config.debug_locality) {
+ log_messages.printf(MSG_NORMAL,
+ "[locality] [HOST#%d]: delete file %s (accompanies %s)\n",
+ g_reply->host.id, fil.name, fi.name
+ );
+ }
+ } else if ( /* for files like h1_XXXX.XX_S5R4 */
+ ( strlen(fi.name) == 15 &&
+ !strncmp("h1_", fi.name, 3) &&
+ !strncmp("_S5R4", fi.name + 10, 5)
+ )
+ ) {
FILE_INFO fil4,fil7,fih7;
fil4=fi;
fil4.name[0]='l';
@@ -1219,8 +1282,8 @@ void send_work_locality() {
g_reply->file_deletes.push_back(fih7);
if (config.debug_locality) {
log_messages.printf(MSG_NORMAL,
- "[locality] [HOST#%d]: delete files %s,%s,%s (not needed)\n",
- g_reply->host.id, fil4.name,fil7.name,fih7.name
+ "[locality] [HOST#%d]: delete files %s,%s,%s (accompanies %s)\n",
+ g_reply->host.id, fil4.name,fil7.name,fih7.name, fi.name
);
}
}
diff --git a/sched/sched_main.cpp b/sched/sched_main.cpp
index 16a4944..9649fa1 100644
--- a/sched/sched_main.cpp
+++ b/sched/sched_main.cpp
@@ -547,7 +547,7 @@ int main(int argc, char** argv) {
sprintf(reply_path, "%s/%d_%u_sched_reply.xml", config.debug_req_reply_dir, g_pid, counter);
// keep an own 'log' per PID in case general logging fails
- // this allows to associate at leas the scheduler request with the client
+ // this allows to associate at least the scheduler request with the client
// IP address (as shown in httpd error log) in case of a crash
sprintf(log_path, "%s/%d_%u_sched.log", config.debug_req_reply_dir, g_pid, counter);
#ifndef _USING_FCGI_
@@ -555,6 +555,12 @@ int main(int argc, char** argv) {
#else
fout = FCGI::fopen(log_path,"a");
#endif
+ if (!fout) {
+ log_messages.printf(MSG_CRITICAL,
+ "can't write client file\n"
+ );
+ exit(1);
+ }
fprintf(fout, "PID: %d Client IP: %s\n", g_pid, get_remote_addr());
fclose(fout);
diff --git a/sched/sched_nci.cpp b/sched/sched_nci.cpp
new file mode 100644
index 0000000..34e29f6
--- /dev/null
+++ b/sched/sched_nci.cpp
@@ -0,0 +1,188 @@
+// This file is part of BOINC.
+// http://boinc.berkeley.edu
+// Copyright (C) 2014 University of California
+//
+// BOINC is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License
+// as published by the Free Software Foundation,
+// either version 3 of the License, or (at your option) any later version.
+//
+// BOINC is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
+// try to send a job for the given app; used for non-compute-intensive apps
+
+// Logic for sending non-compute-intensive (NCI) jobs
+
+#include "sched_check.h"
+#include "sched_main.h"
+#include "sched_msgs.h"
+#include "sched_send.h"
+#include "sched_version.h"
+
+#include "sched_nci.h"
+
+static bool can_send_nci(
+ WU_RESULT& wu_result,
+ WORKUNIT& wu,
+ BEST_APP_VERSION* &bavp,
+ APP* app
+) {
+ bavp = get_app_version(wu, true, false);
+ if (!bavp) {
+ if (config.debug_send) {
+ log_messages.printf(MSG_NORMAL,
+ "[send] No app version for NCI job; skipping\n"
+ );
+ }
+ return false;
+ }
+ int retval = wu_is_infeasible_fast(
+ wu,
+ wu_result.res_server_state, wu_result.res_priority,
+ wu_result.res_report_deadline,
+ *app, *bavp
+ );
+ if (retval) {
+ if (config.debug_send) {
+ log_messages.printf(MSG_NORMAL,
+ "[send] wu_is_infeasible_fast() failed for NCI job; skipping\n"
+ );
+ }
+ return false;
+ }
+ return true;
+}
+
+static int send_job_for_app(APP& app) {
+ BEST_APP_VERSION* bavp;
+ SCHED_DB_RESULT result;
+
+ lock_sema();
+ for (int i=0; i<ssp->max_wu_results; i++) {
+ WU_RESULT& wu_result = ssp->wu_results[i];
+ if (wu_result.state != WR_STATE_PRESENT && wu_result.state != g_pid) {
+ continue;
+ }
+ WORKUNIT wu = wu_result.workunit;
+
+ if (wu.appid != app.id) continue;
+
+ if (!can_send_nci(wu_result, wu, bavp, &app)) {
+ // All jobs for a given NCI app are identical.
+ // If we can't send one, we can't send any.
+ //
+ unlock_sema();
+ log_messages.printf(MSG_NORMAL,
+ "can_send_nci() failed for NCI job\n"
+ );
+ return -1;
+ }
+ wu_result.state = g_pid;
+ unlock_sema();
+ result.id = wu_result.resultid;
+ wu_result.state = WR_STATE_EMPTY;
+ if (result_still_sendable(result, wu)) {
+ if (config.debug_send) {
+ log_messages.printf(MSG_NORMAL,
+ "Sending non-CPU-intensive job: %s\n", wu.name
+ );
+ }
+ add_result_to_reply(result, wu, bavp, false);
+ return 0;
+ }
+ log_messages.printf(MSG_NORMAL,
+ "NCI job was not still sendable\n"
+ );
+ lock_sema();
+ }
+ log_messages.printf(MSG_NORMAL,
+ "no sendable NCI jobs for %s\n", app.user_friendly_name
+ );
+ unlock_sema();
+ return 1;
+}
+
+// try to send jobs for non-CPU-intensive (NCI) apps
+// for which the host doesn't have a job in progress
+//
+int send_nci() {
+ int retval;
+ vector<APP> nci_apps;
+ char buf[1024];
+
+ if (config.debug_send) {
+ log_messages.printf(MSG_NORMAL, "checking for NCI jobs\n");
+ }
+
+ // make a vector of NCI apps
+ //
+ for (int i=0; i<ssp->napps; i++) {
+ if (!ssp->apps[i].non_cpu_intensive) continue;
+ APP app = ssp->apps[i];
+ app.have_job = false;
+ nci_apps.push_back(app);
+ }
+
+ // scan through the list of in-progress jobs,
+ // flagging the associated apps as having jobs
+ //
+ for (unsigned int i=0; i<g_request->other_results.size(); i++) {
+ DB_RESULT r;
+ OTHER_RESULT &ores = g_request->other_results[i];
+ sprintf(buf, "where name='%s'", ores.name);
+ retval = r.lookup(buf);
+ if (retval) {
+ log_messages.printf(MSG_NORMAL, "No such result: %s\n", ores.name);
+ continue;
+ }
+ for (unsigned int j=0; j<nci_apps.size(); j++) {
+ APP& app = nci_apps[j];
+ if (app.id == r.appid) {
+ app.have_job = true;
+ break;
+ }
+ }
+ }
+
+ // For each NCI app w/o a job, try to send one
+ //
+ for (unsigned int i=0; i<nci_apps.size(); i++) {
+ APP& app = nci_apps[i];
+ if (app.have_job) {
+ if (config.debug_send) {
+ log_messages.printf(MSG_NORMAL,
+ "Already have job for %s\n", app.name
+ );
+ }
+ continue;
+ }
+ if (app.beta && !g_wreq->allow_beta_work) {
+ if (config.debug_send) {
+ log_messages.printf(MSG_NORMAL, "%s is beta\n", app.name);
+ }
+ continue;
+ }
+ if (app_not_selected(app.id)) {
+ if (!g_wreq->allow_non_preferred_apps) {
+ if (config.debug_send) {
+ log_messages.printf(MSG_NORMAL,
+ "%s is not selected\n", app.name
+ );
+ }
+ continue;
+ }
+ }
+ retval = send_job_for_app(app);
+ if (retval) {
+ log_messages.printf(MSG_NORMAL,
+ "failed to send job for NCI app %s\n", app.user_friendly_name
+ );
+ }
+ }
+ return 0;
+}
diff --git a/sched/sched_array.h b/sched/sched_nci.h
similarity index 90%
copy from sched/sched_array.h
copy to sched/sched_nci.h
index c1d1f4a..2f005ec 100644
--- a/sched/sched_array.h
+++ b/sched/sched_nci.h
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
-// Copyright (C) 2008 University of California
+// Copyright (C) 2014 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -15,5 +15,4 @@
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
-extern void send_work_old();
extern int send_nci();
diff --git a/sched/sched_resend.cpp b/sched/sched_resend.cpp
index c0e00da..e676667 100644
--- a/sched/sched_resend.cpp
+++ b/sched/sched_resend.cpp
@@ -153,6 +153,26 @@ bool resend_lost_work() {
}
if (can_resend) {
app = ssp->lookup_app(wu.appid);
+ if (!app) {
+ log_messages.printf(MSG_CRITICAL,
+ "can't resend - app not found for [RESULT#%u]\n", result.id
+ );
+ can_resend = false;
+ }
+ }
+ if (can_resend && app->deprecated) {
+ log_messages.printf(MSG_NORMAL,
+ "[RESULT#%u] can't resend - app is deprecated \n", result.id
+ );
+ can_resend = false;
+ }
+ if (can_resend && app_not_selected(app->id)) {
+ log_messages.printf(MSG_NORMAL,
+ "[RESULT#%u] can't resend - app is not selected\n", result.id
+ );
+ can_resend = false;
+ }
+ if (can_resend) {
bavp = get_app_version(wu, true, false);
if (!bavp) {
if (config.debug_resend) {
diff --git a/sched/sched_result.cpp b/sched/sched_result.cpp
index b85b187..8e50f85 100644
--- a/sched/sched_result.cpp
+++ b/sched/sched_result.cpp
@@ -57,6 +57,11 @@ static inline void got_good_result(SCHED_RESULT_ITEM& sri) {
}
}
+// This is called then a job crashed or exceeded limits on a host.
+// Enforce
+// - mechanism that reduces jobs per day to that host
+// - mechanism that categorizes hosts as "reliable"
+//
static inline void got_bad_result(SCHED_RESULT_ITEM& sri) {
int gavid = generalized_app_version_id(sri.app_version_id, sri.appid);
DB_HOST_APP_VERSION* havp = gavid_to_havp(gavid);
@@ -388,9 +393,12 @@ int handle_results() {
srip->outcome = RESULT_OUTCOME_CLIENT_ERROR;
srip->validate_state = VALIDATE_STATE_INVALID;
- // adjust quota and reset error rate
+ // adjust quota and reset consecutive valid
+ // (but not if aborted by project)
//
- got_bad_result(*srip);
+ if (srip->exit_status != EXIT_ABORTED_BY_PROJECT) {
+ got_bad_result(*srip);
+ }
}
} // loop over all incoming results
diff --git a/sched/sched_score.cpp b/sched/sched_score.cpp
index d89cd42..ce4668b 100644
--- a/sched/sched_score.cpp
+++ b/sched/sched_score.cpp
@@ -77,7 +77,7 @@ bool JOB::get_score(WU_RESULT& wu_result) {
}
}
- if (app_not_selected(wu_result.workunit)) {
+ if (app_not_selected(app->id)) {
if (g_wreq->allow_non_preferred_apps) {
score -= 1;
} else {
diff --git a/sched/sched_send.cpp b/sched/sched_send.cpp
index cbf0386..5961461 100644
--- a/sched/sched_send.cpp
+++ b/sched/sched_send.cpp
@@ -46,6 +46,7 @@
#include "sched_locality.h"
#include "sched_main.h"
#include "sched_msgs.h"
+#include "sched_nci.h"
#include "sched_shmem.h"
#include "sched_score.h"
#include "sched_timezone.h"
@@ -364,7 +365,7 @@ double max_allowable_disk() {
// Compute the max allowable additional disk usage based on prefs
//
x1 = prefs.disk_max_used_gb*GIGA - host.d_boinc_used_total;
- x2 = host.d_total*prefs.disk_max_used_pct/100.
+ x2 = host.d_total * prefs.disk_max_used_pct / 100.0
- host.d_boinc_used_total;
x3 = host.d_free - prefs.disk_min_free_gb*GIGA; // may be negative
x = std::min(x1, std::min(x2, x3));
@@ -394,9 +395,9 @@ double max_allowable_disk() {
if (config.debug_send) {
log_messages.printf(MSG_NORMAL,
"[send] No disk space available: disk_max_used_gb %.2fGB disk_max_used_pct %.2f disk_min_free_gb %.2fGB\n",
- prefs.disk_max_used_gb/GIGA,
+ prefs.disk_max_used_gb,
prefs.disk_max_used_pct,
- prefs.disk_min_free_gb/GIGA
+ prefs.disk_min_free_gb
);
log_messages.printf(MSG_NORMAL,
"[send] No disk space available: host.d_total %.2fGB host.d_free %.2fGB host.d_boinc_used_total %.2fGB\n",
@@ -773,10 +774,34 @@ bool work_needed(bool locality_sched) {
// if we've failed to send a result because of a transient condition,
// return false to preserve invariant
//
- if (g_wreq->disk.insufficient || g_wreq->speed.insufficient || g_wreq->mem.insufficient || g_wreq->no_allowed_apps_available) {
+ if (g_wreq->disk.insufficient) {
if (config.debug_send) {
log_messages.printf(MSG_NORMAL,
- "[send] stopping work search - locality condition\n"
+ "[send] stopping work search - insufficient disk space\n"
+ );
+ }
+ return false;
+ }
+ if (g_wreq->speed.insufficient) {
+ if (config.debug_send) {
+ log_messages.printf(MSG_NORMAL,
+ "[send] stopping work search - host too slow\n"
+ );
+ }
+ return false;
+ }
+ if (g_wreq->mem.insufficient) {
+ if (config.debug_send) {
+ log_messages.printf(MSG_NORMAL,
+ "[send] stopping work search - insufficient memory\n"
+ );
+ }
+ return false;
+ }
+ if (g_wreq->no_allowed_apps_available) {
+ if (config.debug_send) {
+ log_messages.printf(MSG_NORMAL,
+ "[send] stopping work search - no locality app selected\n"
);
}
return false;
@@ -966,9 +991,13 @@ int add_result_to_reply(
double est_dur = estimate_duration(wu, *bavp);
if (config.debug_send) {
+ double max_time = wu.rsc_fpops_bound / bavp->host_usage.projected_flops;
+ char buf1[64],buf2[64];
+ secs_to_hmsf(est_dur, buf1);
+ secs_to_hmsf(max_time, buf2);
log_messages.printf(MSG_NORMAL,
- "[send] [HOST#%d] sending [RESULT#%u %s] (est. dur. %.2f seconds)\n",
- g_reply->host.id, result.id, result.name, est_dur
+ "[send] [HOST#%d] sending [RESULT#%d %s] (est. dur. %.2fs (%s)) (max time %.2fs (%s))\n",
+ g_reply->host.id, result.id, result.name, est_dur, buf1, max_time, buf2
);
}
@@ -1632,18 +1661,51 @@ void send_work() {
);
}
send_work_locality();
+
+ // save 'insufficient' flags from the first scheduler
+ bool disk_insufficient = g_wreq->disk.insufficient;
+ bool speed_insufficient = g_wreq->speed.insufficient;
+ bool mem_insufficient = g_wreq->mem.insufficient;
+ bool no_allowed_apps_available = g_wreq->no_allowed_apps_available;
+
+ // reset 'insufficient' flags for the second scheduler
+ g_wreq->disk.insufficient = false;
+ g_wreq->speed.insufficient = false;
+ g_wreq->mem.insufficient = false;
+ g_wreq->no_allowed_apps_available = false;
+
if (config.debug_locality) {
log_messages.printf(MSG_NORMAL,
"[mixed] sending non-locality work second\n"
);
}
send_work_old();
+
+ // recombine the 'insufficient' flags from the two schedulers
+ g_wreq->disk.insufficient = g_wreq->disk.insufficient && disk_insufficient;
+ g_wreq->speed.insufficient = g_wreq->speed.insufficient && speed_insufficient;
+ g_wreq->mem.insufficient = g_wreq->mem.insufficient && mem_insufficient;
+ g_wreq->no_allowed_apps_available = g_wreq->no_allowed_apps_available && no_allowed_apps_available;
+
} else {
if (config.debug_locality) {
log_messages.printf(MSG_NORMAL,
"[mixed] sending non-locality work first\n"
);
}
+
+ // save 'insufficient' flags from the first scheduler
+ bool disk_insufficient = g_wreq->disk.insufficient;
+ bool speed_insufficient = g_wreq->speed.insufficient;
+ bool mem_insufficient = g_wreq->mem.insufficient;
+ bool no_allowed_apps_available = g_wreq->no_allowed_apps_available;
+
+ // reset 'insufficient' flags for the second scheduler
+ g_wreq->disk.insufficient = false;
+ g_wreq->speed.insufficient = false;
+ g_wreq->mem.insufficient = false;
+ g_wreq->no_allowed_apps_available = false;
+
send_work_old();
if (config.debug_locality) {
log_messages.printf(MSG_NORMAL,
@@ -1651,6 +1713,13 @@ void send_work() {
);
}
send_work_locality();
+
+ // recombine the 'insufficient' flags from the two schedulers
+ g_wreq->disk.insufficient = g_wreq->disk.insufficient && disk_insufficient;
+ g_wreq->speed.insufficient = g_wreq->speed.insufficient && speed_insufficient;
+ g_wreq->mem.insufficient = g_wreq->mem.insufficient && mem_insufficient;
+ g_wreq->no_allowed_apps_available = g_wreq->no_allowed_apps_available && no_allowed_apps_available;
+
}
} else if (config.locality_scheduling) {
send_work_locality();
diff --git a/sched/sched_send.h b/sched/sched_send.h
index fa797ea..eebeca5 100644
--- a/sched/sched_send.h
+++ b/sched/sched_send.h
@@ -53,7 +53,6 @@ extern int update_wu_on_send(WORKUNIT wu, time_t x, APP&, BEST_APP_VERSION&);
extern void lock_sema();
extern void unlock_sema();
extern const char* find_user_friendly_name(int appid);
-extern bool app_not_selected(WORKUNIT&);
extern bool work_needed(bool);
extern void send_work_setup();
extern int effective_ncpus();
diff --git a/sched/sched_version.cpp b/sched/sched_version.cpp
index cd66e9e..f095c3d 100644
--- a/sched/sched_version.cpp
+++ b/sched/sched_version.cpp
@@ -533,6 +533,10 @@ BEST_APP_VERSION* get_app_version(
bool job_needs_64b = (wu.rsc_memory_bound > max_32b_address_space());
if (config.debug_version_select) {
+ log_messages.printf(MSG_NORMAL,
+ "[version] get_app_version(): getting app version for WU#%d (%s) appid:%d\n",
+ wu.id, wu.name, wu.appid
+ );
if (job_needs_64b) {
log_messages.printf(MSG_NORMAL,
"[version] job needs 64-bit app version: mem bnd %f\n",
@@ -681,6 +685,11 @@ BEST_APP_VERSION* get_app_version(
APP_VERSION& av = ssp->app_versions[j];
if (av.appid != wu.appid) continue;
if (av.platformid != p->id) continue;
+ if (av.beta) {
+ if (!g_wreq->allow_beta_work) {
+ continue;
+ }
+ }
if (strlen(av.plan_class)) {
if (!app_plan(*g_request, av.plan_class, host_usage)) {
diff --git a/sched/start b/sched/start
index 962b385..3179bf2 100755
--- a/sched/start
+++ b/sched/start
@@ -79,7 +79,7 @@ Both:
import boinc_path_config
from Boinc import boinc_project_path, configxml
-import sys, os, getopt, time, glob, fcntl, signal, socket
+import sys, os, getopt, time, glob, fcntl, signal, socket, getpass
right_now = int(time.time())
verbose = os.isatty(sys.stdout.fileno())
@@ -161,6 +161,9 @@ def get_daemon_output_name(task):
return os.path.join(log_dir,
task.__dict__.get('output') or get_task_command_basename(task) + '.log')
+def get_daemon_silent_start(task):
+ return task.__dict__.get('silent_start') or 0
+
def get_daemon_pid_name(task):
return os.path.join(pid_dir,
task.__dict__.get('pid_file') or get_task_command_basename(task) + '.pid')
@@ -404,7 +407,7 @@ def run_daemon(task):
if verbose:
print >>sys.stderr, " Daemon already running:",task.cmd
sys.exit(0)
- if verbose or verbose_daemon_run:
+ if verbose or ( verbose_daemon_run and not get_daemon_silent_start(task) ):
print " Starting daemon:", task.cmd
sys.stdout.flush()
redirect(get_daemon_output_name(task))
@@ -416,8 +419,7 @@ def run_daemon(task):
def run_daemons():
found_any = False
if verbose: print "Starting daemons"
- if is_main_host:
- remove_stop_daemons()
+ remove_stop_daemons()
for task in config.daemons:
if task.host != local_hostname:
continue
@@ -706,6 +708,12 @@ if not command:
raise SystemExit('No command specified and script name is not "start", "stop", or "status"')
config = configxml.ConfigFile(config_filename).read()
+
+# if a <project_user_name> is configured, terminate script if not ran by this user
+if 'project_user_name' in config.config.__dict__:
+ if getpass.getuser() != config.config.project_user_name:
+ raise SystemExit('The script must be run by user "' + config.config.project_user_name + '"')
+
run_state = configxml.RunStateFile(run_state_filename).read(failopen_ok = True)
if 'ssh_exec' in config.config.__dict__:
diff --git a/sched/transitioner.cpp b/sched/transitioner.cpp
index afea870..55591ac 100644
--- a/sched/transitioner.cpp
+++ b/sched/transitioner.cpp
@@ -296,6 +296,14 @@ int handle_wu(
}
break;
case RESULT_OUTCOME_CLIENT_ERROR:
+ // is user aborted job, don't count it as an error
+ //
+ if (res_item.res_exit_status == EXIT_ABORTED_VIA_GUI) {
+ nno_reply++;
+ } else {
+ nerrors++;
+ }
+ break;
case RESULT_OUTCOME_VALIDATE_ERROR:
nerrors++;
break;
diff --git a/sched/validator.cpp b/sched/validator.cpp
index ed77393..5915e73 100644
--- a/sched/validator.cpp
+++ b/sched/validator.cpp
@@ -92,6 +92,8 @@ char app_name[256];
DB_APP app;
int wu_id_modulus=0;
int wu_id_remainder=0;
+int wu_id_min=0;
+int wu_id_max=0;
int one_pass_N_WU=0;
bool one_pass = false;
double max_granted_credit = 200 * 1000 * 365;
@@ -101,6 +103,7 @@ bool credit_from_wu = false;
bool credit_from_runtime = false;
double max_runtime = 0;
bool no_credit = false;
+bool dry_run = false;
int g_argc;
char **g_argv;
@@ -144,17 +147,21 @@ int is_valid(
if (update_credited_job) {
credited_job.userid = host.userid;
credited_job.workunitid = long(wu.opaque);
- retval = credited_job.insert();
- if (retval) {
- log_messages.printf(MSG_CRITICAL,
- "[RESULT#%u] Warning: credited_job insert failed (userid: %d workunit: %f err: %s)\n",
- result.id, host.userid, wu.opaque, boincerror(retval)
- );
+ if (dry_run) {
+ log_messages.printf(MSG_NORMAL, "DB not updated (dry run)\n");
} else {
- log_messages.printf(MSG_DEBUG,
- "[RESULT#%u %s] added credited_job record [WU#%u OPAQUE#%f USER#%d]\n",
- result.id, result.name, wu.id, wu.opaque, host.userid
- );
+ retval = credited_job.insert();
+ if (retval) {
+ log_messages.printf(MSG_CRITICAL,
+ "[RESULT#%u] Warning: credited_job insert failed (userid: %d workunit: %f err: %s)\n",
+ result.id, host.userid, wu.opaque, boincerror(retval)
+ );
+ } else {
+ log_messages.printf(MSG_DEBUG,
+ "[RESULT#%u %s] added credited_job record [WU#%u OPAQUE#%f USER#%d]\n",
+ result.id, result.name, wu.id, wu.opaque, host.userid
+ );
+ }
}
}
@@ -313,15 +320,21 @@ int handle_wu(
is_invalid(havv[0]);
}
if (hav.host_id && update_hav) {
- log_messages.printf(MSG_NORMAL,
- "[HOST#%d AV#%d] [outlier=%d] Updating HAV in db. pfc.n=%f->%f\n",
- havv[0].host_id, havv[0].app_version_id, result.runtime_outlier, hav_orig.pfc.n,havv[0].pfc.n);
- retval=havv[0].update_validator(hav_orig);
- if (retval) {
- log_messages.printf(MSG_CRITICAL,
- "[HOST#%d AV%d] hav.update_validator() failed: %s\n",
- hav.host_id, hav.app_version_id, boincerror(retval)
+ if (dry_run) {
+ log_messages.printf(MSG_NORMAL, "DB not updated (dry run)\n");
+ } else {
+ log_messages.printf(MSG_NORMAL,
+ "[HOST#%d AV#%d] [outlier=%d] Updating HAV in DB. pfc.n=%f->%f\n",
+ havv[0].host_id, havv[0].app_version_id,
+ result.runtime_outlier, hav_orig.pfc.n, havv[0].pfc.n
);
+ retval=havv[0].update_validator(hav_orig);
+ if (retval) {
+ log_messages.printf(MSG_CRITICAL,
+ "[HOST#%d AV%d] hav.update_validator() failed: %s\n",
+ hav.host_id, hav.app_version_id, boincerror(retval)
+ );
+ }
}
}
host.update_diff_validator(host_initial);
@@ -330,13 +343,16 @@ int handle_wu(
"[RESULT#%u %s] granted_credit %f\n",
result.id, result.name, result.granted_credit
);
-
- retval = validator.update_result(result);
- if (retval) {
- log_messages.printf(MSG_CRITICAL,
- "[RESULT#%u %s] Can't update result: %s\n",
- result.id, result.name, boincerror(retval)
- );
+ if (dry_run) {
+ log_messages.printf(MSG_NORMAL, "DB not updated (dry run)\n");
+ } else {
+ retval = validator.update_result(result);
+ if (retval) {
+ log_messages.printf(MSG_CRITICAL,
+ "[RESULT#%u %s] Can't update result: %s\n",
+ result.id, result.name, boincerror(retval)
+ );
+ }
}
}
}
@@ -539,28 +555,34 @@ int handle_wu(
break;
}
- if (hav.host_id) {
- log_messages.printf(MSG_NORMAL,
- "[HOST#%d AV#%d] [outlier=%d] Updating HAV in db. pfc.n=%f->%f\n",
- hav.host_id, hav.app_version_id, result.runtime_outlier, hav_orig.pfc.n,hav.pfc.n);
- retval = hav.update_validator(hav_orig);
- if (retval) {
- log_messages.printf(MSG_CRITICAL,
- "[HOST#%d AV%d] hav.update_validator() failed: %s\n",
- hav.host_id, hav.app_version_id, boincerror(retval)
+ if (dry_run) {
+ log_messages.printf(MSG_NORMAL, "DB not updated (dry run)\n");
+ } else {
+ if (hav.host_id) {
+ log_messages.printf(MSG_NORMAL,
+ "[HOST#%d AV#%d] [outlier=%d] Updating HAV in DB. pfc.n=%f->%f\n",
+ hav.host_id, hav.app_version_id,
+ result.runtime_outlier, hav_orig.pfc.n, hav.pfc.n
);
+ retval = hav.update_validator(hav_orig);
+ if (retval) {
+ log_messages.printf(MSG_CRITICAL,
+ "[HOST#%d AV%d] hav.update_validator() failed: %s\n",
+ hav.host_id, hav.app_version_id, boincerror(retval)
+ );
+ }
}
- }
- if (update_host) {
- retval = host.update_diff_validator(host_initial);
- }
- if (update_result) {
- retval = validator.update_result(result);
- if (retval) {
- log_messages.printf(MSG_CRITICAL,
- "[RESULT#%u %s] result.update() failed: %s\n",
- result.id, result.name, boincerror(retval)
- );
+ if (update_host) {
+ retval = host.update_diff_validator(host_initial);
+ }
+ if (update_result) {
+ retval = validator.update_result(result);
+ if (retval) {
+ log_messages.printf(MSG_CRITICAL,
+ "[RESULT#%u %s] result.update() failed: %s\n",
+ result.id, result.name, boincerror(retval)
+ );
+ }
}
}
}
@@ -590,12 +612,16 @@ int handle_wu(
result.server_state = RESULT_SERVER_STATE_OVER;
result.outcome = RESULT_OUTCOME_DIDNT_NEED;
- retval = validator.update_result(result);
- if (retval) {
- log_messages.printf(MSG_CRITICAL,
- "[RESULT#%u %s] result.update() failed: %s\n",
- result.id, result.name, boincerror(retval)
- );
+ if (dry_run) {
+ log_messages.printf(MSG_NORMAL, "DB not updated (dry run)\n");
+ } else {
+ retval = validator.update_result(result);
+ if (retval) {
+ log_messages.printf(MSG_CRITICAL,
+ "[RESULT#%u %s] result.update() failed: %s\n",
+ result.id, result.name, boincerror(retval)
+ );
+ }
}
}
} else {
@@ -641,13 +667,17 @@ leave:
wu.need_validate = 0;
- retval = validator.update_workunit(wu);
- if (retval) {
- log_messages.printf(MSG_CRITICAL,
- "[WU#%u %s] update_workunit() failed: %s\n",
- wu.id, wu.name, boincerror(retval)
- );
- return retval;
+ if (dry_run) {
+ log_messages.printf(MSG_NORMAL, "DB not updated (dry run)\n");
+ } else {
+ retval = validator.update_workunit(wu);
+ if (retval) {
+ log_messages.printf(MSG_CRITICAL,
+ "[WU#%u %s] update_workunit() failed: %s\n",
+ wu.id, wu.name, boincerror(retval)
+ );
+ return retval;
+ }
}
return 0;
}
@@ -665,7 +695,8 @@ bool do_validate_scan() {
//
while (1) {
retval = validator.enumerate(
- app.id, SELECT_LIMIT, wu_id_modulus, wu_id_remainder, items
+ app.id, SELECT_LIMIT, wu_id_modulus, wu_id_remainder,
+ wu_id_min, wu_id_max, items
);
if (retval) {
if (retval != ERR_DB_NOT_FOUND) {
@@ -715,6 +746,7 @@ int main_loop() {
daemon_sleep(sleep_interval);
#endif
}
+ if (one_pass) break;
}
return 0;
}
@@ -732,7 +764,10 @@ int main(int argc, char** argv) {
"Optional arguments:\n"
" --one_pass_N_WU N Validate at most N WUs, then exit\n"
" --one_pass Make one pass through WU table, then exit\n"
+ " --dry_run Don't update db, just write logs (for debugging)\n"
" --mod n i Process only WUs with (id mod n) == i\n"
+ " --max_wu_id n Process only WUs with id <= n\n"
+ " --min_wu_id n Process only WUs with id >= n\n"
" --max_granted_credit X Grant no more than this amount of credit to a result\n"
" --update_credited_job Add record to credited_job table after granting credit\n"
" --credit_from_wu Credit is specified in WU XML\n"
@@ -761,6 +796,8 @@ int main(int argc, char** argv) {
one_pass = true;
} else if (is_arg(argv[i], "sleep_interval")) {
sleep_interval = atoi(argv[++i]);
+ } else if (is_arg(argv[i], "dry_run")) {
+ dry_run = true;
} else if (is_arg(argv[i], "one_pass")) {
one_pass = true;
} else if (is_arg(argv[i], "app")) {
@@ -772,6 +809,10 @@ int main(int argc, char** argv) {
} else if (is_arg(argv[i], "mod")) {
wu_id_modulus = atoi(argv[++i]);
wu_id_remainder = atoi(argv[++i]);
+ } else if (is_arg(argv[i], "min_wu_id")) {
+ wu_id_min = atoi(argv[++i]);
+ } else if (is_arg(argv[i], "max_wu_id")) {
+ wu_id_max = atoi(argv[++i]);
} else if (is_arg(argv[i], "max_granted_credit")) {
max_granted_credit = atof(argv[++i]);
} else if (is_arg(argv[i], "update_credited_job")) {
@@ -831,6 +872,16 @@ int main(int argc, char** argv) {
"Modulus %d, remainder %d\n", wu_id_modulus, wu_id_remainder
);
}
+ if (wu_id_min) {
+ log_messages.printf(MSG_NORMAL,
+ "min wu id %d\n", wu_id_min
+ );
+ }
+ if (wu_id_max) {
+ log_messages.printf(MSG_NORMAL,
+ "max wu id %d\n", wu_id_max
+ );
+ }
install_stop_signal_handler();
diff --git a/tools/backend_lib.cpp b/tools/backend_lib.cpp
index 562fdc9..9369906 100644
--- a/tools/backend_lib.cpp
+++ b/tools/backend_lib.cpp
@@ -346,7 +346,7 @@ int create_work2(
fprintf(stderr, "target_nresults > max_success_results; can't create job\n");
return ERR_INVALID_PARAM;
}
- if (wu.transitioner_flags & TRANSITION_NONE) {
+ if (wu.transitioner_flags) {
wu.transition_time = INT_MAX;
} else {
wu.transition_time = time(0);
diff --git a/tools/create_work.cpp b/tools/create_work.cpp
index fc3a127..bf8ec8e 100644
--- a/tools/create_work.cpp
+++ b/tools/create_work.cpp
@@ -158,6 +158,15 @@ void JOB_DESC::parse_cmdline(int argc, char** argv) {
}
}
+void check_assign_id(int x) {
+ if (x == 0) {
+ fprintf(stderr,
+ "you must specify a nonzero database ID for assigning jobs to users, teams, or hosts.\n"
+ );
+ exit(1);
+ }
+}
+
int main(int argc, char** argv) {
DB_APP app;
int retval;
@@ -235,23 +244,28 @@ int main(int argc, char** argv) {
jd.assign_type = ASSIGN_USER;
jd.assign_multi = true;
jd.assign_id = atoi(argv[++i]);
+ check_assign_id(jd.assign_id);
} else if (arg(argv, i, "broadcast_team")) {
jd.assign_flag = true;
jd.assign_type = ASSIGN_TEAM;
jd.assign_multi = true;
jd.assign_id = atoi(argv[++i]);
+ check_assign_id(jd.assign_id);
} else if (arg(argv, i, "target_host")) {
jd.assign_flag = true;
jd.assign_type = ASSIGN_HOST;
jd.assign_id = atoi(argv[++i]);
+ check_assign_id(jd.assign_id);
} else if (arg(argv, i, "target_user")) {
jd.assign_flag = true;
jd.assign_type = ASSIGN_USER;
jd.assign_id = atoi(argv[++i]);
+ check_assign_id(jd.assign_id);
} else if (arg(argv, i, "target_team")) {
jd.assign_flag = true;
jd.assign_type = ASSIGN_TEAM;
jd.assign_id = atoi(argv[++i]);
+ check_assign_id(jd.assign_id);
} else if (arg(argv, i, "help")) {
usage();
exit(0);
@@ -420,7 +434,9 @@ int main(int argc, char** argv) {
}
void JOB_DESC::create() {
- char buf[256];
+ if (assign_flag) {
+ wu.transitioner_flags = assign_multi?TRANSITION_NONE:TRANSITION_NO_NEW_RESULTS;
+ }
int retval = create_work2(
wu,
wu_template,
@@ -450,14 +466,6 @@ void JOB_DESC::create() {
);
exit(1);
}
- sprintf(buf, "transitioner_flags=%d",
- assign_multi?TRANSITION_NONE:TRANSITION_NO_NEW_RESULTS
- );
- retval = wu.update_field(buf);
- if (retval) {
- fprintf(stderr, "wu.update() failed: %s\n", boincerror(retval));
- exit(1);
- }
}
}
diff --git a/tools/update_versions b/tools/update_versions
index f983727..eaf1f09 100755
--- a/tools/update_versions
+++ b/tools/update_versions
@@ -391,6 +391,7 @@ function convert_simplexml($x) {
$v->needs_network = get_bool($x, "needs_network");
$v->is_wrapper = get_bool($x, "is_wrapper");
$v->file_prefix = (string)$x->file_prefix;
+ $v->beta = get_bool($x, "beta");
return $v;
}
@@ -415,6 +416,7 @@ function process_version($a, $v, $p) {
$vers->dont_throttle = false;
$vers->needs_network = false;
$vers->is_wrapper = false;
+ $vers->beta = false;
$vers->file_prefix = false;
}
@@ -463,7 +465,8 @@ function process_version($a, $v, $p) {
$now = time();
$vnum = parse_version($v);
$plat = lookup_platform($platform);
- $query = "set create_time=$now, appid=$app->id, version_num=$vnum, platformid=$plat->id , xml_doc='$xml', plan_class='$plan_class'";
+ $b = $vers->beta?1:0;
+ $query = "set create_time=$now, appid=$app->id, version_num=$vnum, platformid=$plat->id , xml_doc='$xml', plan_class='$plan_class', beta=$b";
$id = BoincAppVersion::insert($query);
if ($id) {
diff --git a/version.h.in b/version.h.in
index f0b957d..feefc44 100644
--- a/version.h.in
+++ b/version.h.in
@@ -12,6 +12,12 @@
/* Release part of BOINC version number */
#define BOINC_RELEASE @BOINC_RELEASE@
+/* Release part of wrapper version number */
+#define WRAPPER_RELEASE @WRAPPER_RELEASE@
+
+/* Release part of vboxwrapper version number */
+#define VBOXWRAPPER_RELEASE @VBOXWRAPPER_RELEASE@
+
/* String representation of BOINC version number */
#define BOINC_VERSION_STRING "@BOINC_VERSION_STRING@"
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-boinc/boinc.git
More information about the pkg-boinc-commits
mailing list