forked from quickshell/quickshell
		
	crash: add crash reporter
This commit is contained in:
		
							parent
							
								
									5040f3796c
								
							
						
					
					
						commit
						fe1d15e8f6
					
				
					 23 changed files with 1118 additions and 315 deletions
				
			
		
							
								
								
									
										1
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					blank_issues_enabled: true
 | 
				
			||||||
							
								
								
									
										72
									
								
								.github/ISSUE_TEMPLATE/crash.yml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								.github/ISSUE_TEMPLATE/crash.yml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,72 @@
 | 
				
			||||||
 | 
					name: Crash Report
 | 
				
			||||||
 | 
					description: Quickshell has crashed
 | 
				
			||||||
 | 
					labels: ["bug", "crash"]
 | 
				
			||||||
 | 
					body:
 | 
				
			||||||
 | 
					  - type: textarea
 | 
				
			||||||
 | 
					    id: crashinfo
 | 
				
			||||||
 | 
					    attributes:
 | 
				
			||||||
 | 
					      label: General crash information
 | 
				
			||||||
 | 
					      description: |
 | 
				
			||||||
 | 
					        Paste the contents of the `info.txt` file in your crash folder here.
 | 
				
			||||||
 | 
					      value: "<details> <summary>General information</summary>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <Paste the contents of the file here inside of the triple backticks>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </details>"
 | 
				
			||||||
 | 
					    validations:
 | 
				
			||||||
 | 
					        required: true
 | 
				
			||||||
 | 
					  - type: textarea
 | 
				
			||||||
 | 
					    id: userinfo
 | 
				
			||||||
 | 
					    attributes:
 | 
				
			||||||
 | 
					      label: What caused the crash
 | 
				
			||||||
 | 
					      description: |
 | 
				
			||||||
 | 
					        Any information likely to help debug the crash. What were you doing when the crash occurred,
 | 
				
			||||||
 | 
					        what changes did you make, can you get it to happen again?
 | 
				
			||||||
 | 
					  - type: textarea
 | 
				
			||||||
 | 
					    id: dump
 | 
				
			||||||
 | 
					    attributes:
 | 
				
			||||||
 | 
					      label: Minidump
 | 
				
			||||||
 | 
					      description: |
 | 
				
			||||||
 | 
					        Attach `minidump.dmp` here. If it is too big to upload, compress it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        You may skip this step if quickshell crashed while processing a password
 | 
				
			||||||
 | 
					        or other sensitive information. If you skipped it write why instead.
 | 
				
			||||||
 | 
					    validations:
 | 
				
			||||||
 | 
					        required: true
 | 
				
			||||||
 | 
					  - type: textarea
 | 
				
			||||||
 | 
					    id: logs
 | 
				
			||||||
 | 
					    attributes:
 | 
				
			||||||
 | 
					      label: Log file
 | 
				
			||||||
 | 
					      description: |
 | 
				
			||||||
 | 
					        Attach `log.qslog` here. If it is too big to upload, compress it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        You can preview the log if you'd like using `quickshell read-log <path-to-log>`.
 | 
				
			||||||
 | 
					    validations:
 | 
				
			||||||
 | 
					        required: true
 | 
				
			||||||
 | 
					  - type: textarea
 | 
				
			||||||
 | 
					    id: config
 | 
				
			||||||
 | 
					    attributes:
 | 
				
			||||||
 | 
					      label: Configuration
 | 
				
			||||||
 | 
					      description: |
 | 
				
			||||||
 | 
					        Attach your configuration here, preferrably in full (not just one file).
 | 
				
			||||||
 | 
					        Compress it into a zip, tar, etc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This will help us reproduce the crash ourselves.
 | 
				
			||||||
 | 
					  - type: textarea
 | 
				
			||||||
 | 
					    id: bt
 | 
				
			||||||
 | 
					    attributes:
 | 
				
			||||||
 | 
					      label: Backtrace
 | 
				
			||||||
 | 
					      description: |
 | 
				
			||||||
 | 
					        If you have gdb installed and use systemd, or otherwise know how to get a backtrace,
 | 
				
			||||||
 | 
					        we would appreciate one. (You may have gdb installed without knowing it)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        1. Run `coredumpctl debug <pid>` where `pid` is the number shown after "Crashed process ID"
 | 
				
			||||||
 | 
					        in the crash reporter.
 | 
				
			||||||
 | 
					        2. Once it loads, type `bt -full` (then enter)
 | 
				
			||||||
 | 
					        3. Copy the output and attach it as a file or in a spoiler.
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@ option(BUILD_TESTING "Build tests" OFF)
 | 
				
			||||||
option(ASAN "Enable ASAN" OFF) # note: better output with gcc than clang
 | 
					option(ASAN "Enable ASAN" OFF) # note: better output with gcc than clang
 | 
				
			||||||
option(FRAME_POINTERS "Always keep frame pointers" ${ASAN})
 | 
					option(FRAME_POINTERS "Always keep frame pointers" ${ASAN})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					option(CRASH_REPORTER "Enable the crash reporter" ON)
 | 
				
			||||||
option(USE_JEMALLOC "Use jemalloc over the system malloc implementation" ON)
 | 
					option(USE_JEMALLOC "Use jemalloc over the system malloc implementation" ON)
 | 
				
			||||||
option(SOCKETS "Enable unix socket support" ON)
 | 
					option(SOCKETS "Enable unix socket support" ON)
 | 
				
			||||||
option(WAYLAND "Enable wayland support" ON)
 | 
					option(WAYLAND "Enable wayland support" ON)
 | 
				
			||||||
| 
						 | 
					@ -29,6 +30,7 @@ option(SERVICE_UPOWER "UPower service" ON)
 | 
				
			||||||
option(SERVICE_NOTIFICATIONS "Notification server" ON)
 | 
					option(SERVICE_NOTIFICATIONS "Notification server" ON)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
message(STATUS "Quickshell configuration")
 | 
					message(STATUS "Quickshell configuration")
 | 
				
			||||||
 | 
					message(STATUS "  Crash reporter: ${CRASH_REPORTER}")
 | 
				
			||||||
message(STATUS "  Jemalloc: ${USE_JEMALLOC}")
 | 
					message(STATUS "  Jemalloc: ${USE_JEMALLOC}")
 | 
				
			||||||
message(STATUS "  Build tests: ${BUILD_TESTING}")
 | 
					message(STATUS "  Build tests: ${BUILD_TESTING}")
 | 
				
			||||||
message(STATUS "  Sockets: ${SOCKETS}")
 | 
					message(STATUS "  Sockets: ${SOCKETS}")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@
 | 
				
			||||||
  ninja,
 | 
					  ninja,
 | 
				
			||||||
  qt6,
 | 
					  qt6,
 | 
				
			||||||
  cli11,
 | 
					  cli11,
 | 
				
			||||||
 | 
					  breakpad,
 | 
				
			||||||
  jemalloc,
 | 
					  jemalloc,
 | 
				
			||||||
  wayland,
 | 
					  wayland,
 | 
				
			||||||
  wayland-protocols,
 | 
					  wayland-protocols,
 | 
				
			||||||
| 
						 | 
					@ -28,6 +29,7 @@
 | 
				
			||||||
     else "unknown"),
 | 
					     else "unknown"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  debug ? false,
 | 
					  debug ? false,
 | 
				
			||||||
 | 
					  withCrashReporter ? true,
 | 
				
			||||||
  withJemalloc ? true, # masks heap fragmentation
 | 
					  withJemalloc ? true, # masks heap fragmentation
 | 
				
			||||||
  withQtSvg ? true,
 | 
					  withQtSvg ? true,
 | 
				
			||||||
  withWayland ? true,
 | 
					  withWayland ? true,
 | 
				
			||||||
| 
						 | 
					@ -55,6 +57,7 @@
 | 
				
			||||||
    qt6.qtdeclarative
 | 
					    qt6.qtdeclarative
 | 
				
			||||||
    cli11
 | 
					    cli11
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
 | 
					  ++ (lib.optional withCrashReporter breakpad)
 | 
				
			||||||
  ++ (lib.optional withJemalloc jemalloc)
 | 
					  ++ (lib.optional withJemalloc jemalloc)
 | 
				
			||||||
  ++ (lib.optional withQtSvg qt6.qtsvg)
 | 
					  ++ (lib.optional withQtSvg qt6.qtsvg)
 | 
				
			||||||
  ++ (lib.optionals withWayland [ qt6.qtwayland wayland ])
 | 
					  ++ (lib.optionals withWayland [ qt6.qtwayland wayland ])
 | 
				
			||||||
