Skip to content

Commit b983540

Browse files
authored
Merge pull request #6131 from BOINC/mac_installer_fixes
Mac: Fix bugs in Mac installer
2 parents e5c3484 + 504f69f commit b983540

File tree

17 files changed

+489
-324
lines changed

17 files changed

+489
-324
lines changed

client/check_security.cpp

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ int use_sandbox, int isManager, char* path_to_error, int len
8282
char dir_path[MAXPATHLEN], full_path[MAXPATHLEN];
8383
struct stat sbuf;
8484
int retval;
85+
int retval2;
8586
int useFakeProjectUserAndGroup = 0;
8687
int isMacInstaller = 0;
8788

@@ -149,9 +150,10 @@ int use_sandbox, int isManager, char* path_to_error, int len
149150
if ((sbuf.st_mode & (S_ISUID | S_ISGID)) != (S_ISUID | S_ISGID))
150151
return -1005;
151152

152-
boinc_master_uid = sbuf.st_uid;
153-
boinc_master_gid = sbuf.st_gid;
154-
153+
if (useFakeProjectUserAndGroup) {
154+
boinc_master_uid = sbuf.st_uid;
155+
boinc_master_gid = sbuf.st_gid;
156+
}
155157
#ifdef __WXMAC__
156158
if (!IsUserInGroup(REAL_BOINC_MASTER_NAME))
157159
return -1099;
@@ -161,10 +163,15 @@ int use_sandbox, int isManager, char* path_to_error, int len
161163
boinc_master_gid = getegid();
162164
}
163165

166+
retval2 = check_boinc_users_primarygroupIds(useFakeProjectUserAndGroup, isMacInstaller);
167+
164168
#ifdef __APPLE__
165169
char DataDirPath[MAXPATHLEN];
170+
#ifdef _MAC_INSTALLER
171+
strlcpy(DataDirPath, dataPath, sizeof(dir_path)); // Installer
172+
#else
166173
getcwd(DataDirPath, sizeof(DataDirPath));
167-
174+
#endif
168175
snprintf(full_path, sizeof(full_path),
169176
"%s/%s", DataDirPath, FIX_BOINC_USERS_FILENAME
170177
);
@@ -184,18 +191,17 @@ int use_sandbox, int isManager, char* path_to_error, int len
184191
if (! isMacInstaller) {
185192
// MacOS updates often change the PrimaryGroupID of boinc_master and
186193
// boinc_project to 20 (staff.)
187-
retval = check_boinc_users_primarygroupIds(useFakeProjectUserAndGroup, isMacInstaller);
188-
if ((retval == -1301) || (retval == -1302)) {
194+
if ((retval2 == -1301) || (retval2 == -1302)) {
189195
snprintf(full_path, sizeof(full_path),
190196
"\"%s/%s\"", DataDirPath, FIX_BOINC_USERS_FILENAME
191197
);
192198
printf("Permissions error %d, calling %s\n", retval, full_path);
193199
callPosixSpawn(full_path); // Try to fix it
194-
retval = check_boinc_users_primarygroupIds(useFakeProjectUserAndGroup, isMacInstaller);
200+
retval2 = check_boinc_users_primarygroupIds(useFakeProjectUserAndGroup, isMacInstaller);
195201
}
196202
}
197-
if (retval)
198-
return retval;
203+
if (retval2)
204+
return retval2;
199205
#endif
200206

201207
#if 0 // Manager is no longer setgid
@@ -231,15 +237,11 @@ int use_sandbox, int isManager, char* path_to_error, int len
231237
#endif // Manager is no longer setgid
232238

233239

234-
#ifdef _MAC_INSTALLER
240+
#if (defined(__WXMAC__) || defined(_MAC_INSTALLER)) // If Mac BOINC Manager or installer
235241
// Require absolute owner and group boinc_master:boinc_master
236242
// Get the full path to BOINC Client inside this application's bundle
237243
//
238-
snprintf(full_path, sizeof(full_path),
239-
"%s/Contents/Resources/boinc", dir_path
240-
);
241-
242-
retval = stat(full_path, &sbuf);
244+
retval = stat("/Applications/BOINCManager.app/Contents/Resources/boinc", &sbuf);
243245
if (retval)
244246
return -1016; // Should never happen
245247

@@ -294,13 +296,13 @@ int use_sandbox, int isManager, char* path_to_error, int len
294296
#ifdef _MAC_INSTALLER
295297
strlcpy(dir_path, dataPath, sizeof(dir_path)); // Installer
296298
#else // _MAC_INSTALLER
297-
gid_t egid = getegid();;
298-
uid_t euid = geteuid();;
299+
gid_t egid = getegid();
300+
uid_t euid = geteuid();
299301

300302
getcwd(dir_path, sizeof(dir_path)); // Client or Manager
301303

302304
if (! isManager) { // If BOINC Client
303-
if (egid != boinc_master_gid)
305+
if (egid != boinc_master_gid)
304306
return -1019; // Client should be running setgid boinc_master
305307

306308
if (euid != boinc_master_uid)
@@ -502,7 +504,8 @@ static int check_boinc_users_primarygroupIds(int useFakeProjectUserAndGroup, int
502504
if (pw->pw_gid != grp->gr_gid) {
503505
return -1301;
504506
}
505-
} else { // Use current user and group (see comment above)
507+
} else { // Use current user and group. See comment near start of check_security()
508+
// about "Debugging Mac client or Mac BOINC Manager")
506509

507510
pw = getpwuid(boinc_master_uid);
508511
if (pw == NULL)

clientgui/AdvancedFrame.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@
5050
#include "ViewProjects.h"
5151
#include "ViewWork.h"
5252
#include "ViewTransfers.h"
53-
#include "ViewMessages.h"
5453
#include "ViewStatistics.h"
5554
#include "ViewResources.h"
5655
#include "DlgAbout.h"
@@ -1567,6 +1566,7 @@ void CAdvancedFrame::OnRefreshView(CFrameEvent& WXUNUSED(event)) {
15671566
if (IsShown()) {
15681567
CMainDocument* pDoc = wxGetApp().GetDocument();
15691568
CBOINCBaseView* pView = NULL;
1569+
wxTimerEvent timerEvent;
15701570
wxString strTabTitle = wxEmptyString;
15711571
int iCount = 0;
15721572
static int iLastCount = 0;
@@ -1996,6 +1996,7 @@ void CAdvancedFrame::OnDarkModeChanged( wxSysColourChangedEvent& WXUNUSED(event)
19961996
CBOINCBaseView* theView = NULL;;
19971997
CBOINCListCtrl* theListCtrl = NULL;
19981998
long bottomItem;
1999+
wxTimerEvent timerEvent;
19992000
int currentPage = _GetCurrentViewPage();
20002001

20012002
StopTimers();

clientgui/BOINCBaseFrame.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// This file is part of BOINC.
22
// http://boinc.berkeley.edu
3-
// Copyright (C) 2023 University of California
3+
// Copyright (C) 2025 University of California
44
//
55
// BOINC is free software; you can redistribute it and/or modify it
66
// under the terms of the GNU Lesser General Public License
@@ -169,7 +169,7 @@ void CBOINCBaseFrame::OnPeriodicRPC(wxTimerEvent& WXUNUSED(event)) {
169169
wxGetApp().OnFinishInit();
170170
}
171171

172-
wxGetApp().CheckPartialActivation();
172+
// wxGetApp().CheckPartialActivation();
173173
#endif
174174

175175
if (!bAlreadyRunningLoop && m_pPeriodicRPCTimer->IsRunning()) {

clientgui/BOINCGUIApp.cpp

Lines changed: 65 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// This file is part of BOINC.
22
// http://boinc.berkeley.edu
3-
// Copyright (C) 2024 University of California
3+
// Copyright (C) 2025 University of California
44
//
55
// BOINC is free software; you can redistribute it and/or modify it
66
// under the terms of the GNU Lesser General Public License
@@ -149,6 +149,7 @@ bool CBOINCGUIApp::OnInit() {
149149
m_iDisplayAnotherInstanceRunningDialog = 1;
150150
#ifdef __WXMAC__
151151
m_iHideMenuBarIcon = 0;
152+
m_iWasShutDownBySystemWhileHidden = 0;
152153
#endif
153154
m_iGUISelected = BOINC_SIMPLEGUI;
154155
m_bSafeMessageBoxDisplayed = 0;
@@ -232,6 +233,18 @@ bool CBOINCGUIApp::OnInit() {
232233
m_pConfig->Read(wxT("DisplayAnotherInstanceRunningDialog"), &m_iDisplayAnotherInstanceRunningDialog, 1L);
233234
#ifdef __WXMAC__
234235
m_pConfig->Read(wxT("HideMenuBarIcon"), &m_iHideMenuBarIcon, 0L);
236+
m_pConfig->Read(wxT("WasShutDownBySystemWhileHidden"), &m_iWasShutDownBySystemWhileHidden, 0L);
237+
// If Manager was hidden and was shut down by system when user last logged
238+
// out, MacOS's "Reopen windows when logging in" functionality may relaunch
239+
// us visible before our LaunchAgent launches us with the "autostart" arg.
240+
// QuitAppleEventHandler() set m_iWasShutDownBySystemWhileHidden to 1, causing
241+
// CBOINCGUIApp::SaveState to set WasShutDownBySystemWhileHidden in our
242+
// configuration file to tell us to treat this as an autostart and launch hidden.
243+
if (m_iWasShutDownBySystemWhileHidden) {
244+
m_iWasShutDownBySystemWhileHidden = 0;
245+
m_bBOINCMGRAutoStarted = true;
246+
m_bGUIVisible = false;
247+
}
235248
#endif
236249
m_pConfig->Read(wxT("DisableAutoStart"), &m_iBOINCMGRDisableAutoStart, 0L);
237250
m_pConfig->Read(wxT("LanguageISO"), &m_strISOLanguageCode, wxT(""));
@@ -395,7 +408,6 @@ bool CBOINCGUIApp::OnInit() {
395408
g_use_sandbox, true, path_to_error, sizeof(path_to_error)
396409
);
397410
}
398-
399411
if (iErrorCode) {
400412

401413
#if (defined(__WXMAC__) && (!defined (_DEBUG)))
@@ -490,6 +502,16 @@ bool CBOINCGUIApp::OnInit() {
490502
// Detect if BOINC Manager is already running, if so, bring it into the
491503
// foreground and then exit.
492504
if (DetectDuplicateInstance()) {
505+
#ifdef __WXMAC__
506+
SetActivationPolicyAccessory(true);
507+
// Hack to work around an issue with the Mac Installer
508+
if (m_bBOINCMGRAutoStarted) return false;
509+
// TODO: Should we also ignore duplicate instances that occur within
510+
// TODO: a short time after login (detemined by getlastlogxbyname(),
511+
// TODO: in case MacOS's "Reopen windows when logging in" functionality
512+
// TODO: relaunched us after our LaunchAgent autostarted us?
513+
514+
#endif
493515
if (GetBOINCMGRDisplayAnotherInstanceRunningMessage()) {
494516
wxString appName = m_pSkinManager->GetAdvanced()->GetApplicationName();
495517
wxString message;
@@ -501,11 +523,19 @@ bool CBOINCGUIApp::OnInit() {
501523
CDlgGenericMessage dlg(NULL, &params);
502524
dlg.ShowModal();
503525
SetBOINCMGRDisplayAnotherInstanceRunningMessage(!dlg.GetDisableMessageValue());
504-
SaveState();
505526
}
506527
return false;
507528
}
508-
529+
#ifdef __WXMAC__
530+
// Each time a second instance of BOINC Managr is launched, it normally
531+
// nadds an additional BOINC icon to the "recently run apps" section
532+
// of the Dock, cluttering it up. To avoid this, we set the LSUIElement
533+
// key in its info.plist, which prevents the app from appearing in the
534+
// Dock. But if this is not a duplicate instance, we call this routine
535+
// to tell the system to show the icon in the Dock.
536+
// https://stackoverflow.com/questions/620841/how-to-hide-the-dock-icon
537+
SetActivationPolicyAccessory(false); // Show our Dock tile
538+
#endif
509539
// Initialize the main document
510540
m_pDocument = new CMainDocument();
511541
wxASSERT(m_pDocument);
@@ -686,6 +716,7 @@ void CBOINCGUIApp::SaveState() {
686716
m_pConfig->Write(wxT("DisplayAnotherInstanceRunningDialog"), m_iDisplayAnotherInstanceRunningDialog);
687717
#ifdef __WXMAC__
688718
m_pConfig->Write(wxT("HideMenuBarIcon"), m_iHideMenuBarIcon);
719+
m_pConfig->Write(wxT("WasShutDownBySystemWhileHidden"), m_iWasShutDownBySystemWhileHidden);
689720
#endif
690721
m_pConfig->Write(wxT("DisableAutoStart"), m_iBOINCMGRDisableAutoStart);
691722
m_pConfig->Write(wxT("RunDaemon"), m_bRunDaemon);
@@ -1105,30 +1136,41 @@ int CBOINCGUIApp::IdleTrackerDetach() {
11051136
void CBOINCGUIApp::OnActivateApp(wxActivateEvent& event) {
11061137
m_bProcessingActivateAppEvent = true;
11071138

1108-
11091139
#ifndef __WXMSW__ // On Win, the following raises the wrong window
11101140
if (event.GetActive())
11111141
#endif
11121142
{
11131143
#ifdef __WXMAC__
1114-
ShowInterface();
1115-
#elif defined(__WXGTK__)
1116-
// Linux allows the Event Log to be brought forward and made active
1117-
// even if we have a modal dialog displayed (associated with our
1118-
// main frame.) This test is needed to allow bringing the modal
1119-
// dialog forward again by clicking on its title bar.
1120-
if (!IsModalDialogDisplayed())
1144+
// When our LaunchAgent / login item launches us at login, it activates
1145+
// this app, but we want it hidden. So we immediatly hide it upon the
1146+
// first activation when run as a login item.
1147+
static bool first = true;
1148+
1149+
CMainDocument* pDoc = wxGetApp().GetDocument();
1150+
if (m_bBOINCMGRAutoStarted && (!pDoc->IsConnected()) && first) {
1151+
first = false;
1152+
ShowApplication(false);
1153+
} else
1154+
#endif
1155+
#if (defined (__WXMAC__) || defined(__WXGTK__))
11211156
{
1122-
bool keepEventLogInFront = m_bEventLogWasActive;
1123-
1124-
if (m_pEventLog && !m_pEventLog->IsIconized() && !keepEventLogInFront) {
1125-
m_pEventLog->Raise();
1126-
}
1127-
if (m_pFrame) {
1128-
m_pFrame->Raise();
1129-
}
1130-
if (m_pEventLog && !m_pEventLog->IsIconized() && keepEventLogInFront) {
1131-
m_pEventLog->Raise();
1157+
// Linux allows the Event Log to be brought forward and made active
1158+
// even if we have a modal dialog displayed (associated with our
1159+
// main frame.) This test is needed to allow bringing the modal
1160+
// dialog forward again by clicking on its title bar.
1161+
if (!IsModalDialogDisplayed())
1162+
{
1163+
bool keepEventLogInFront = m_bEventLogWasActive;
1164+
1165+
if (m_pEventLog && !m_pEventLog->IsIconized() && !keepEventLogInFront) {
1166+
m_pEventLog->Raise();
1167+
}
1168+
if (m_pFrame) {
1169+
m_pFrame->Raise();
1170+
}
1171+
if (m_pEventLog && !m_pEventLog->IsIconized() && keepEventLogInFront) {
1172+
m_pEventLog->Raise();
1173+
}
11321174
}
11331175
}
11341176
#endif
@@ -1536,6 +1578,7 @@ bool CBOINCGUIApp::IsModalDialogDisplayed() {
15361578
return false;
15371579
}
15381580

1581+
15391582
// Prevent recursive entry of CMainDocument::RequestRPC()
15401583
int CBOINCGUIApp::FilterEvent(wxEvent &event) {
15411584
int theEventType;
@@ -1588,4 +1631,3 @@ int CBOINCGUIApp::FilterEvent(wxEvent &event) {
15881631

15891632
return -1;
15901633
}
1591-

clientgui/BOINCGUIApp.h

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// This file is part of BOINC.
22
// http://boinc.berkeley.edu
3-
// Copyright (C) 2024 University of California
3+
// Copyright (C) 2025 University of California
44
//
55
// BOINC is free software; you can redistribute it and/or modify it
66
// under the terms of the GNU Lesser General Public License
@@ -123,6 +123,7 @@ class CBOINCGUIApp : public wxApp {
123123
int m_iDisplayAnotherInstanceRunningDialog;
124124
#ifdef __WXMAC__
125125
int m_iHideMenuBarIcon;
126+
int m_iWasShutDownBySystemWhileHidden;
126127
#endif
127128

128129
bool m_bGUIVisible;
@@ -181,7 +182,7 @@ class CBOINCGUIApp : public wxApp {
181182
{ return m_iBOINCMGRDisableAutoStart; }
182183
void SetBOINCMGRDisableAutoStart(int iDisableAutoStart)
183184
{ m_iBOINCMGRDisableAutoStart = iDisableAutoStart; }
184-
185+
bool getBOINCMGRAutoStarted() { return m_bBOINCMGRAutoStarted; }
185186
int GetBOINCMGRDisplayExitMessage()
186187
{ return m_iDisplayExitDialog; }
187188
void SetBOINCMGRDisplayExitMessage(int iDisplayExitMessage)
@@ -202,6 +203,8 @@ class CBOINCGUIApp : public wxApp {
202203
{ return m_iHideMenuBarIcon; }
203204
void SetBOINCMGRHideMenuBarIcon(int iHideMenuBarIcon)
204205
{ m_iHideMenuBarIcon = iHideMenuBarIcon; }
206+
void SetBOINCMGRWasShutDownBySystemWhileHidden(int val)
207+
{ m_iWasShutDownBySystemWhileHidden = val; }
205208
#endif
206209

207210
bool GetRunDaemon()
@@ -276,19 +279,14 @@ class CBOINCGUIApp : public wxApp {
276279
bool WasFileModifiedBeforeSystemBoot(char * filePath);
277280
void HideThisApp(void);
278281
void getDisplayNameForThisApp(char* pathBuf, size_t bufSize);
279-
280-
#if !wxCHECK_VERSION(3,0,1)
281-
// This should be fixed after wxCocoa 3.0.0:
282-
// http://trac.wxwidgets.org/ticket/16156
282+
void SetActivationPolicyAccessory(bool hideDock);
283+
void CheckPartialActivation();
283284

284285
// Override standard wxCocoa wxApp::CallOnInit() to allow Manager
285286
// to run properly when launched hidden on login via Login Item.
286287
bool CallOnInit();
287288
#endif
288289

289-
void CheckPartialActivation();
290-
#endif
291-
292290
DECLARE_EVENT_TABLE()
293291
};
294292

0 commit comments

Comments
 (0)