2019-11-08 11:40:22 +00:00
|
|
|
// This file is part of BOINC.
|
|
|
|
// http://boinc.berkeley.edu
|
2022-04-06 08:26:49 +00:00
|
|
|
// Copyright (C) 2022 University of California
|
2019-11-08 11:40:22 +00:00
|
|
|
//
|
|
|
|
// 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/>.
|
|
|
|
//
|
|
|
|
// gfx_cleanup.mm
|
|
|
|
//
|
|
|
|
// Used by screensaver to work around a bug in OS 10.15 Catalina
|
|
|
|
// - Detects when ScreensaverEngine exits without calling [ScreenSaverView stopAnimation]
|
|
|
|
// - If that happens, it sends RPC to BOINC client to kill current graphics app.
|
|
|
|
// Note: this can rarely happen in earlier versions, but is of main concern under
|
|
|
|
// OS 10.13 and later, where it can cause an ugly white full-screen display
|
|
|
|
//
|
|
|
|
// Called by CScreensaver via popen(path, "w")
|
|
|
|
//
|
|
|
|
|
|
|
|
#import <Cocoa/Cocoa.h>
|
2020-04-23 14:18:54 +00:00
|
|
|
#include <SystemConfiguration/SystemConfiguration.h>
|
2019-11-08 11:40:22 +00:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
|
|
|
|
#include "gui_rpc_client.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "mac_util.h"
|
2022-11-10 13:30:28 +00:00
|
|
|
#include "shmem.h"
|
2019-11-08 11:40:22 +00:00
|
|
|
|
|
|
|
#define CREATE_LOG 0
|
|
|
|
#define USE_TIMER 0
|
|
|
|
|
|
|
|
#if CREATE_LOG
|
|
|
|
void print_to_log_file(const char *format, ...);
|
2020-04-23 14:18:54 +00:00
|
|
|
#else
|
|
|
|
#define print_to_log_file(...)
|
2019-11-08 11:40:22 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
pid_t parentPid;
|
|
|
|
int GFX_PidFromScreensaver = 0;
|
|
|
|
pthread_t MonitorParentThread = 0;
|
|
|
|
bool quit_MonitorParentThread = false;
|
|
|
|
|
2022-11-10 13:30:28 +00:00
|
|
|
struct ss_shmem_data {
|
|
|
|
pid_t gfx_pid;
|
|
|
|
int gfx_slot;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct ss_shmem_data* ss_shmem = NULL;
|
|
|
|
|
2019-11-08 11:40:22 +00:00
|
|
|
#if USE_TIMER
|
|
|
|
time_t startTime = 0;
|
|
|
|
time_t endTime = 0;
|
|
|
|
time_t elapsedTime = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void killGfxApp(pid_t thePID) {
|
2020-04-23 14:18:54 +00:00
|
|
|
#if 0
|
|
|
|
print_to_log_file("in gfx_cleanup: killGfxApp()");
|
|
|
|
kill(thePID, SIGKILL);
|
|
|
|
#else
|
|
|
|
char buf[256];
|
|
|
|
char userName[64];
|
2019-11-08 11:40:22 +00:00
|
|
|
RPC_CLIENT *rpc;
|
|
|
|
int retval;
|
2020-05-16 05:21:00 +00:00
|
|
|
std::string msg;
|
2019-11-08 11:40:22 +00:00
|
|
|
|
|
|
|
chdir("/Library/Application Support/BOINC Data");
|
2020-04-23 14:18:54 +00:00
|
|
|
safe_strcpy(buf, "");
|
2020-05-16 04:02:15 +00:00
|
|
|
read_gui_rpc_password(buf, msg);
|
2019-11-08 11:40:22 +00:00
|
|
|
|
|
|
|
rpc = new RPC_CLIENT;
|
|
|
|
if (rpc->init(NULL)) { // Initialize communications with Core Client
|
|
|
|
fprintf(stderr, "in gfx_cleanup: killGfxApp(): rpc->init(NULL) failed");
|
|
|
|
return;
|
|
|
|
}
|
2020-04-23 14:18:54 +00:00
|
|
|
if (strlen(buf)) {
|
|
|
|
retval = rpc->authorize(buf);
|
2019-11-08 11:40:22 +00:00
|
|
|
if (retval) {
|
|
|
|
fprintf(stderr, "in gfx_cleanup: killGfxApp(): authorization failure: %d\n", retval);
|
|
|
|
rpc->close();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-23 14:18:54 +00:00
|
|
|
CFStringRef cf_gUserName = SCDynamicStoreCopyConsoleUser(NULL, NULL, NULL);
|
|
|
|
CFStringGetCString(cf_gUserName, userName, sizeof(userName), kCFStringEncodingUTF8);
|
|
|
|
|
|
|
|
retval = rpc->run_graphics_app("stop", thePID, userName);
|
|
|
|
print_to_log_file("in gfx_cleanup: killGfxApp(): rpc->run_graphics_app(stop) returned retval=%d", retval);
|
|
|
|
|
|
|
|
// Wait until graphics app has exited before closing our own black fullscreen
|
|
|
|
// window to prevent an ugly white flash [see comment in main() below].
|
|
|
|
int i;
|
|
|
|
pid_t p = 0;
|
|
|
|
for (i=0; i<100; ++i) {
|
|
|
|
boinc_sleep(0.1);
|
|
|
|
p = thePID;
|
|
|
|
// On OS 10.15+ (Catalina), it might be more efficient to get this from shared memory
|
|
|
|
retval = rpc->run_graphics_app("test", p, userName);
|
|
|
|
if (retval || (p==0)) break;
|
|
|
|
}
|
|
|
|
print_to_log_file("in gfx_cleanup: killGfxApp(%d): rpc->run_graphics_app(test) returned pid %d, retval %d when i = %d", thePID, p, retval, i);
|
|
|
|
|
2022-11-10 13:30:28 +00:00
|
|
|
// Graphics apps called by screensaver or Manager (via Show
|
|
|
|
// Graphics button) now write files in their slot directory as
|
|
|
|
// the logged in user, not boinc_master. This ugly hack tells
|
|
|
|
// BOINC client to fix all ownerships in this slot directory
|
|
|
|
char shmem_name[MAXPATHLEN];
|
|
|
|
snprintf(shmem_name, sizeof(shmem_name), "/tmp/boinc_ss_%s", userName);
|
|
|
|
retval = attach_shmem_mmap(shmem_name, (void**)&ss_shmem);
|
|
|
|
if (ss_shmem) {
|
|
|
|
rpc->run_graphics_app("stop", ss_shmem->gfx_slot, "");
|
|
|
|
ss_shmem->gfx_slot = -1;
|
|
|
|
}
|
|
|
|
|
2019-11-08 11:40:22 +00:00
|
|
|
rpc->close();
|
2020-04-23 14:18:54 +00:00
|
|
|
#endif
|
|
|
|
return;
|
2019-11-08 11:40:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void * MonitorParent(void* param) {
|
2020-04-23 14:18:54 +00:00
|
|
|
print_to_log_file("in gfx_cleanup: Starting MonitorParent");
|
2019-11-08 11:40:22 +00:00
|
|
|
while (true) {
|
|
|
|
boinc_sleep(0.25); // Test every 1/4 second
|
|
|
|
if (getppid() != parentPid) {
|
|
|
|
if (GFX_PidFromScreensaver) {
|
|
|
|
killGfxApp(GFX_PidFromScreensaver);
|
|
|
|
}
|
|
|
|
if (quit_MonitorParentThread) {
|
|
|
|
return 0;
|
|
|
|
}
|
2020-04-23 14:18:54 +00:00
|
|
|
print_to_log_file("in gfx_cleanup: parent died, exiting (child) after handling %d",GFX_PidFromScreensaver);
|
|
|
|
#if USE_TIMER
|
|
|
|
endTime = time(NULL);
|
|
|
|
elapsedTime = endTime - startTime;
|
|
|
|
print_to_log_file("elapsed time=%d", (int) elapsedTime);
|
|
|
|
#endif
|
2019-11-08 11:40:22 +00:00
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
NSWindow* myWindow;
|
|
|
|
|
|
|
|
@interface AppDelegate : NSObject <NSApplicationDelegate, NSUserNotificationCenterDelegate>
|
|
|
|
{
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@implementation AppDelegate
|
|
|
|
|
|
|
|
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
|
|
|
{
|
2022-04-06 08:26:49 +00:00
|
|
|
int retval __attribute__((unused)) = 0;
|
2019-11-08 11:40:22 +00:00
|
|
|
char buf[256];
|
|
|
|
|
|
|
|
retval = pthread_create(&MonitorParentThread, NULL, MonitorParent, 0);
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
fgets(buf, sizeof(buf), stdin);
|
2020-04-23 14:18:54 +00:00
|
|
|
print_to_log_file("in gfx_cleanup: parent sent %d to child buf=%s", GFX_PidFromScreensaver, buf);
|
2019-11-08 11:40:22 +00:00
|
|
|
if (feof(stdin)) {
|
2020-04-23 14:18:54 +00:00
|
|
|
print_to_log_file("in gfx_cleanup: got eof");
|
2019-11-08 11:40:22 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ferror(stdin) && (errno != EINTR)) {
|
|
|
|
fprintf(stderr, "in gfx_cleanup: fgets got error %d %s", errno, strerror(errno));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(buf, "Quit\n")) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
GFX_PidFromScreensaver = atoi(buf);
|
2020-04-23 14:18:54 +00:00
|
|
|
print_to_log_file("in gfx_cleanup: parent sent %d to child buf=%s", GFX_PidFromScreensaver, buf);
|
2019-11-08 11:40:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (GFX_PidFromScreensaver) {
|
|
|
|
killGfxApp(GFX_PidFromScreensaver);
|
|
|
|
}
|
|
|
|
|
|
|
|
quit_MonitorParentThread = true;
|
|
|
|
|
|
|
|
// [NSApp stop:self];
|
|
|
|
exit(0);
|
|
|
|
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
int main(int argc, char* argv[]) {
|
2020-04-23 14:18:54 +00:00
|
|
|
print_to_log_file("Entered gfx_cleanup");
|
2019-11-08 11:40:22 +00:00
|
|
|
#if USE_TIMER
|
|
|
|
startTime = time(NULL);
|
|
|
|
#endif
|
|
|
|
parentPid = getppid();
|
|
|
|
|
|
|
|
bool cover_gfx_window = (compareOSVersionTo(10, 13) >= 0);
|
|
|
|
|
|
|
|
// Create shared app instance
|
|
|
|
[NSApplication sharedApplication];
|
|
|
|
|
2020-04-23 14:18:54 +00:00
|
|
|
// Because prpject graphics applications under OS 10.13+ draw to an IOSurface,
|
2019-11-08 11:40:22 +00:00
|
|
|
// the application's own window is white, but is normally covered by the
|
|
|
|
// ScreensaverEngine's window. If the ScreensaverEngine exits without first
|
|
|
|
// calling [ScreenSaverView stopAnimation], the white fullscreen window will
|
|
|
|
// briefly be visible until we kill the graphics app, causing an ugly and
|
|
|
|
// annoying white flash. So we hide that with our own black fullscreen window
|
|
|
|
// to prevent the white flash.
|
|
|
|
if (cover_gfx_window) {
|
|
|
|
NSArray *allScreens = [NSScreen screens];
|
|
|
|
NSRect windowRect = [ allScreens[0] frame ];
|
|
|
|
NSUInteger windowStyle = NSWindowStyleMaskBorderless;
|
|
|
|
NSWindow *myWindow = [[NSWindow alloc] initWithContentRect:windowRect
|
|
|
|
styleMask:windowStyle
|
|
|
|
backing:NSBackingStoreBuffered
|
|
|
|
defer:NO];
|
|
|
|
[myWindow setBackgroundColor:[NSColor blackColor]];
|
|
|
|
[ myWindow setLevel:kCGOverlayWindowLevel ]; // slightly above the graphics app's window
|
|
|
|
[myWindow orderFrontRegardless];
|
|
|
|
}
|
|
|
|
|
|
|
|
AppDelegate *myDelegate = [[AppDelegate alloc] init];
|
|
|
|
[ NSApp setDelegate:myDelegate];
|
|
|
|
|
|
|
|
[NSApp run];
|
|
|
|
|
2020-04-23 14:18:54 +00:00
|
|
|
print_to_log_file("exiting gfx_cleanup after handling %d",GFX_PidFromScreensaver);
|
2019-11-08 11:40:22 +00:00
|
|
|
#if USE_TIMER
|
|
|
|
endTime = time(NULL);
|
|
|
|
elapsedTime = endTime - startTime;
|
2020-04-23 14:18:54 +00:00
|
|
|
print_to_log_file("elapsed time=%d", (int) elapsedTime);
|
2019-11-08 11:40:22 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// print_to_log_file.c
|
|
|
|
|
|
|
|
#if CREATE_LOG
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
|
|
|
#include <time.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#ifdef unix
|
|
|
|
//#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void strip_cr(char *buf);
|
|
|
|
|
|
|
|
// print_to_log_file - use for debugging.
|
|
|
|
// prints time stamp plus a formatted string to log file.
|
|
|
|
// calling syntax: same as printf.
|
|
|
|
void print_to_log_file(const char *format, ...) {
|
|
|
|
FILE *f;
|
|
|
|
va_list args;
|
|
|
|
char buf[256];
|
|
|
|
time_t t;
|
|
|
|
#if 0
|
|
|
|
strcpy(buf, "/Library/Application Support/test_log.txt");
|
|
|
|
#else
|
|
|
|
strcpy(buf, getenv("HOME"));
|
|
|
|
strcat(buf, "/Library/Application Support/BOINC/test_log_gfx_cleanup.txt");
|
|
|
|
#endif
|
|
|
|
f = fopen(buf, "a");
|
|
|
|
// f = fopen("/Library/Games_Demo/test_log.txt", "a");
|
|
|
|
if (!f) return;
|
|
|
|
|
|
|
|
time(&t);
|
|
|
|
strcpy(buf, asctime(localtime(&t)));
|
|
|
|
strip_cr(buf);
|
|
|
|
|
|
|
|
fputs(buf, f);
|
|
|
|
fputs(" ", f);
|
|
|
|
|
|
|
|
va_start(args, format);
|
|
|
|
vfprintf(f, format, args);
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
fputs("\n", f);
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
|
|
|
|
void strip_cr(char *buf)
|
|
|
|
{
|
|
|
|
char *theCR;
|
|
|
|
|
|
|
|
theCR = strrchr(buf, '\n');
|
|
|
|
if (theCR)
|
|
|
|
*theCR = '\0';
|
|
|
|
theCR = strrchr(buf, '\r');
|
|
|
|
if (theCR)
|
|
|
|
*theCR = '\0';
|
|
|
|
}
|
|
|
|
#endif // CREATE_LOG
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
extern "C" {
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void PrintBacktrace(void) {
|
|
|
|
// Dummy routine to satisfy linker
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
}
|
|
|
|
#endif
|