| 
						 | 
					@ -67,6 +70,7 @@
 | 
				
			||||||
  cmakeBuildType = if debug then "Debug" else "RelWithDebInfo";
 | 
					  cmakeBuildType = if debug then "Debug" else "RelWithDebInfo";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  cmakeFlags = [ "-DGIT_REVISION=${gitRev}" ]
 | 
					  cmakeFlags = [ "-DGIT_REVISION=${gitRev}" ]
 | 
				
			||||||
 | 
					  ++ lib.optional (!withCrashReporter) "-DCRASH_REPORTER=OFF"
 | 
				
			||||||
  ++ lib.optional (!withJemalloc) "-DUSE_JEMALLOC=OFF"
 | 
					  ++ lib.optional (!withJemalloc) "-DUSE_JEMALLOC=OFF"
 | 
				
			||||||
  ++ lib.optional (!withWayland) "-DWAYLAND=OFF"
 | 
					  ++ lib.optional (!withWayland) "-DWAYLAND=OFF"
 | 
				
			||||||
  ++ lib.optional (!withPipewire) "-DSERVICE_PIPEWIRE=OFF"
 | 
					  ++ lib.optional (!withPipewire) "-DSERVICE_PIPEWIRE=OFF"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,10 @@ install(TARGETS quickshell RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
 | 
				
			||||||
add_subdirectory(core)
 | 
					add_subdirectory(core)
 | 
				
			||||||
add_subdirectory(io)
 | 
					add_subdirectory(io)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if (CRASH_REPORTER)
 | 
				
			||||||
 | 
						add_subdirectory(crash)
 | 
				
			||||||
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (DBUS)
 | 
					if (DBUS)
 | 
				
			||||||
	add_subdirectory(dbus)
 | 
						add_subdirectory(dbus)
 | 
				
			||||||
endif()
 | 
					endif()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,9 +41,19 @@ qt_add_library(quickshell-core STATIC
 | 
				
			||||||
	clock.cpp
 | 
						clock.cpp
 | 
				
			||||||
	logging.cpp
 | 
						logging.cpp
 | 
				
			||||||
	paths.cpp
 | 
						paths.cpp
 | 
				
			||||||
 | 
						crashinfo.cpp
 | 
				
			||||||
 | 
						common.cpp
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
set_source_files_properties(main.cpp PROPERTIES COMPILE_DEFINITIONS GIT_REVISION="${GIT_REVISION}")
 | 
					if (CRASH_REPORTER)
 | 
				
			||||||
 | 
						set(CRASH_REPORTER_DEF 1)
 | 
				
			||||||
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add_library(quickshell-build INTERFACE)
 | 
				
			||||||
 | 
					configure_file(build.hpp.in build.hpp)
 | 
				
			||||||
 | 
					target_include_directories(quickshell-build INTERFACE ${CMAKE_CURRENT_BINARY_DIR})
 | 
				
			||||||
 | 
					target_link_libraries(quickshell-core PRIVATE quickshell-build)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
qt_add_qml_module(quickshell-core URI Quickshell VERSION 0.1)
 | 
					qt_add_qml_module(quickshell-core URI Quickshell VERSION 0.1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
target_link_libraries(quickshell-core PRIVATE ${QT_DEPS} Qt6::QuickPrivate CLI11::CLI11)
 | 
					target_link_libraries(quickshell-core PRIVATE ${QT_DEPS} Qt6::QuickPrivate CLI11::CLI11)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										6
									
								
								src/core/build.hpp.in
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/core/build.hpp.in
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NOLINTBEGIN
 | 
				
			||||||
 | 
					#define GIT_REVISION "@GIT_REVISION@"
 | 
				
			||||||
 | 
					#define CRASH_REPORTER @CRASH_REPORTER_DEF@
 | 
				
			||||||
 | 
					// NOLINTEND
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/core/common.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/core/common.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					#include "common.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qdatetime.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace qs {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const QDateTime Common::LAUNCH_TIME = QDateTime::currentDateTime();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								src/core/common.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/core/common.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qdatetime.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace qs {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct Common {
 | 
				
			||||||
 | 
						static const QDateTime LAUNCH_TIME;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace qs
 | 
				
			||||||
							
								
								
									
										19
									
								
								src/core/crashinfo.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/core/crashinfo.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					#include "crashinfo.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qdatastream.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QDataStream& operator<<(QDataStream& stream, const InstanceInfo& info) {
 | 
				
			||||||
 | 
						stream << info.configPath << info.shellId << info.launchTime << info.noColor;
 | 
				
			||||||
 | 
						return stream;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QDataStream& operator>>(QDataStream& stream, InstanceInfo& info) {
 | 
				
			||||||
 | 
						stream >> info.configPath >> info.shellId >> info.launchTime >> info.noColor;
 | 
				
			||||||
 | 
						return stream;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace qs::crash {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CrashInfo CrashInfo::INSTANCE = {}; // NOLINT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										26
									
								
								src/core/crashinfo.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/core/crashinfo.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,26 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qdatetime.h>
 | 
				
			||||||
 | 
					#include <qstring.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct InstanceInfo {
 | 
				
			||||||
 | 
						QString configPath;
 | 
				
			||||||
 | 
						QString shellId;
 | 
				
			||||||
 | 
						QString initialWorkdir;
 | 
				
			||||||
 | 
						QDateTime launchTime;
 | 
				
			||||||
 | 
						bool noColor = false;
 | 
				
			||||||
 | 
						bool sparseLogsOnly = false;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QDataStream& operator<<(QDataStream& stream, const InstanceInfo& info);
 | 
				
			||||||
 | 
					QDataStream& operator>>(QDataStream& stream, InstanceInfo& info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace qs::crash {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct CrashInfo {
 | 
				
			||||||
 | 
						int logFd = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static CrashInfo INSTANCE; // NOLINT
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace qs::crash
 | 
				
			||||||
| 
						 | 
					@ -24,6 +24,7 @@
 | 
				
			||||||
#include <sys/mman.h>
 | 
					#include <sys/mman.h>
 | 
				
			||||||
#include <sys/sendfile.h>
 | 
					#include <sys/sendfile.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "crashinfo.hpp"
 | 
				
			||||||
#include "logging_p.hpp"
 | 
					#include "logging_p.hpp"
 | 
				
			||||||
#include "logging_qtprivate.cpp" // NOLINT
 | 
					#include "logging_qtprivate.cpp" // NOLINT
 | 
				
			||||||
#include "paths.hpp"
 | 
					#include "paths.hpp"
 | 
				
			||||||
| 
						 | 
					@ -198,14 +199,16 @@ void ThreadLogging::init() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (logMfd != -1) {
 | 
						if (logMfd != -1) {
 | 
				
			||||||
		this->file = new QFile();
 | 
							this->file = new QFile();
 | 
				
			||||||
		this->file->open(logMfd, QFile::WriteOnly, QFile::AutoCloseHandle);
 | 
							this->file->open(logMfd, QFile::ReadWrite, QFile::AutoCloseHandle);
 | 
				
			||||||
		this->fileStream.setDevice(this->file);
 | 
							this->fileStream.setDevice(this->file);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (dlogMfd != -1) {
 | 
						if (dlogMfd != -1) {
 | 
				
			||||||
 | 
							crash::CrashInfo::INSTANCE.logFd = dlogMfd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this->detailedFile = new QFile();
 | 
							this->detailedFile = new QFile();
 | 
				
			||||||
		// buffered by WriteBuffer
 | 
							// buffered by WriteBuffer
 | 
				
			||||||
		this->detailedFile->open(dlogMfd, QFile::WriteOnly | QFile::Unbuffered, QFile::AutoCloseHandle);
 | 
							this->detailedFile->open(dlogMfd, QFile::ReadWrite | QFile::Unbuffered, QFile::AutoCloseHandle);
 | 
				
			||||||
		this->detailedWriter.setDevice(this->detailedFile);
 | 
							this->detailedWriter.setDevice(this->detailedFile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!this->detailedWriter.writeHeader()) {
 | 
							if (!this->detailedWriter.writeHeader()) {
 | 
				
			||||||
| 
						 | 
					@ -245,7 +248,7 @@ void ThreadLogging::initFs() {
 | 
				
			||||||
	auto* file = new QFile(path);
 | 
						auto* file = new QFile(path);
 | 
				
			||||||
	auto* detailedFile = new QFile(detailedPath);
 | 
						auto* detailedFile = new QFile(detailedPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!file->open(QFile::WriteOnly | QFile::Truncate)) {
 | 
						if (!file->open(QFile::ReadWrite | QFile::Truncate)) {
 | 
				
			||||||
		qCCritical(logLogging
 | 
							qCCritical(logLogging
 | 
				
			||||||
		) << "Could not start filesystem logger as the log file could not be created:"
 | 
							) << "Could not start filesystem logger as the log file could not be created:"
 | 
				
			||||||
		  << path;
 | 
							  << path;
 | 
				
			||||||
| 
						 | 
					@ -256,7 +259,7 @@ void ThreadLogging::initFs() {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// buffered by WriteBuffer
 | 
						// buffered by WriteBuffer
 | 
				
			||||||
	if (!detailedFile->open(QFile::WriteOnly | QFile::Truncate | QFile::Unbuffered)) {
 | 
						if (!detailedFile->open(QFile::ReadWrite | QFile::Truncate | QFile::Unbuffered)) {
 | 
				
			||||||
		qCCritical(logLogging
 | 
							qCCritical(logLogging
 | 
				
			||||||
		) << "Could not start detailed filesystem logger as the log file could not be created:"
 | 
							) << "Could not start detailed filesystem logger as the log file could not be created:"
 | 
				
			||||||
		  << detailedPath;
 | 
							  << detailedPath;
 | 
				
			||||||
| 
						 | 
					@ -287,6 +290,8 @@ void ThreadLogging::initFs() {
 | 
				
			||||||
			sendfile(detailedFile->handle(), oldFile->handle(), nullptr, oldFile->size());
 | 
								sendfile(detailedFile->handle(), oldFile->handle(), nullptr, oldFile->size());
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							crash::CrashInfo::INSTANCE.logFd = detailedFile->handle();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this->detailedFile = detailedFile;
 | 
							this->detailedFile = detailedFile;
 | 
				
			||||||
		this->detailedWriter.setDevice(detailedFile);
 | 
							this->detailedWriter.setDevice(detailedFile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,17 @@
 | 
				
			||||||
#include "main.hpp"
 | 
					#include "main.hpp"
 | 
				
			||||||
 | 
					#include <cstdlib>
 | 
				
			||||||
#include <iostream>
 | 
					#include <iostream>
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <CLI/App.hpp>
 | 
					#include <CLI/App.hpp>
 | 
				
			||||||
#include <CLI/CLI.hpp> // NOLINT: Need to include this for impls of some CLI11 classes
 | 
					#include <CLI/CLI.hpp> // NOLINT: Need to include this for impls of some CLI11 classes
 | 
				
			||||||
 | 
					#include <CLI/Error.hpp>
 | 
				
			||||||
#include <CLI/Validators.hpp>
 | 
					#include <CLI/Validators.hpp>
 | 
				
			||||||
#include <qapplication.h>
 | 
					#include <qapplication.h>
 | 
				
			||||||
#include <qcoreapplication.h>
 | 
					#include <qcoreapplication.h>
 | 
				
			||||||
#include <qcryptographichash.h>
 | 
					#include <qcryptographichash.h>
 | 
				
			||||||
 | 
					#include <qdatastream.h>
 | 
				
			||||||
 | 
					#include <qdatetime.h>
 | 
				
			||||||
#include <qdir.h>
 | 
					#include <qdir.h>
 | 
				
			||||||
#include <qfileinfo.h>
 | 
					#include <qfileinfo.h>
 | 
				
			||||||
#include <qguiapplication.h>
 | 
					#include <qguiapplication.h>
 | 
				
			||||||
| 
						 | 
					@ -24,162 +28,164 @@
 | 
				
			||||||
#include <qtextstream.h>
 | 
					#include <qtextstream.h>
 | 
				
			||||||
#include <qtpreprocessorsupport.h>
 | 
					#include <qtpreprocessorsupport.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "build.hpp"
 | 
				
			||||||
 | 
					#include "common.hpp"
 | 
				
			||||||
 | 
					#include "crashinfo.hpp"
 | 
				
			||||||
#include "logging.hpp"
 | 
					#include "logging.hpp"
 | 
				
			||||||
#include "paths.hpp"
 | 
					#include "paths.hpp"
 | 
				
			||||||
#include "plugin.hpp"
 | 
					#include "plugin.hpp"
 | 
				
			||||||
#include "rootwrapper.hpp"
 | 
					#include "rootwrapper.hpp"
 | 
				
			||||||
 | 
					#if CRASH_REPORTER
 | 
				
			||||||
 | 
					#include "../crash/handler.hpp"
 | 
				
			||||||
 | 
					#include "../crash/main.hpp"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int qs_main(int argc, char** argv) {
 | 
					struct CommandInfo {
 | 
				
			||||||
 | 
						QString configPath;
 | 
				
			||||||
 | 
						QString manifestPath;
 | 
				
			||||||
 | 
						QString configName;
 | 
				
			||||||
 | 
						QString& initialWorkdir;
 | 
				
			||||||
 | 
						int& debugPort;
 | 
				
			||||||
 | 
						bool& waitForDebug;
 | 
				
			||||||
 | 
						bool& printInfo;
 | 
				
			||||||
 | 
						bool& noColor;
 | 
				
			||||||
 | 
						bool& sparseLogsOnly;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto qArgC = 1;
 | 
					void processCommand(int argc, char** argv, CommandInfo& info) {
 | 
				
			||||||
	auto* qArgV = argv;
 | 
						auto app = CLI::App("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto noColor = !qEnvironmentVariableIsEmpty("NO_COLOR");
 | 
						class QStringOption {
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
							QStringOption() = default;
 | 
				
			||||||
 | 
							QStringOption& operator=(const std::string& str) {
 | 
				
			||||||
 | 
								this->str = QString::fromStdString(str);
 | 
				
			||||||
 | 
								return *this;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QString workingDirectory;
 | 
							QString& operator*() { return this->str; }
 | 
				
			||||||
	QString configFilePath;
 | 
					 | 
				
			||||||
	QString shellId;
 | 
					 | 
				
			||||||
	auto printInfo = false;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto debugPort = -1;
 | 
						private:
 | 
				
			||||||
	auto waitForDebug = false;
 | 
							QString str;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto useQApplication = false;
 | 
						class QStringRefOption {
 | 
				
			||||||
	auto nativeTextRendering = false;
 | 
						public:
 | 
				
			||||||
	auto desktopSettingsAware = true;
 | 
							QStringRefOption(QString* str): str(str) {}
 | 
				
			||||||
	QHash<QString, QString> envOverrides;
 | 
							QStringRefOption& operator=(const std::string& str) {
 | 
				
			||||||
 | 
								*this->str = QString::fromStdString(str);
 | 
				
			||||||
 | 
								return *this;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	{
 | 
						private:
 | 
				
			||||||
		auto app = CLI::App("");
 | 
							QString* str;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		class QStringOption {
 | 
						/// ---
 | 
				
			||||||
		public:
 | 
						QStringRefOption path(&info.configPath);
 | 
				
			||||||
			QStringOption() = default;
 | 
						QStringRefOption manifest(&info.manifestPath);
 | 
				
			||||||
			QStringOption& operator=(const std::string& str) {
 | 
						QStringRefOption config(&info.configName);
 | 
				
			||||||
				this->str = QString::fromStdString(str);
 | 
						QStringRefOption workdirRef(&info.initialWorkdir);
 | 
				
			||||||
				return *this;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			QString& operator*() { return this->str; }
 | 
						auto* selection = app.add_option_group(
 | 
				
			||||||
 | 
						    "Config Selection",
 | 
				
			||||||
 | 
						    "Select a configuration to run (defaults to $XDG_CONFIG_HOME/quickshell/shell.qml)"
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		private:
 | 
						auto* pathArg =
 | 
				
			||||||
			QString str;
 | 
						    selection->add_option("-p,--path", path, "Path to a QML file to run. (Env:QS_CONFIG_PATH)");
 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		class QStringRefOption {
 | 
						auto* mfArg = selection->add_option(
 | 
				
			||||||
		public:
 | 
						    "-m,--manifest",
 | 
				
			||||||
			QStringRefOption(QString* str): str(str) {}
 | 
						    manifest,
 | 
				
			||||||
			QStringRefOption& operator=(const std::string& str) {
 | 
						    "Path to a manifest containing configurations. (Env:QS_MANIFEST)\n"
 | 
				
			||||||
				*this->str = QString::fromStdString(str);
 | 
						    "(Defaults to $XDG_CONFIG_HOME/quickshell/manifest.conf)"
 | 
				
			||||||
				return *this;
 | 
						);
 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		private:
 | 
						auto* cfgArg = selection->add_option(
 | 
				
			||||||
			QString* str;
 | 
						    "-c,--config",
 | 
				
			||||||
		};
 | 
						    config,
 | 
				
			||||||
 | 
						    "Name of a configuration within a manifest. (Env:QS_CONFIG_NAME)"
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/// ---
 | 
						selection->add_option("-d,--workdir", workdirRef, "Initial working directory.");
 | 
				
			||||||
		QStringOption path;
 | 
					 | 
				
			||||||
		QStringOption manifest;
 | 
					 | 
				
			||||||
		QStringOption config;
 | 
					 | 
				
			||||||
		QStringRefOption workdirRef(&workingDirectory);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		auto* selection = app.add_option_group(
 | 
						pathArg->excludes(mfArg, cfgArg);
 | 
				
			||||||
		    "Config Selection",
 | 
					 | 
				
			||||||
		    "Select a configuration to run (defaults to $XDG_CONFIG_HOME/quickshell/shell.qml)"
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		auto* pathArg =
 | 
						/// ---
 | 
				
			||||||
		    selection->add_option("-p,--path", path, "Path to a QML file to run. (Env:QS_CONFIG_PATH)");
 | 
						auto* debug = app.add_option_group("Debugging");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		auto* mfArg = selection->add_option(
 | 
						auto* debugPortArg = debug
 | 
				
			||||||
		    "-m,--manifest",
 | 
						                         ->add_option(
 | 
				
			||||||
		    manifest,
 | 
						                             "--debugport",
 | 
				
			||||||
		    "Path to a manifest containing configurations. (Env:QS_MANIFEST)\n"
 | 
						                             info.debugPort,
 | 
				
			||||||
		    "(Defaults to $XDG_CONFIG_HOME/quickshell/manifest.conf)"
 | 
						                             "Open the given port for a QML debugger to connect to."
 | 
				
			||||||
		);
 | 
						                         )
 | 
				
			||||||
 | 
						                         ->check(CLI::Range(0, 65535));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		auto* cfgArg = selection->add_option(
 | 
						debug
 | 
				
			||||||
		    "-c,--config",
 | 
						    ->add_flag(
 | 
				
			||||||
		    config,
 | 
						        "--waitfordebug",
 | 
				
			||||||
		    "Name of a configuration within a manifest. (Env:QS_CONFIG_NAME)"
 | 
						        info.waitForDebug,
 | 
				
			||||||
		);
 | 
						        "Wait for a debugger to attach to the given port before launching."
 | 
				
			||||||
 | 
						    )
 | 
				
			||||||
 | 
						    ->needs(debugPortArg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		selection->add_option("-d,--workdir", workdirRef, "Initial working directory.");
 | 
						/// ---
 | 
				
			||||||
 | 
						app.add_flag("--info", info.printInfo, "Print information about the shell")
 | 
				
			||||||
 | 
						    ->excludes(debugPortArg);
 | 
				
			||||||
 | 
						app.add_flag("--no-color", info.noColor, "Do not color the log output. (Env:NO_COLOR)");
 | 
				
			||||||
 | 
						auto* printVersion = app.add_flag("-V,--version", "Print quickshell's version, then exit.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		pathArg->excludes(mfArg, cfgArg);
 | 
						app.add_flag(
 | 
				
			||||||
 | 
						    "--no-detailed-logs",
 | 
				
			||||||
 | 
						    info.sparseLogsOnly,
 | 
				
			||||||
 | 
						    "Do not enable this unless you know what you are doing."
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/// ---
 | 
						/// ---
 | 
				
			||||||
		auto* debug = app.add_option_group("Debugging");
 | 
						QStringOption logPath;
 | 
				
			||||||
 | 
						QStringOption logFilter;
 | 
				
			||||||
 | 
						auto logNoTime = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		auto* debugPortArg = debug
 | 
						auto* readLog = app.add_subcommand("read-log", "Read a quickshell log file.");
 | 
				
			||||||
		                         ->add_option(
 | 
						readLog->add_option("path", logPath, "Path to the log file to read")->required();
 | 
				
			||||||
		                             "--debugport",
 | 
					 | 
				
			||||||
		                             debugPort,
 | 
					 | 
				
			||||||
		                             "Open the given port for a QML debugger to connect to."
 | 
					 | 
				
			||||||
		                         )
 | 
					 | 
				
			||||||
		                         ->check(CLI::Range(0, 65535));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		debug
 | 
						readLog->add_option(
 | 
				
			||||||
		    ->add_flag(
 | 
						    "-f,--filter",
 | 
				
			||||||
		        "--waitfordebug",
 | 
						    logFilter,
 | 
				
			||||||
		        waitForDebug,
 | 
						    "Logging categories to display. (same syntax as QT_LOGGING_RULES)"
 | 
				
			||||||
		        "Wait for a debugger to attach to the given port before launching."
 | 
						);
 | 
				
			||||||
		    )
 | 
					 | 
				
			||||||
		    ->needs(debugPortArg);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/// ---
 | 
						readLog->add_flag("--no-time", logNoTime, "Do not print timestamps of log messages.");
 | 
				
			||||||
		auto sparseLogsOnly = false;
 | 
						readLog->add_flag("--no-color", info.noColor, "Do not color the log output. (Env:NO_COLOR)");
 | 
				
			||||||
		app.add_flag("--info", printInfo, "Print information about the shell")->excludes(debugPortArg);
 | 
					 | 
				
			||||||
		app.add_flag("--no-color", noColor, "Do not color the log output. (Env:NO_COLOR)");
 | 
					 | 
				
			||||||
		auto* printVersion = app.add_flag("-V,--version", "Print quickshell's version, then exit.");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		app.add_flag(
 | 
						try {
 | 
				
			||||||
		    "--no-detailed-logs",
 | 
							app.parse(argc, argv);
 | 
				
			||||||
		    sparseLogsOnly,
 | 
						} catch (const CLI::ParseError& e) {
 | 
				
			||||||
		    "Do not enable this unless you know what you are doing."
 | 
							exit(app.exit(e)); // NOLINT
 | 
				
			||||||
		);
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/// ---
 | 
						if (*printVersion) {
 | 
				
			||||||
		QStringOption logPath;
 | 
							std::cout << "quickshell pre-release, revision: " << GIT_REVISION << std::endl;
 | 
				
			||||||
		QStringOption logFilter;
 | 
							exit(0); // NOLINT
 | 
				
			||||||
		auto logNoTime = false;
 | 
						} else if (*readLog) {
 | 
				
			||||||
 | 
							auto file = QFile(*logPath);
 | 
				
			||||||
		auto* readLog = app.add_subcommand("read-log", "Read a quickshell log file.");
 | 
							if (!file.open(QFile::ReadOnly)) {
 | 
				
			||||||
		readLog->add_option("path", logPath, "Path to the log file to read")->required();
 | 
								qCritical() << "Failed to open log for reading:" << *logPath;
 | 
				
			||||||
 | 
								exit(-1); // NOLINT
 | 
				
			||||||
		readLog->add_option(
 | 
					 | 
				
			||||||
		    "-f,--filter",
 | 
					 | 
				
			||||||
		    logFilter,
 | 
					 | 
				
			||||||
		    "Logging categories to display. (same syntax as QT_LOGGING_RULES)"
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		readLog->add_flag("--no-time", logNoTime, "Do not print timestamps of log messages.");
 | 
					 | 
				
			||||||
		readLog->add_flag("--no-color", noColor, "Do not color the log output. (Env:NO_COLOR)");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		CLI11_PARSE(app, argc, argv);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		const auto qApplication = QCoreApplication(qArgC, qArgV);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Start log manager - has to happen with an active event loop or offthread can't be started.
 | 
					 | 
				
			||||||
		LogManager::init(!noColor, sparseLogsOnly);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (*printVersion) {
 | 
					 | 
				
			||||||
			std::cout << "quickshell pre-release, revision: " << GIT_REVISION << std::endl;
 | 
					 | 
				
			||||||
			return 0;
 | 
					 | 
				
			||||||
		} if (*readLog) {
 | 
					 | 
				
			||||||
			auto file = QFile(*logPath);
 | 
					 | 
				
			||||||
			if (!file.open(QFile::ReadOnly)) {
 | 
					 | 
				
			||||||
				qCritical() << "Failed to open log for reading:" << *logPath;
 | 
					 | 
				
			||||||
				return -1;
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				qInfo() << "Reading log" << *logPath;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return qs::log::readEncodedLogs(&file, !logNoTime, *logFilter) ? 0 : -1;
 | 
					 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
 | 
								qInfo() << "Reading log" << *logPath;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// NOLINTBEGIN
 | 
							exit( // NOLINT
 | 
				
			||||||
 | 
							    qs::log::readEncodedLogs(&file, !logNoTime, *logFilter) ? 0 : -1
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QString commandConfigPath(QString path, QString manifest, QString config, bool printInfo) {
 | 
				
			||||||
 | 
						// NOLINTBEGIN
 | 
				
			||||||
#define CHECK(rname, name, level, label, expr)                                                     \
 | 
					#define CHECK(rname, name, level, label, expr)                                                     \
 | 
				
			||||||
	QString name = expr;                                                                             \
 | 
						QString name = expr;                                                                             \
 | 
				
			||||||
	if (rname.isEmpty() && !name.isEmpty()) {                                                        \
 | 
						if (rname.isEmpty() && !name.isEmpty()) {                                                        \
 | 
				
			||||||
| 
						 | 
					@ -189,231 +195,341 @@ int qs_main(int argc, char** argv) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define OPTSTR(name) (name.isEmpty() ? "(unset)" : name.toStdString())
 | 
					#define OPTSTR(name) (name.isEmpty() ? "(unset)" : name.toStdString())
 | 
				
			||||||
			// NOLINTEND
 | 
						// NOLINTEND
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			QString basePath;
 | 
						QString basePath;
 | 
				
			||||||
			int basePathLevel = 0;
 | 
						int basePathLevel = 0;
 | 
				
			||||||
			Q_UNUSED(basePathLevel);
 | 
						Q_UNUSED(basePathLevel);
 | 
				
			||||||
			{
 | 
						{
 | 
				
			||||||
				// NOLINTBEGIN
 | 
							// NOLINTBEGIN
 | 
				
			||||||
				// clang-format off
 | 
							// clang-format off
 | 
				
			||||||
				CHECK(basePath, envBasePath, 0, foundbase, qEnvironmentVariable("QS_BASE_PATH"));
 | 
							CHECK(basePath, envBasePath, 0, foundbase, qEnvironmentVariable("QS_BASE_PATH"));
 | 
				
			||||||
				CHECK(basePath, defaultBasePath, 0, foundbase, QDir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)).filePath("quickshell"));
 | 
							CHECK(basePath, defaultBasePath, 0, foundbase, QDir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)).filePath("quickshell"));
 | 
				
			||||||
				// clang-format on
 | 
							// clang-format on
 | 
				
			||||||
				// NOLINTEND
 | 
							// NOLINTEND
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (printInfo) {
 | 
							if (printInfo) {
 | 
				
			||||||
					// clang-format off
 | 
								// clang-format off
 | 
				
			||||||
					std::cout << "Base path: " << OPTSTR(basePath) << "\n";
 | 
								std::cout << "Base path: " << OPTSTR(basePath) << "\n";
 | 
				
			||||||
					std::cout << " - Environment (QS_BASE_PATH): " << OPTSTR(envBasePath) << "\n";
 | 
								std::cout << " - Environment (QS_BASE_PATH): " << OPTSTR(envBasePath) << "\n";
 | 
				
			||||||
					std::cout << " - Default: " << OPTSTR(defaultBasePath) << "\n";
 | 
								std::cout << " - Default: " << OPTSTR(defaultBasePath) << "\n";
 | 
				
			||||||
					// clang-format on
 | 
								// clang-format on
 | 
				
			||||||
				}
 | 
							}
 | 
				
			||||||
			}
 | 
						}
 | 
				
			||||||
		foundbase:;
 | 
					foundbase:;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			QString configPath;
 | 
						QString configPath;
 | 
				
			||||||
			int configPathLevel = 10;
 | 
						int configPathLevel = 10;
 | 
				
			||||||
			{
 | 
						{
 | 
				
			||||||
				// NOLINTBEGIN
 | 
							// NOLINTBEGIN
 | 
				
			||||||
				CHECK(configPath, optionConfigPath, 0, foundpath, *path);
 | 
							CHECK(configPath, optionConfigPath, 0, foundpath, path);
 | 
				
			||||||
				CHECK(configPath, envConfigPath, 1, foundpath, qEnvironmentVariable("QS_CONFIG_PATH"));
 | 
							CHECK(configPath, envConfigPath, 1, foundpath, qEnvironmentVariable("QS_CONFIG_PATH"));
 | 
				
			||||||
				// NOLINTEND
 | 
							// NOLINTEND
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (printInfo) {
 | 
							if (printInfo) {
 | 
				
			||||||
					// clang-format off
 | 
								// clang-format off
 | 
				
			||||||
					std::cout << "\nConfig path: " << OPTSTR(configPath) << "\n";
 | 
								std::cout << "\nConfig path: " << OPTSTR(configPath) << "\n";
 | 
				
			||||||
					std::cout << " - Option: " << OPTSTR(optionConfigPath) << "\n";
 | 
								std::cout << " - Option: " << OPTSTR(optionConfigPath) << "\n";
 | 
				
			||||||
					std::cout << " - Environment (QS_CONFIG_PATH): " << OPTSTR(envConfigPath) << "\n";
 | 
								std::cout << " - Environment (QS_CONFIG_PATH): " << OPTSTR(envConfigPath) << "\n";
 | 
				
			||||||
					// clang-format on
 | 
								// clang-format on
 | 
				
			||||||
				}
 | 
							}
 | 
				
			||||||
			}
 | 
						}
 | 
				
			||||||
		foundpath:;
 | 
					foundpath:;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			QString manifestPath;
 | 
						QString manifestPath;
 | 
				
			||||||
			int manifestPathLevel = 10;
 | 
						int manifestPathLevel = 10;
 | 
				
			||||||
			{
 | 
						{
 | 
				
			||||||
				// NOLINTBEGIN
 | 
							// NOLINTBEGIN
 | 
				
			||||||
				// clang-format off
 | 
							// clang-format off
 | 
				
			||||||
				CHECK(manifestPath, optionManifestPath, 0, foundmf, *manifest);
 | 
							CHECK(manifestPath, optionManifestPath, 0, foundmf, manifest);
 | 
				
			||||||
				CHECK(manifestPath, envManifestPath, 1, foundmf, qEnvironmentVariable("QS_MANIFEST"));
 | 
							CHECK(manifestPath, envManifestPath, 1, foundmf, qEnvironmentVariable("QS_MANIFEST"));
 | 
				
			||||||
				CHECK(manifestPath, defaultManifestPath, 2, foundmf, QDir(basePath).filePath("manifest.conf"));
 | 
							CHECK(manifestPath, defaultManifestPath, 2, foundmf, QDir(basePath).filePath("manifest.conf"));
 | 
				
			||||||
				// clang-format on
 | 
							// clang-format on
 | 
				
			||||||
				// NOLINTEND
 | 
							// NOLINTEND
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (printInfo) {
 | 
							if (printInfo) {
 | 
				
			||||||
					// clang-format off
 | 
								// clang-format off
 | 
				
			||||||
					std::cout << "\nManifest path: " << OPTSTR(manifestPath) << "\n";
 | 
								std::cout << "\nManifest path: " << OPTSTR(manifestPath) << "\n";
 | 
				
			||||||
					std::cout << " - Option: " << OPTSTR(optionManifestPath) << "\n";
 | 
								std::cout << " - Option: " << OPTSTR(optionManifestPath) << "\n";
 | 
				
			||||||
					std::cout << " - Environment (QS_MANIFEST): " << OPTSTR(envManifestPath) << "\n";
 | 
								std::cout << " - Environment (QS_MANIFEST): " << OPTSTR(envManifestPath) << "\n";
 | 
				
			||||||
					std::cout << " - Default: " << OPTSTR(defaultManifestPath) << "\n";
 | 
								std::cout << " - Default: " << OPTSTR(defaultManifestPath) << "\n";
 | 
				
			||||||
					// clang-format on
 | 
								// clang-format on
 | 
				
			||||||
				}
 | 
							}
 | 
				
			||||||
			}
 | 
						}
 | 
				
			||||||
		foundmf:;
 | 
					foundmf:;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			QString configName;
 | 
						QString configName;
 | 
				
			||||||
			int configNameLevel = 10;
 | 
						int configNameLevel = 10;
 | 
				
			||||||
			{
 | 
						{
 | 
				
			||||||
				// NOLINTBEGIN
 | 
							// NOLINTBEGIN
 | 
				
			||||||
				CHECK(configName, optionConfigName, 0, foundname, *config);
 | 
							CHECK(configName, optionConfigName, 0, foundname, config);
 | 
				
			||||||
				CHECK(configName, envConfigName, 1, foundname, qEnvironmentVariable("QS_CONFIG_NAME"));
 | 
							CHECK(configName, envConfigName, 1, foundname, qEnvironmentVariable("QS_CONFIG_NAME"));
 | 
				
			||||||
				// NOLINTEND
 | 
							// NOLINTEND
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (printInfo) {
 | 
							if (printInfo) {
 | 
				
			||||||
					// clang-format off
 | 
								// clang-format off
 | 
				
			||||||
					std::cout << "\nConfig name: " << OPTSTR(configName) << "\n";
 | 
								std::cout << "\nConfig name: " << OPTSTR(configName) << "\n";
 | 
				
			||||||
					std::cout << " - Option: " << OPTSTR(optionConfigName) << "\n";
 | 
								std::cout << " - Option: " << OPTSTR(optionConfigName) << "\n";
 | 
				
			||||||
					std::cout << " - Environment (QS_CONFIG_NAME): " << OPTSTR(envConfigName) << "\n\n";
 | 
								std::cout << " - Environment (QS_CONFIG_NAME): " << OPTSTR(envConfigName) << "\n\n";
 | 
				
			||||||
					// clang-format on
 | 
								// clang-format on
 | 
				
			||||||
				}
 | 
							}
 | 
				
			||||||
			}
 | 
						}
 | 
				
			||||||
		foundname:;
 | 
					foundname:;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (!configPath.isEmpty() && configPathLevel <= configNameLevel) {
 | 
						QString configFilePath;
 | 
				
			||||||
				configFilePath = configPath;
 | 
					 | 
				
			||||||
			} else if (!configName.isEmpty()) {
 | 
					 | 
				
			||||||
				if (!manifestPath.isEmpty()) {
 | 
					 | 
				
			||||||
					auto file = QFile(manifestPath);
 | 
					 | 
				
			||||||
					if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
 | 
					 | 
				
			||||||
						auto stream = QTextStream(&file);
 | 
					 | 
				
			||||||
						while (!stream.atEnd()) {
 | 
					 | 
				
			||||||
							auto line = stream.readLine();
 | 
					 | 
				
			||||||
							if (line.trimmed().startsWith("#")) continue;
 | 
					 | 
				
			||||||
							if (line.trimmed().isEmpty()) continue;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
							auto split = line.split('=');
 | 
						if (!configPath.isEmpty() && configPathLevel <= configNameLevel) {
 | 
				
			||||||
							if (split.length() != 2) {
 | 
							configFilePath = configPath;
 | 
				
			||||||
								qCritical() << "manifest line not in expected format 'name = relativepath':"
 | 
						} else if (!configName.isEmpty()) {
 | 
				
			||||||
								            << line;
 | 
							if (!manifestPath.isEmpty()) {
 | 
				
			||||||
								return -1;
 | 
								auto file = QFile(manifestPath);
 | 
				
			||||||
							}
 | 
								if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
 | 
				
			||||||
 | 
									auto stream = QTextStream(&file);
 | 
				
			||||||
 | 
									while (!stream.atEnd()) {
 | 
				
			||||||
 | 
										auto line = stream.readLine();
 | 
				
			||||||
 | 
										if (line.trimmed().startsWith("#")) continue;
 | 
				
			||||||
 | 
										if (line.trimmed().isEmpty()) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							if (split[0].trimmed() == configName) {
 | 
										auto split = line.split('=');
 | 
				
			||||||
								configFilePath = QDir(QFileInfo(file).canonicalPath()).filePath(split[1].trimmed());
 | 
										if (split.length() != 2) {
 | 
				
			||||||
								goto haspath; // NOLINT
 | 
											qCritical() << "manifest line not in expected format 'name = relativepath':" << line;
 | 
				
			||||||
							}
 | 
											exit(-1); // NOLINT
 | 
				
			||||||
						}
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						qCritical() << "configuration" << configName << "not found in manifest" << manifestPath;
 | 
										if (split[0].trimmed() == configName) {
 | 
				
			||||||
						return -1;
 | 
											configFilePath = QDir(QFileInfo(file).canonicalPath()).filePath(split[1].trimmed());
 | 
				
			||||||
					} else if (manifestPathLevel < 2) {
 | 
											goto foundp;
 | 
				
			||||||
						qCritical() << "cannot open config manifest at" << manifestPath;
 | 
					 | 
				
			||||||
						return -1;
 | 
					 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				{
 | 
									qCritical() << "configuration" << configName << "not found in manifest" << manifestPath;
 | 
				
			||||||
					auto basePathInfo = QFileInfo(basePath);
 | 
									exit(-1); // NOLINT
 | 
				
			||||||
					if (!basePathInfo.exists()) {
 | 
								} else if (manifestPathLevel < 2) {
 | 
				
			||||||
						qCritical() << "base path does not exist:" << basePath;
 | 
									qCritical() << "cannot open config manifest at" << manifestPath;
 | 
				
			||||||
						return -1;
 | 
									exit(-1); // NOLINT
 | 
				
			||||||
					} else if (!QFileInfo(basePathInfo.canonicalFilePath()).isDir()) {
 | 
								}
 | 
				
			||||||
						qCritical() << "base path is not a directory" << basePath;
 | 
							}
 | 
				
			||||||
						return -1;
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
					auto dir = QDir(basePath);
 | 
							{
 | 
				
			||||||
					for (auto& entry: dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) {
 | 
								auto basePathInfo = QFileInfo(basePath);
 | 
				
			||||||
						if (entry == configName) {
 | 
								if (!basePathInfo.exists()) {
 | 
				
			||||||
							configFilePath = dir.filePath(entry);
 | 
									qCritical() << "base path does not exist:" << basePath;
 | 
				
			||||||
							goto haspath; // NOLINT
 | 
									exit(-1); // NOLINT
 | 
				
			||||||
						}
 | 
								} else if (!QFileInfo(basePathInfo.canonicalFilePath()).isDir()) {
 | 
				
			||||||
					}
 | 
									qCritical() << "base path is not a directory" << basePath;
 | 
				
			||||||
 | 
									exit(-1); // NOLINT
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					qCritical() << "no directory named " << configName << "found in base path" << basePath;
 | 
								auto dir = QDir(basePath);
 | 
				
			||||||
					return -1;
 | 
								for (auto& entry: dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) {
 | 
				
			||||||
 | 
									if (entry == configName) {
 | 
				
			||||||
 | 
										configFilePath = dir.filePath(entry);
 | 
				
			||||||
 | 
										goto foundp;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			haspath:;
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				configFilePath = basePath;
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			auto configFile = QFileInfo(configFilePath);
 | 
								qCritical() << "no directory named " << configName << "found in base path" << basePath;
 | 
				
			||||||
			if (!configFile.exists()) {
 | 
								exit(-1); // NOLINT
 | 
				
			||||||
				qCritical() << "config path does not exist:" << configFilePath;
 | 
							}
 | 
				
			||||||
				return -1;
 | 
						} else {
 | 
				
			||||||
			}
 | 
							configFilePath = basePath;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (configFile.isDir()) {
 | 
					foundp:;
 | 
				
			||||||
				configFilePath = QDir(configFilePath).filePath("shell.qml");
 | 
						auto configFile = QFileInfo(configFilePath);
 | 
				
			||||||
			}
 | 
						if (!configFile.exists()) {
 | 
				
			||||||
 | 
							qCritical() << "config path does not exist:" << configFilePath;
 | 
				
			||||||
 | 
							exit(-1); // NOLINT
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			configFile = QFileInfo(configFilePath);
 | 
						if (configFile.isDir()) {
 | 
				
			||||||
			if (!configFile.exists()) {
 | 
							configFilePath = QDir(configFilePath).filePath("shell.qml");
 | 
				
			||||||
				qCritical() << "no shell.qml found in config path:" << configFilePath;
 | 
						}
 | 
				
			||||||
				return -1;
 | 
					 | 
				
			||||||
			} else if (configFile.isDir()) {
 | 
					 | 
				
			||||||
				qCritical() << "shell.qml is a directory:" << configFilePath;
 | 
					 | 
				
			||||||
				return -1;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			configFilePath = QFileInfo(configFilePath).canonicalFilePath();
 | 
						configFile = QFileInfo(configFilePath);
 | 
				
			||||||
			configFile = QFileInfo(configFilePath);
 | 
						if (!configFile.exists()) {
 | 
				
			||||||
			if (!configFile.exists()) {
 | 
							qCritical() << "no shell.qml found in config path:" << configFilePath;
 | 
				
			||||||
				qCritical() << "config file does not exist:" << configFilePath;
 | 
							exit(-1); // NOLINT
 | 
				
			||||||
				return -1;
 | 
						} else if (configFile.isDir()) {
 | 
				
			||||||
			} else if (configFile.isDir()) {
 | 
							qCritical() << "shell.qml is a directory:" << configFilePath;
 | 
				
			||||||
				qCritical() << "config file is a directory:" << configFilePath;
 | 
							exit(-1); // NOLINT
 | 
				
			||||||
				return -1;
 | 
						}
 | 
				
			||||||
			}
 | 
					
 | 
				
			||||||
 | 
						configFilePath = QFileInfo(configFilePath).canonicalFilePath();
 | 
				
			||||||
 | 
						configFile = QFileInfo(configFilePath);
 | 
				
			||||||
 | 
						if (!configFile.exists()) {
 | 
				
			||||||
 | 
							qCritical() << "config file does not exist:" << configFilePath;
 | 
				
			||||||
 | 
							exit(-1); // NOLINT
 | 
				
			||||||
 | 
						} else if (configFile.isDir()) {
 | 
				
			||||||
 | 
							qCritical() << "config file is a directory:" << configFilePath;
 | 
				
			||||||
 | 
							exit(-1); // NOLINT
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#undef CHECK
 | 
					#undef CHECK
 | 
				
			||||||
#undef OPTSTR
 | 
					#undef OPTSTR
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			shellId = QCryptographicHash::hash(configFilePath.toUtf8(), QCryptographicHash::Md5).toHex();
 | 
						return configFilePath;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			qInfo() << "Config file path:" << configFilePath;
 | 
					int qs_main(int argc, char** argv) {
 | 
				
			||||||
 | 
					#if CRASH_REPORTER
 | 
				
			||||||
 | 
						qsCheckCrash(argc, argv);
 | 
				
			||||||
 | 
						auto crashHandler = qs::crash::CrashHandler();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (!QFile(configFilePath).exists()) {
 | 
						auto qArgC = 1;
 | 
				
			||||||
				qCritical() << "config file does not exist";
 | 
						auto* qArgV = argv;
 | 
				
			||||||
				return -1;
 | 
					
 | 
				
			||||||
 | 
						QString configFilePath;
 | 
				
			||||||
 | 
						QString initialWorkdir;
 | 
				
			||||||
 | 
						QString shellId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int debugPort = -1;
 | 
				
			||||||
 | 
						bool waitForDebug = false;
 | 
				
			||||||
 | 
						bool printInfo = false;
 | 
				
			||||||
 | 
						bool noColor = !qEnvironmentVariableIsEmpty("NO_COLOR");
 | 
				
			||||||
 | 
						bool sparseLogsOnly = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto useQApplication = false;
 | 
				
			||||||
 | 
						auto nativeTextRendering = false;
 | 
				
			||||||
 | 
						auto desktopSettingsAware = true;
 | 
				
			||||||
 | 
						QHash<QString, QString> envOverrides;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const auto qApplication = QCoreApplication(qArgC, qArgV);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if CRASH_REPORTER
 | 
				
			||||||
 | 
							auto lastInfoFdStr = qEnvironmentVariable("__QUICKSHELL_CRASH_INFO_FD");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!lastInfoFdStr.isEmpty()) {
 | 
				
			||||||
 | 
								auto lastInfoFd = lastInfoFdStr.toInt();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								QFile file;
 | 
				
			||||||
 | 
								file.open(lastInfoFd, QFile::ReadOnly, QFile::AutoCloseHandle);
 | 
				
			||||||
 | 
								file.seek(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								auto ds = QDataStream(&file);
 | 
				
			||||||
 | 
								InstanceInfo info;
 | 
				
			||||||
 | 
								ds >> info;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								configFilePath = info.configPath;
 | 
				
			||||||
 | 
								initialWorkdir = info.initialWorkdir;
 | 
				
			||||||
 | 
								noColor = info.noColor;
 | 
				
			||||||
 | 
								sparseLogsOnly = info.sparseLogsOnly;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								LogManager::init(!noColor, sparseLogsOnly);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								qCritical().nospace() << "Quickshell has crashed under pid "
 | 
				
			||||||
 | 
								                      << qEnvironmentVariable("__QUICKSHELL_CRASH_DUMP_PID").toInt()
 | 
				
			||||||
 | 
								                      << " (Coredumps will be available under that pid.)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								qCritical() << "Further crash information is stored under"
 | 
				
			||||||
 | 
								            << QsPaths::crashDir(info.shellId, info.launchTime).path();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (info.launchTime.msecsTo(QDateTime::currentDateTime()) < 10000) {
 | 
				
			||||||
 | 
									qCritical() << "Quickshell crashed within 10 seconds of launching. Not restarting to avoid "
 | 
				
			||||||
 | 
									               "a crash loop.";
 | 
				
			||||||
 | 
									return 0;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									qCritical() << "Quickshell has been restarted.";
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			auto file = QFile(configFilePath);
 | 
								crashHandler.init();
 | 
				
			||||||
			if (!file.open(QFile::ReadOnly | QFile::Text)) {
 | 
							} else
 | 
				
			||||||
				qCritical() << "could not open config file";
 | 
					#endif
 | 
				
			||||||
				return -1;
 | 
							{
 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			auto stream = QTextStream(&file);
 | 
								auto command = CommandInfo {
 | 
				
			||||||
			while (!stream.atEnd()) {
 | 
								    .initialWorkdir = initialWorkdir,
 | 
				
			||||||
				auto line = stream.readLine().trimmed();
 | 
								    .debugPort = debugPort,
 | 
				
			||||||
				if (line.startsWith("//@ pragma ")) {
 | 
								    .waitForDebug = waitForDebug,
 | 
				
			||||||
					auto pragma = line.sliced(11).trimmed();
 | 
								    .printInfo = printInfo,
 | 
				
			||||||
 | 
								    .noColor = noColor,
 | 
				
			||||||
 | 
								    .sparseLogsOnly = sparseLogsOnly,
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					if (pragma == "UseQApplication") useQApplication = true;
 | 
								processCommand(argc, argv, command);
 | 
				
			||||||
					else if (pragma == "NativeTextRendering") nativeTextRendering = true;
 | 
					 | 
				
			||||||
					else if (pragma == "IgnoreSystemSettings") desktopSettingsAware = false;
 | 
					 | 
				
			||||||
					else if (pragma.startsWith("Env ")) {
 | 
					 | 
				
			||||||
						auto envPragma = pragma.sliced(4);
 | 
					 | 
				
			||||||
						auto splitIdx = envPragma.indexOf('=');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
						if (splitIdx == -1) {
 | 
								// Start log manager - has to happen with an active event loop or offthread can't be started.
 | 
				
			||||||
							qCritical() << "Env pragma" << pragma << "not in the form 'VAR = VALUE'";
 | 
								LogManager::init(!noColor, sparseLogsOnly);
 | 
				
			||||||
							return -1;
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
						auto var = envPragma.sliced(0, splitIdx).trimmed();
 | 
					#if CRASH_REPORTER
 | 
				
			||||||
						auto val = envPragma.sliced(splitIdx + 1).trimmed();
 | 
								// Started after log manager for pretty debug logs. Unlikely anything will crash before this point, but
 | 
				
			||||||
						envOverrides.insert(var, val);
 | 
								// this can be moved if it happens.
 | 
				
			||||||
					} else if (pragma.startsWith("ShellId ")) {
 | 
								crashHandler.init();
 | 
				
			||||||
						shellId = pragma.sliced(8).trimmed();
 | 
					#endif
 | 
				
			||||||
					} else {
 | 
					
 | 
				
			||||||
						qCritical() << "Unrecognized pragma" << pragma;
 | 
								configFilePath = commandConfigPath(
 | 
				
			||||||
 | 
								    command.configPath,
 | 
				
			||||||
 | 
								    command.manifestPath,
 | 
				
			||||||
 | 
								    command.configName,
 | 
				
			||||||
 | 
								    command.printInfo
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							shellId = QCryptographicHash::hash(configFilePath.toUtf8(), QCryptographicHash::Md5).toHex();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							qInfo() << "Config file path:" << configFilePath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!QFile(configFilePath).exists()) {
 | 
				
			||||||
 | 
								qCritical() << "config file does not exist";
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							auto file = QFile(configFilePath);
 | 
				
			||||||
 | 
							if (!file.open(QFile::ReadOnly | QFile::Text)) {
 | 
				
			||||||
 | 
								qCritical() << "could not open config file";
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							auto stream = QTextStream(&file);
 | 
				
			||||||
 | 
							while (!stream.atEnd()) {
 | 
				
			||||||
 | 
								auto line = stream.readLine().trimmed();
 | 
				
			||||||
 | 
								if (line.startsWith("//@ pragma ")) {
 | 
				
			||||||
 | 
									auto pragma = line.sliced(11).trimmed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (pragma == "UseQApplication") useQApplication = true;
 | 
				
			||||||
 | 
									else if (pragma == "NativeTextRendering") nativeTextRendering = true;
 | 
				
			||||||
 | 
									else if (pragma == "IgnoreSystemSettings") desktopSettingsAware = false;
 | 
				
			||||||
 | 
									else if (pragma.startsWith("Env ")) {
 | 
				
			||||||
 | 
										auto envPragma = pragma.sliced(4);
 | 
				
			||||||
 | 
										auto splitIdx = envPragma.indexOf('=');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if (splitIdx == -1) {
 | 
				
			||||||
 | 
											qCritical() << "Env pragma" << pragma << "not in the form 'VAR = VALUE'";
 | 
				
			||||||
						return -1;
 | 
											return -1;
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				} else if (line.startsWith("import")) break;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			file.close();
 | 
										auto var = envPragma.sliced(0, splitIdx).trimmed();
 | 
				
			||||||
 | 
										auto val = envPragma.sliced(splitIdx + 1).trimmed();
 | 
				
			||||||
 | 
										envOverrides.insert(var, val);
 | 
				
			||||||
 | 
									} else if (pragma.startsWith("ShellId ")) {
 | 
				
			||||||
 | 
										shellId = pragma.sliced(8).trimmed();
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										qCritical() << "Unrecognized pragma" << pragma;
 | 
				
			||||||
 | 
										return -1;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else if (line.startsWith("import")) break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							file.close();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	qInfo() << "Shell ID:" << shellId;
 | 
						qInfo() << "Shell ID:" << shellId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (printInfo) return 0;
 | 
						if (printInfo) return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if CRASH_REPORTER
 | 
				
			||||||
 | 
						crashHandler.setInstanceInfo(InstanceInfo {
 | 
				
			||||||
 | 
						    .configPath = configFilePath,
 | 
				
			||||||
 | 
						    .shellId = shellId,
 | 
				
			||||||
 | 
						    .initialWorkdir = initialWorkdir,
 | 
				
			||||||
 | 
						    .launchTime = qs::Common::LAUNCH_TIME,
 | 
				
			||||||
 | 
						    .noColor = noColor,
 | 
				
			||||||
 | 
						    .sparseLogsOnly = sparseLogsOnly,
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (auto [var, val]: envOverrides.asKeyValueRange()) {
 | 
						for (auto [var, val]: envOverrides.asKeyValueRange()) {
 | 
				
			||||||
		qputenv(var.toUtf8(), val.toUtf8());
 | 
							qputenv(var.toUtf8(), val.toUtf8());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -484,8 +600,8 @@ int qs_main(int argc, char** argv) {
 | 
				
			||||||
		QQmlDebuggingEnabler::startTcpDebugServer(debugPort, wait);
 | 
							QQmlDebuggingEnabler::startTcpDebugServer(debugPort, wait);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!workingDirectory.isEmpty()) {
 | 
						if (!initialWorkdir.isEmpty()) {
 | 
				
			||||||
		QDir::setCurrent(workingDirectory);
 | 
							QDir::setCurrent(initialWorkdir);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QuickshellPlugin::initPlugins();
 | 
						QuickshellPlugin::initPlugins();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,8 @@
 | 
				
			||||||
#include <qtenvironmentvariables.h>
 | 
					#include <qtenvironmentvariables.h>
 | 
				
			||||||
#include <unistd.h>
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "common.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Q_LOGGING_CATEGORY(logPaths, "quickshell.paths", QtWarningMsg);
 | 
					Q_LOGGING_CATEGORY(logPaths, "quickshell.paths", QtWarningMsg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
QsPaths* QsPaths::instance() {
 | 
					QsPaths* QsPaths::instance() {
 | 
				
			||||||
| 
						 | 
					@ -18,6 +20,15 @@ QsPaths* QsPaths::instance() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void QsPaths::init(QString shellId) { QsPaths::instance()->shellId = std::move(shellId); }
 | 
					void QsPaths::init(QString shellId) { QsPaths::instance()->shellId = std::move(shellId); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QDir QsPaths::crashDir(const QString& shellId, const QDateTime& launchTime) {
 | 
				
			||||||
 | 
						auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
 | 
				
			||||||
 | 
						dir = QDir(dir.filePath("crashes"));
 | 
				
			||||||
 | 
						dir = QDir(dir.filePath(shellId));
 | 
				
			||||||
 | 
						dir = QDir(dir.filePath(QString("run-%1").arg(launchTime.toMSecsSinceEpoch())));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return dir;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
QDir* QsPaths::cacheDir() {
 | 
					QDir* QsPaths::cacheDir() {
 | 
				
			||||||
	if (this->cacheState == DirState::Unknown) {
 | 
						if (this->cacheState == DirState::Unknown) {
 | 
				
			||||||
		auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
 | 
							auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
 | 
				
			||||||
| 
						 | 
					@ -73,7 +84,7 @@ QDir* QsPaths::instanceRunDir() {
 | 
				
			||||||
			this->instanceRunState = DirState::Failed;
 | 
								this->instanceRunState = DirState::Failed;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			this->mInstanceRunDir =
 | 
								this->mInstanceRunDir =
 | 
				
			||||||
			    runtimeDir->filePath(QString("run-%1").arg(QDateTime::currentMSecsSinceEpoch()));
 | 
								    runtimeDir->filePath(QString("run-%1").arg(qs::Common::LAUNCH_TIME.toMSecsSinceEpoch()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			qCDebug(logPaths) << "Initialized instance runtime path:" << this->mInstanceRunDir.path();
 | 
								qCDebug(logPaths) << "Initialized instance runtime path:" << this->mInstanceRunDir.path();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,12 @@
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					#include <qdatetime.h>
 | 
				
			||||||
#include <qdir.h>
 | 
					#include <qdir.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class QsPaths {
 | 
					class QsPaths {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	static QsPaths* instance();
 | 
						static QsPaths* instance();
 | 
				
			||||||
	static void init(QString shellId);
 | 
						static void init(QString shellId);
 | 
				
			||||||
 | 
						static QDir crashDir(const QString& shellId, const QDateTime& launchTime);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QDir* cacheDir();
 | 
						QDir* cacheDir();
 | 
				
			||||||
	QDir* runDir();
 | 
						QDir* runDir();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										16
									
								
								src/crash/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/crash/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					qt_add_library(quickshell-crash STATIC
 | 
				
			||||||
 | 
						main.cpp
 | 
				
			||||||
 | 
						interface.cpp
 | 
				
			||||||
 | 
						handler.cpp
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					qs_pch(quickshell-crash)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					find_package(PkgConfig REQUIRED)
 | 
				
			||||||
 | 
					pkg_check_modules(breakpad REQUIRED IMPORTED_TARGET breakpad)
 | 
				
			||||||
 | 
					# only need client?? take only includes from pkg config todo
 | 
				
			||||||
 | 
					target_link_libraries(quickshell-crash PRIVATE PkgConfig::breakpad -lbreakpad_client)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					target_link_libraries(quickshell-crash PRIVATE quickshell-build Qt6::Widgets)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					target_link_libraries(quickshell-core PRIVATE quickshell-crash)
 | 
				
			||||||
							
								
								
									
										180
									
								
								src/crash/handler.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								src/crash/handler.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,180 @@
 | 
				
			||||||
 | 
					#include "handler.hpp"
 | 
				
			||||||
 | 
					#include <array>
 | 
				
			||||||
 | 
					#include <cstdio>
 | 
				
			||||||
 | 
					#include <cstring>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <bits/types/sigset_t.h>
 | 
				
			||||||
 | 
					#include <breakpad/client/linux/handler/exception_handler.h>
 | 
				
			||||||
 | 
					#include <breakpad/client/linux/handler/minidump_descriptor.h>
 | 
				
			||||||
 | 
					#include <breakpad/common/linux/linux_libc_support.h>
 | 
				
			||||||
 | 
					#include <qdatastream.h>
 | 
				
			||||||
 | 
					#include <qfile.h>
 | 
				
			||||||
 | 
					#include <qlogging.h>
 | 
				
			||||||
 | 
					#include <qloggingcategory.h>
 | 
				
			||||||
 | 
					#include <sys/mman.h>
 | 
				
			||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../core/crashinfo.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern char** environ; // NOLINT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace google_breakpad;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace qs::crash {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Q_LOGGING_CATEGORY(logCrashHandler, "quickshell.crashhandler", QtWarningMsg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct CrashHandlerPrivate {
 | 
				
			||||||
 | 
						ExceptionHandler* exceptionHandler = nullptr;
 | 
				
			||||||
 | 
						int minidumpFd = -1;
 | 
				
			||||||
 | 
						int infoFd = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static bool minidumpCallback(const MinidumpDescriptor& descriptor, void* context, bool succeeded);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CrashHandler::CrashHandler(): d(new CrashHandlerPrivate()) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void CrashHandler::init() {
 | 
				
			||||||
 | 
						// MinidumpDescriptor has no move constructor and the copy constructor breaks fds.
 | 
				
			||||||
 | 
						auto createHandler = [this](const MinidumpDescriptor& desc) {
 | 
				
			||||||
 | 
							this->d->exceptionHandler = new ExceptionHandler(
 | 
				
			||||||
 | 
							    desc,
 | 
				
			||||||
 | 
							    nullptr,
 | 
				
			||||||
 | 
							    &CrashHandlerPrivate::minidumpCallback,
 | 
				
			||||||
 | 
							    this->d,
 | 
				
			||||||
 | 
							    true,
 | 
				
			||||||
 | 
							    -1
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qCDebug(logCrashHandler) << "Starting crash handler...";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this->d->minidumpFd = memfd_create("quickshell:minidump", MFD_CLOEXEC);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (this->d->minidumpFd == -1) {
 | 
				
			||||||
 | 
							qCCritical(logCrashHandler
 | 
				
			||||||
 | 
							) << "Failed to allocate minidump memfd, minidumps will be saved in the working directory.";
 | 
				
			||||||
 | 
							createHandler(MinidumpDescriptor("."));
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							qCDebug(logCrashHandler) << "Created memfd" << this->d->minidumpFd
 | 
				
			||||||
 | 
							                         << "for holding possible minidumps.";
 | 
				
			||||||
 | 
							createHandler(MinidumpDescriptor(this->d->minidumpFd));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qCInfo(logCrashHandler) << "Crash handler initialized.";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void CrashHandler::setInstanceInfo(const InstanceInfo& info) {
 | 
				
			||||||
 | 
						this->d->infoFd = memfd_create("quickshell:instance_info", MFD_CLOEXEC);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (this->d->infoFd == -1) {
 | 
				
			||||||
 | 
							qCCritical(logCrashHandler
 | 
				
			||||||
 | 
							) << "Failed to allocate instance info memfd, crash recovery will not work.";
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QFile file;
 | 
				
			||||||
 | 
						file.open(this->d->infoFd, QFile::ReadWrite);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QDataStream ds(&file);
 | 
				
			||||||
 | 
						ds << info;
 | 
				
			||||||
 | 
						file.flush();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qCDebug(logCrashHandler) << "Stored instance info in memfd" << this->d->infoFd;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CrashHandler::~CrashHandler() {
 | 
				
			||||||
 | 
						delete this->d->exceptionHandler;
 | 
				
			||||||
 | 
						delete this->d;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool CrashHandlerPrivate::minidumpCallback(
 | 
				
			||||||
 | 
					    const MinidumpDescriptor& /*descriptor*/,
 | 
				
			||||||
 | 
					    void* context,
 | 
				
			||||||
 | 
					    bool /*success*/
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
						// A fork that just dies to ensure the coredump is caught by the system.
 | 
				
			||||||
 | 
						auto coredumpPid = fork();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (coredumpPid == 0) {
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto* self = static_cast<CrashHandlerPrivate*>(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto exe = std::array<char, 4096>();
 | 
				
			||||||
 | 
						if (readlink("/proc/self/exe", exe.data(), exe.size() - 1) == -1) {
 | 
				
			||||||
 | 
							perror("Failed to find crash reporter executable.\n");
 | 
				
			||||||
 | 
							_exit(-1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto arg = std::array<char*, 2> {exe.data(), nullptr};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto env = std::array<char*, 4096>();
 | 
				
			||||||
 | 
						auto envi = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto infoFd = dup(self->infoFd);
 | 
				
			||||||
 | 
						auto infoFdStr = std::array<char, 38>();
 | 
				
			||||||
 | 
						memcpy(infoFdStr.data(), "__QUICKSHELL_CRASH_INFO_FD=-1" /*\0*/, 30);
 | 
				
			||||||
 | 
						if (infoFd != -1) my_uitos(&infoFdStr[27], infoFd, 10);
 | 
				
			||||||
 | 
						env[envi++] = infoFdStr.data();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto corePidStr = std::array<char, 39>();
 | 
				
			||||||
 | 
						memcpy(corePidStr.data(), "__QUICKSHELL_CRASH_DUMP_PID=-1" /*\0*/, 31);
 | 
				
			||||||
 | 
						if (coredumpPid != -1) my_uitos(&corePidStr[28], coredumpPid, 10);
 | 
				
			||||||
 | 
						env[envi++] = corePidStr.data();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto populateEnv = [&]() {
 | 
				
			||||||
 | 
							auto senvi = 0;
 | 
				
			||||||
 | 
							while (envi < 4095) {
 | 
				
			||||||
 | 
								env[envi++] = environ[senvi++]; // NOLINT
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							env[envi] = nullptr;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sigset_t sigset;
 | 
				
			||||||
 | 
						sigemptyset(&sigset);                       // NOLINT (include)
 | 
				
			||||||
 | 
						sigprocmask(SIG_SETMASK, &sigset, nullptr); // NOLINT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto pid = fork();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (pid == -1) {
 | 
				
			||||||
 | 
							perror("Failed to fork and launch crash reporter.\n");
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						} else if (pid == 0) {
 | 
				
			||||||
 | 
							// dup to remove CLOEXEC
 | 
				
			||||||
 | 
							// if already -1 will return -1
 | 
				
			||||||
 | 
							auto dumpFd = dup(self->minidumpFd);
 | 
				
			||||||
 | 
							auto logFd = dup(CrashInfo::INSTANCE.logFd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// allow up to 10 digits, which should never happen
 | 
				
			||||||
 | 
							auto dumpFdStr = std::array<char, 38>();
 | 
				
			||||||
 | 
							auto logFdStr = std::array<char, 37>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							memcpy(dumpFdStr.data(), "__QUICKSHELL_CRASH_DUMP_FD=-1" /*\0*/, 30);
 | 
				
			||||||
 | 
							memcpy(logFdStr.data(), "__QUICKSHELL_CRASH_LOG_FD=-1" /*\0*/, 29);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (dumpFd != -1) my_uitos(&dumpFdStr[27], dumpFd, 10);
 | 
				
			||||||
 | 
							if (logFd != -1) my_uitos(&logFdStr[26], logFd, 10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							env[envi++] = dumpFdStr.data();
 | 
				
			||||||
 | 
							env[envi++] = logFdStr.data();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							populateEnv();
 | 
				
			||||||
 | 
							execve(exe.data(), arg.data(), env.data());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							perror("Failed to launch crash reporter.\n");
 | 
				
			||||||
 | 
							_exit(-1);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							populateEnv();
 | 
				
			||||||
 | 
							execve(exe.data(), arg.data(), env.data());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							perror("Failed to relaunch quickshell.\n");
 | 
				
			||||||
 | 
							_exit(-1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false; // should make sure it hits the system coredump handler
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace qs::crash
 | 
				
			||||||
							
								
								
									
										23
									
								
								src/crash/handler.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/crash/handler.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qtclasshelpermacros.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../core/crashinfo.hpp"
 | 
				
			||||||
 | 
					namespace qs::crash {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct CrashHandlerPrivate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CrashHandler {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						explicit CrashHandler();
 | 
				
			||||||
 | 
						~CrashHandler();
 | 
				
			||||||
 | 
						Q_DISABLE_COPY_MOVE(CrashHandler);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void init();
 | 
				
			||||||
 | 
						void setInstanceInfo(const InstanceInfo& info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						CrashHandlerPrivate* d;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace qs::crash
 | 
				
			||||||
							
								
								
									
										97
									
								
								src/crash/interface.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/crash/interface.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,97 @@
 | 
				
			||||||
 | 
					#include "interface.hpp"
 | 
				
			||||||
 | 
					#include <utility>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qapplication.h>
 | 
				
			||||||
 | 
					#include <qboxlayout.h>
 | 
				
			||||||
 | 
					#include <qdesktopservices.h>
 | 
				
			||||||
 | 
					#include <qfont.h>
 | 
				
			||||||
 | 
					#include <qfontinfo.h>
 | 
				
			||||||
 | 
					#include <qlabel.h>
 | 
				
			||||||
 | 
					#include <qnamespace.h>
 | 
				
			||||||
 | 
					#include <qobject.h>
 | 
				
			||||||
 | 
					#include <qpushbutton.h>
 | 
				
			||||||
 | 
					#include <qwidget.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "build.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ReportLabel: public QWidget {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						ReportLabel(const QString& label, const QString& content, QWidget* parent): QWidget(parent) {
 | 
				
			||||||
 | 
							auto* layout = new QHBoxLayout(this);
 | 
				
			||||||
 | 
							layout->setContentsMargins(0, 0, 0, 0);
 | 
				
			||||||
 | 
							layout->addWidget(new QLabel(label, this));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							auto* cl = new QLabel(content, this);
 | 
				
			||||||
 | 
							cl->setTextInteractionFlags(Qt::TextSelectableByMouse);
 | 
				
			||||||
 | 
							layout->addWidget(cl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							layout->addStretch();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CrashReporterGui::CrashReporterGui(QString reportFolder, int pid)
 | 
				
			||||||
 | 
					    : reportFolder(std::move(reportFolder)) {
 | 
				
			||||||
 | 
						this->setWindowFlags(Qt::Window);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto textHeight = QFontInfo(QFont()).pixelSize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto* mainLayout = new QVBoxLayout(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mainLayout->addWidget(new QLabel(
 | 
				
			||||||
 | 
						    "<u>Quickshell has crashed. Please submit a bug report to help us fix it.</u>",
 | 
				
			||||||
 | 
						    this
 | 
				
			||||||
 | 
						));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mainLayout->addSpacing(textHeight);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mainLayout->addWidget(new QLabel("General information", this));
 | 
				
			||||||
 | 
						mainLayout->addWidget(new ReportLabel("Git Revision:", GIT_REVISION, this));
 | 
				
			||||||
 | 
						mainLayout->addWidget(new ReportLabel("Crashed process ID:", QString::number(pid), this));
 | 
				
			||||||
 | 
						mainLayout->addWidget(new ReportLabel("Crash report folder:", this->reportFolder, this));
 | 
				
			||||||
 | 
						mainLayout->addSpacing(textHeight);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mainLayout->addWidget(new QLabel("Please open a bug report for this issue via github or email."));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mainLayout->addWidget(new ReportLabel(
 | 
				
			||||||
 | 
						    "Github:",
 | 
				
			||||||
 | 
						    "https://github.com/outfoxxed/quickshell/issues/new?template=crash.yml",
 | 
				
			||||||
 | 
						    this
 | 
				
			||||||
 | 
						));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mainLayout->addWidget(new ReportLabel("Email:", "quickshell-bugs@outfoxxed.me", this));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto* buttons = new QWidget(this);
 | 
				
			||||||
 | 
						buttons->setMinimumWidth(900);
 | 
				
			||||||
 | 
						auto* buttonLayout = new QHBoxLayout(buttons);
 | 
				
			||||||
 | 
						buttonLayout->setContentsMargins(0, 0, 0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto* reportButton = new QPushButton("Open report page", buttons);
 | 
				
			||||||
 | 
						reportButton->setDefault(true);
 | 
				
			||||||
 | 
						QObject::connect(reportButton, &QPushButton::clicked, this, &CrashReporterGui::openReportUrl);
 | 
				
			||||||
 | 
						buttonLayout->addWidget(reportButton);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto* openFolderButton = new QPushButton("Open crash folder", buttons);
 | 
				
			||||||
 | 
						QObject::connect(openFolderButton, &QPushButton::clicked, this, &CrashReporterGui::openFolder);
 | 
				
			||||||
 | 
						buttonLayout->addWidget(openFolderButton);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto* cancelButton = new QPushButton("Exit", buttons);
 | 
				
			||||||
 | 
						QObject::connect(cancelButton, &QPushButton::clicked, this, &CrashReporterGui::cancel);
 | 
				
			||||||
 | 
						buttonLayout->addWidget(cancelButton);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mainLayout->addWidget(buttons);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mainLayout->addStretch();
 | 
				
			||||||
 | 
						this->setFixedSize(this->sizeHint());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void CrashReporterGui::openFolder() {
 | 
				
			||||||
 | 
						QDesktopServices::openUrl(QUrl::fromLocalFile(this->reportFolder));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void CrashReporterGui::openReportUrl() {
 | 
				
			||||||
 | 
						QDesktopServices::openUrl(
 | 
				
			||||||
 | 
						    QUrl("https://github.com/outfoxxed/quickshell/issues/new?template=crash.yml")
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void CrashReporterGui::cancel() { QApplication::quit(); }
 | 
				
			||||||
							
								
								
									
										17
									
								
								src/crash/interface.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/crash/interface.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qwidget.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CrashReporterGui: public QWidget {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						CrashReporterGui(QString reportFolder, int pid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private slots:
 | 
				
			||||||
 | 
						void openFolder();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static void openReportUrl();
 | 
				
			||||||
 | 
						static void cancel();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						QString reportFolder;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										165
									
								
								src/crash/main.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								src/crash/main.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,165 @@
 | 
				
			||||||
 | 
					#include "main.hpp"
 | 
				
			||||||
 | 
					#include <cerrno>
 | 
				
			||||||
 | 
					#include <cstdlib>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qapplication.h>
 | 
				
			||||||
 | 
					#include <qconfig.h>
 | 
				
			||||||
 | 
					#include <qdatastream.h>
 | 
				
			||||||
 | 
					#include <qdatetime.h>
 | 
				
			||||||
 | 
					#include <qdir.h>
 | 
				
			||||||
 | 
					#include <qfile.h>
 | 
				
			||||||
 | 
					#include <qlogging.h>
 | 
				
			||||||
 | 
					#include <qloggingcategory.h>
 | 
				
			||||||
 | 
					#include <qtenvironmentvariables.h>
 | 
				
			||||||
 | 
					#include <qtextstream.h>
 | 
				
			||||||
 | 
					#include <sys/sendfile.h>
 | 
				
			||||||
 | 
					#include <sys/types.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../core/crashinfo.hpp"
 | 
				
			||||||
 | 
					#include "../core/logging.hpp"
 | 
				
			||||||
 | 
					#include "../core/paths.hpp"
 | 
				
			||||||
 | 
					#include "build.hpp"
 | 
				
			||||||
 | 
					#include "interface.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Q_LOGGING_CATEGORY(logCrashReporter, "quickshell.crashreporter", QtWarningMsg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void recordCrashInfo(const QDir& crashDir, const InstanceInfo& instanceInfo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void qsCheckCrash(int argc, char** argv) {
 | 
				
			||||||
 | 
						auto fd = qEnvironmentVariable("__QUICKSHELL_CRASH_DUMP_FD");
 | 
				
			||||||
 | 
						if (fd.isEmpty()) return;
 | 
				
			||||||
 | 
						auto app = QApplication(argc, argv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						InstanceInfo instance;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto crashProc = qEnvironmentVariable("__QUICKSHELL_CRASH_DUMP_PID").toInt();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							auto infoFd = qEnvironmentVariable("__QUICKSHELL_CRASH_INFO_FD").toInt();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							QFile file;
 | 
				
			||||||
 | 
							file.open(infoFd, QFile::ReadOnly, QFile::AutoCloseHandle);
 | 
				
			||||||
 | 
							file.seek(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							auto ds = QDataStream(&file);
 | 
				
			||||||
 | 
							ds >> instance;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LogManager::init(!instance.noColor, false);
 | 
				
			||||||
 | 
						auto crashDir = QsPaths::crashDir(instance.shellId, instance.launchTime);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qCInfo(logCrashReporter) << "Starting crash reporter...";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						recordCrashInfo(crashDir, instance);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto gui = CrashReporterGui(crashDir.path(), crashProc);
 | 
				
			||||||
 | 
						gui.show();
 | 
				
			||||||
 | 
						exit(QApplication::exec()); // NOLINT
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int tryDup(int fd, const QString& path) {
 | 
				
			||||||
 | 
						QFile sourceFile;
 | 
				
			||||||
 | 
						if (!sourceFile.open(fd, QFile::ReadOnly, QFile::AutoCloseHandle)) {
 | 
				
			||||||
 | 
							qCCritical(logCrashReporter) << "Failed to open source fd for duplication.";
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto destFile = QFile(path);
 | 
				
			||||||
 | 
						if (!destFile.open(QFile::WriteOnly)) {
 | 
				
			||||||
 | 
							qCCritical(logCrashReporter) << "Failed to open dest file for duplication.";
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto size = sourceFile.size();
 | 
				
			||||||
 | 
						off_t offset = 0;
 | 
				
			||||||
 | 
						ssize_t count = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sourceFile.seek(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (count != size) {
 | 
				
			||||||
 | 
							auto r = sendfile(destFile.handle(), sourceFile.handle(), &offset, sourceFile.size());
 | 
				
			||||||
 | 
							if (r == -1) {
 | 
				
			||||||
 | 
								qCCritical(logCrashReporter).nospace()
 | 
				
			||||||
 | 
								    << "Failed to duplicate fd " << fd << " with error code " << errno
 | 
				
			||||||
 | 
								    << ". Error: " << qt_error_string();
 | 
				
			||||||
 | 
								return 3;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								count += r;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void recordCrashInfo(const QDir& crashDir, const InstanceInfo& instance) {
 | 
				
			||||||
 | 
						qCDebug(logCrashReporter) << "Recording crash information at" << crashDir.path();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!crashDir.mkpath(".")) {
 | 
				
			||||||
 | 
							qCCritical(logCrashReporter) << "Failed to create folder" << crashDir
 | 
				
			||||||
 | 
							                             << "to save crash information.";
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto crashProc = qEnvironmentVariable("__QUICKSHELL_CRASH_DUMP_PID").toInt();
 | 
				
			||||||
 | 
						auto dumpFd = qEnvironmentVariable("__QUICKSHELL_CRASH_DUMP_FD").toInt();
 | 
				
			||||||
 | 
						auto logFd = qEnvironmentVariable("__QUICKSHELL_CRASH_LOG_FD").toInt();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qCDebug(logCrashReporter) << "Saving minidump from fd" << dumpFd;
 | 
				
			||||||
 | 
						auto dumpDupStatus = tryDup(dumpFd, crashDir.filePath("minidump.dmp"));
 | 
				
			||||||
 | 
						if (dumpDupStatus != 0) {
 | 
				
			||||||
 | 
							qCCritical(logCrashReporter) << "Failed to write minidump:" << dumpDupStatus;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qCDebug(logCrashReporter) << "Saving log from fd" << logFd;
 | 
				
			||||||
 | 
						auto logDupStatus = tryDup(logFd, crashDir.filePath("log.qslog"));
 | 
				
			||||||
 | 
						if (logDupStatus != 0) {
 | 
				
			||||||
 | 
							qCCritical(logCrashReporter) << "Failed to save log:" << logDupStatus;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							auto extraInfoFile = QFile(crashDir.filePath("info.txt"));
 | 
				
			||||||
 | 
							if (!extraInfoFile.open(QFile::WriteOnly)) {
 | 
				
			||||||
 | 
								qCCritical(logCrashReporter) << "Failed to open crash info file for writing.";
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								auto stream = QTextStream(&extraInfoFile);
 | 
				
			||||||
 | 
								stream << "===== Quickshell Crash =====\n";
 | 
				
			||||||
 | 
								stream << "Git Revision: " << GIT_REVISION << '\n';
 | 
				
			||||||
 | 
								stream << "Crashed process ID: " << crashProc << '\n';
 | 
				
			||||||
 | 
								stream << "Run ID: " << QString("run-%1").arg(instance.launchTime.toMSecsSinceEpoch())
 | 
				
			||||||
 | 
								       << '\n';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								stream << "\n===== Shell Information =====\n";
 | 
				
			||||||
 | 
								stream << "Shell ID: " << instance.shellId << '\n';
 | 
				
			||||||
 | 
								stream << "Config Path: " << instance.configPath << '\n';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								stream << "\n===== Report Integrity =====\n";
 | 
				
			||||||
 | 
								stream << "Minidump save status: " << dumpDupStatus << '\n';
 | 
				
			||||||
 | 
								stream << "Log save status: " << logDupStatus << '\n';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								stream << "\n===== System Information =====\n";
 | 
				
			||||||
 | 
								stream << "Qt Version: " << QT_VERSION_STR << "\n\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								stream << "/etc/os-release:";
 | 
				
			||||||
 | 
								auto osReleaseFile = QFile("/etc/os-release");
 | 
				
			||||||
 | 
								if (osReleaseFile.open(QFile::ReadOnly)) {
 | 
				
			||||||
 | 
									stream << '\n' << osReleaseFile.readAll() << '\n';
 | 
				
			||||||
 | 
									osReleaseFile.close();
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									stream << "FAILED TO OPEN\n";
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								stream << "/etc/lsb-release:";
 | 
				
			||||||
 | 
								auto lsbReleaseFile = QFile("/etc/lsb-release");
 | 
				
			||||||
 | 
								if (lsbReleaseFile.open(QFile::ReadOnly)) {
 | 
				
			||||||
 | 
									stream << '\n' << lsbReleaseFile.readAll() << '\n';
 | 
				
			||||||
 | 
									lsbReleaseFile.close();
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									stream << "FAILED TO OPEN\n";
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								extraInfoFile.close();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qCDebug(logCrashReporter) << "Recorded crash information.";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										3
									
								
								src/crash/main.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/crash/main.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void qsCheckCrash(int argc, char** argv);
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,7 @@
 | 
				
			||||||
#include <qtmetamacros.h>
 | 
					#include <qtmetamacros.h>
 | 
				
			||||||
#include <unistd.h>
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../../core/common.hpp"
 | 
				
			||||||
#include "../../dbus/properties.hpp"
 | 
					#include "../../dbus/properties.hpp"
 | 
				
			||||||
#include "dbus_watcher_interface.h"
 | 
					#include "dbus_watcher_interface.h"
 | 
				
			||||||
#include "item.hpp"
 | 
					#include "item.hpp"
 | 
				
			||||||
| 
						 | 
					@ -31,7 +32,10 @@ StatusNotifierHost::StatusNotifierHost(QObject* parent): QObject(parent) {
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this->hostId = QString("org.kde.StatusNotifierHost-") + QString::number(getpid());
 | 
						this->hostId = QString("org.kde.StatusNotifierHost-%1-%2")
 | 
				
			||||||
 | 
						                   .arg(QString::number(getpid()))
 | 
				
			||||||
 | 
						                   .arg(QString::number(qs::Common::LAUNCH_TIME.toMSecsSinceEpoch()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto success = bus.registerService(this->hostId);
 | 
						auto success = bus.registerService(this->hostId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!success) {
 | 
						if (!success) {
 | 
				
			||||||
| 
						 | 
					@ -98,7 +102,7 @@ void StatusNotifierHost::connectToWatcher() {
 | 
				
			||||||
	    [this](QStringList value, QDBusError error) { // NOLINT
 | 
						    [this](QStringList value, QDBusError error) { // NOLINT
 | 
				
			||||||
		    if (error.isValid()) {
 | 
							    if (error.isValid()) {
 | 
				
			||||||
			    qCWarning(logStatusNotifierHost).noquote()
 | 
								    qCWarning(logStatusNotifierHost).noquote()
 | 
				
			||||||
			        << "Error reading \"RegisteredStatusNotifierITems\" property of watcher"
 | 
								        << "Error reading \"RegisteredStatusNotifierItems\" property of watcher"
 | 
				
			||||||
			        << this->watcher->service();
 | 
								        << this->watcher->service();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			    qCWarning(logStatusNotifierHost) << error;
 | 
								    qCWarning(logStatusNotifierHost) << error;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue