[Solved] Cannot open the default browser under Linux lazarus / 32-Linux ---------------------------------------------------------------------- devEric69 · 2020-10-21 10:16 Hello, I'm trying to launch my default browser under Linux. When Icode OpenURL('https://www.noip.com/');, nothing happens. But, if I launch the command xdg-open https://www.noip.com/ & in the terminal, Firefox opens - without problem - on the requested page \ URL. So, i've then tested: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TWebUtils.OpenURLinDefaultBrowser;var  oProcess: TProcess;  sCmd: string;  i, j, k: integer;begin  oProcess:= TProcess.Create(nil);  try    oProcess.Options:= [poWaitOnExit, poNoConsole];    sCmd:= '/usr/bin/xdg-open https://www.noip.com/ &';    oProcess.CommandLine:= sCmd;    oProcess.Execute;    i:= oProcess.ExitStatus;    // returns 1    j:= oProcess.ExitCode;      // returns 0    k:= oProcess.ProcessID;     // <== returns 36500, but 'ps -ef', in a terminal, doesn't report the existence of such a process??! Htop neither.    oProcess.CloseOutput;  finally    oProcess.Free;  end;end;   I've found that xdg-open has a practice verbose debugging mode...: Code: Bash  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i bash -x /usr/bin/xdg-open https://www.noip.com/+ check_common_commands https://www.noip.com/+ '[' 1 -gt 0 ']'+ parm=https://www.noip.com/+ shift+ case "$parm" in+ '[' 0 -gt 0 ']'+ '[' -z '' ']'+ unset XDG_UTILS_DEBUG_LEVEL+ '[' 0 -lt 1 ']'+ xdg_redirect_output=' > /dev/null 2> /dev/null'+ '[' xhttps://www.noip.com/ '!=' x ']'+ url=+ '[' 1 -gt 0 ']'+ parm=https://www.noip.com/+ shift+ case "$parm" in+ '[' -n '' ']'+ url=https://www.noip.com/+ '[' 0 -gt 0 ']'+ '[' -z https://www.noip.com/ ']'+ detectDE+ unset GREP_OPTIONS+ '[' -n ubuntu:GNOME ']'+ case "${XDG_CURRENT_DESKTOP}" in+ '[' x = x ']'+ '[' x '!=' x ']'+ '[' xthis-is-deprecated '!=' x ']'+ DE=gnome+ '[' xgnome = x ']'+ '[' xgnome = x ']'+ '[' xgnome = xgnome ']'+ which gnome- default-applications-properties+ DE=gnome3+ '[' -f /run/user/1000/flatpak-info ']'+ '[' xgnome3 = x ']'+ DEBUG 2 'Selected DE gnome3'+ '[' -z '' ']'+ return 0+ case "${BROWSER}" in+ case "$DE" in+ open_gnome3 https://www.noip.com/+ gio help open+ gio open https://www.noip.com/+ '[' 0 -eq 0 ']'+ exit_success+ '[' 0 -gt 0 ']'+ exit 0...but I can't run it from a TProcess instance either . Does anyone have an idea, a track, to understand why I can't do the same thing with TProcess? ---------------------------------------------------------------------- Cyrax · 2020-10-21 11:09 Rather than using TProcess.CommandLine directly, you should use TProcess.Parameters. Also do not use "&", only a shell program understands its internally. ---------------------------------------------------------------------- trev · 2020-10-21 11:11 Quote from: devEric69 on October 21, 2020, 12:16:30 pmI'm trying to launch my default browser under Linux. When Icode OpenURL('https://www.noip.com/');, nothing happens. This works for me in Ubuntu 18.04 LTS and now 20.04 LTS: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm2_About.LicLabel2Click(Sender: TObject);begin  OpenURL('https://unlicense.org/');end;  firing up Firefox and taking me to unlicense.org. ---------------------------------------------------------------------- marcov · 2020-10-21 11:50 Your way of execution assumes that xdg-open is a binary, and not a script. ---------------------------------------------------------------------- devEric69 · 2020-10-21 13:32 QuoteThis works for me in Ubuntu 18.04 LTS and now 20.04 LTS Okay, thank you. I'm investigating on my computer... ---------------------------------------------------------------------- dogriz · 2020-10-22 05:54 Quote from: marcov on October 21, 2020, 01:50:12 pmYour way of execution assumes that xdg-open is a binary, and not a script. Why does that matter? ---------------------------------------------------------------------- PascalDragon · 2020-10-22 07:20 Quote from: dogriz on October 22, 2020, 07:54:49 amQuote from: marcov on October 21, 2020, 01:50:12 pmYour way of execution assumes that xdg-open is a binary, and not a script. Why does that matter? Scripts are executed by a shell. It is the one that interprets the shebang (#!) at the start to determine the correct interpreter. TProcess however uses fork/exec and thus the kernel to start binaries and the kernel itself does not handle scripts. Thus you need to start a shell and pass it the command to ensure that it is executed. Note: in theory it would be possible to set up the Linux kernel to handle this using the binfmt_misc functionality, but that is not done by default, thus you can't rely on it. ---------------------------------------------------------------------- MarkMLl · 2020-10-22 08:02 Quote from: PascalDragon on October 22, 2020, 09:20:23 amScripts are executed by a shell. It is the one that interprets the shebang (#!) at the start to determine the correct interpreter. TProcess however uses fork/exec and thus the kernel to start binaries and the kernel itself does not handle scripts. Thus you need to start a shell and pass it the command to ensure that it is executed. The kernel most definitely used to, and I have no reason to believe that this has changed: i.e. the shebang is specifically processed by code in the Linux kernel rather than relying on a shell, which is different from some other unix implementations. However I had an odd situation a couple of weeks ago (please note that I'm working from memory here) where I found that RunCommand() etc. wouldn't execute a script under certain conditions (I think it was while being debugged), but the program concerned is doing some very odd things with POSIX capabilities etc.: Code: [Select](* If the file is executable then assume that this is a script or program that  *) (* checks the content of the file of interest, and if it fails to return a zero *) (* result return false. Otherwise assume that the name is that of the file of   *) (* interest and return false if the age is greater than was specified on the    *) (* command line. ExecuteProcess() might misbehave under gdbserver, if in doubt  *) (* use "old school" debugging techniques.                                     *)     if fpStat(mon[i].name, buff) <> 0 then (* Probably same as FileExists() above *)       exit(false);     if buff.mode and &001 = &001 then begin (* Executable by "other"          *)       try         age := ExecuteProcess(mon[i].name, IntToStr(mon[i].mins))       except         age := $ffff       end;       if age <> 0 then                  (* Visible for debugging              *)         exit(false)       else         smallest := 2                   (* See comment on result/sideffect below *)     end else begin (* There's a misnomer here that always catches me out: this returns a timestamp *) (* rather than an age (relative timestamp).                                   *)       age := FileAge(mon[i].name);      (* Visible for debugging              *)       scratch := DateTimeToStr(FileDateTodateTime(age));       age := Round((UTC_Now() - FileDateTodateTime(age)) * MinsPerDay);       if age > mon[i].mins then         exit(false)     end   end; The parameter mon[ i].name is *directly* from the command line, and in this particular case is normally a Perl script. Works just fine. Code: [Select]#!/usr/bin/perl $MAINLOG = '/var/log/exim4/mainlog'; $MAINLOG1 = $MAINLOG . '.1';            # Optional "last" logfile $LIMIT = 90; ... This has definitely been the case since approximately the 2.2 kernel, which I dived into because somebody (on a private conferencing system) had made the same assertion in a somewhat less pleasant way. I don't know what the original rationale was but I'd speculate that somebody wanted something like to be able to write init in either Perl or as a binary. However these days it makes since since it ensures that POSIX capabilities and other EAs relating to e.g. SELinux can be applied strictly to a single script, rather than having a specially tailored shell which could be abused. MarkMLl ---------------------------------------------------------------------- PascalDragon · 2020-10-22 11:36 Quote from: MarkMLl on October 22, 2020, 10:02:26 amQuote from: PascalDragon on October 22, 2020, 09:20:23 amScripts are executed by a shell. It is the one that interprets the shebang (#!) at the start to determine the correct interpreter. TProcess however uses fork/exec and thus the kernel to start binaries and the kernel itself does not handle scripts. Thus you need to start a shell and pass it the command to ensure that it is executed. The kernel most definitely used to, and I have no reason to believe that this has changed: i.e. the shebang is specifically processed by code in the Linux kernel rather than relying on a shell, which is different from some other unix implementations. Indeed you're right: The Linux kernel initializes a binfmt loader for script files by default. This might not work on every *nix system however as POSIX states this about the exec* family of calls: QuoteHistorically, there have been two ways that implementations can exec shell scripts. One common historical implementation is that the execl(), execv(), execle(), and execve() functions return an [ENOEXEC] error for any file not recognizable as executable, including a shell script. When the execlp() and execvp() functions encounter such a file, they assume the file to be a shell script and invoke a known command interpreter to interpret such files. This is now required by POSIX.1-2017. These implementations of execvp() and execlp() only give the [ENOEXEC] error in the rare case of a problem with the command interpreter's executable file. Because of these implementations, the [ENOEXEC] error is not mentioned for execlp() or execvp(), although implementations can still give it. Another way that some historical implementations handle shell scripts is by recognizing the first two bytes of the file as the character string "#!" and using the remainder of the first line of the file as the name of the command interpreter to execute. TProcess uses either execve or execv, but never exec*p, thus it's not cross platform to assume that TProcess can indeed start a shell script. ---------------------------------------------------------------------- MarkMLl · 2020-10-22 11:52 Quote from: PascalDragon on October 22, 2020, 01:36:20 pmThis might not work on every *nix system however as POSIX states this about the exec* family of calls: Very much agreed. I'm not really in a position to look at e.g. Solaris at the moment, much as I'd like to. I suspect though that things like POSIX capabilities would be a major incentive for a kernel to handle the shebang etc. internally, since generally speaking anything that moves a file to a new inode will- by design- lose the EAs that implement them hence the capabilities will need to be reapplied by a privileged user. I don't know whether an equivalently-robust alternative could be implemented if the kernel didn't at least read the EAs of the file to be executed. I'm not sure which way this argues, but I spotted something on StackOverflow recently where it was being asked why one of the major distreaux (possibly Red Hat) mandated that shebang lines like #!/bin/sh -x should be avoided, instead using #!/bin/sh set -x and so on. My assumption is that that's in recognition of the fact that it's normally the kernel that parses the shebang and that the parser might have limitations e.g. in the line length it's prepared to accept. MarkMLl ---------------------------------------------------------------------- devEric69 · 2020-10-22 19:36 I finally found the reason, which is due to the rights policy under Linux. I run a Lazarus as 'root', to open Firefox in a 'user01' session: Code: Text  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Initialization output: Running Firefox as root in a regular user's session is not supported.  ($XAUTHORITY is /run/user/1000/gdm/Xauthority which is owned by user01.)Running Firefox as root in a regular user's session is not supported.  ($XAUTHORITY is /run/user/1000/gdm/Xauthority which is owned by user01.)Running Firefox as root in a regular user's session is not supported.  ($XAUTHORITY is /run/user/1000/gdm/Xauthority which is owned by user01.)/usr/bin/xdg-open: 869: iceweasel: not found/usr/bin/xdg-open: 869: seamonkey: not found/usr/bin/xdg-open: 869: mozilla: not found/usr/bin/xdg-open: 869: epiphany: not found/usr/bin/xdg-open: 869: konqueror: not found/usr/bin/xdg-open: 869: chromium: not found/usr/bin/xdg-open: 869: chromium-browser: not found/usr/bin/xdg-open: 869: google-chrome: not found/usr/bin/xdg- open: 869: www-browser: not found/usr/bin/xdg-open: 869: links2: not found/usr/bin/xdg-open: 869: elinks: not found/usr/bin/xdg-open: 869: links: not found/usr/bin/xdg-open: 869: lynx: not found/usr/bin/xdg- open: 869: w3m: not foundxdg-open: no method available for opening 'https://unlicense.org/'' In such a case, is there a way to code, with Lazarus, in the program, a workaround to this kind of problem ? ---------------------------------------------------------------------- Bart · 2020-10-22 19:50 Quote from: devEric69 on October 22, 2020, 09:36:25 pmI finally found the reason, which is due to the rights policy under Linux. I run a Lazarus as 'root', Why? I can't think of a reason why you should do that. Bart ---------------------------------------------------------------------- devEric69 · 2020-10-22 19:58 Indeed. No serious or legitimate reason  . But out of curiosity, there would be an imaginable and codable "strategy" ? ---------------------------------------------------------------------- MarkMLl · 2020-10-22 20:21 Quote from: devEric69 on October 22, 2020, 09:36:25 pmIn such a case, is there a way to code, with Lazarus, in the program, a workaround to this kind of problem ? Don't run as root, you're a disaster waiting to happen. xdgopen is quite correctly set up to not run something as complex as a browser with elevated rights, and you should be applying the same philosophy to the Lazarus IDE. If your program needs a specific right over and above those held by ordinary users, then you should be using POSIX capabilities. And what's more if at all possible you should be exploiting the extra capability and then releasing it before starting the GUI part of your program, so that when complex stuff starts there's less chance of damage if something dodgy or malicious turns out to be buried in a library. ** And while gdb has problems debugging that, gdbserver works perfectly well except- as I noted earlier- when trying to run something with a shebang (see PascalDragon's comments for the probable reason). ** Same philosophy as a setuid program, which you'll find is fundamentally incompatible with some widget sets for security reasons. MarkMLl ---------------------------------------------------------------------- devEric69 · 2020-10-23 07:46 QuoteDon't run as root, you're a disaster waiting to happen. In a certain way, yes: indeed, I'm not exempt from forgetting code that would (conditionally) bypass the security layer during development, and that I'll forget to remove in the release . And without help or leads from some other people, I am unable to find such a code - by myself - to bypass the xdg-open's security. QuoteIf your program needs a specific right over and above those held by ordinary users, then you should be using POSIX capabilities. I am 'root' in development only. I always test afterwards, regularly, the software as 'user01'. Now, it's sure that the POSIX software layer which ensures the security, is clearly more aware than me, of the vulnerabilities that may exist, and is more consistent with its constant 'proprietary' + 'group' + 'others' strategy rights flags, to apply to the program when it is installed. In fact, I follow a "everything must be embedded with or near the binary" installation strategy, thanks to AppImage. It's my assumption, that hard disk space is no more the limiting factor, for a client GUI application. QuoteAnd while gdb has problems debugging that, gdbserver works perfectly well except - as I noted earlier - when trying to run something with a shebang (see PascalDragon's comments for the probable reason). That's good to know. I would switch from fpDebug to gdbServer temporarily in the future, when I'll suspect a specific right problem. Thank you - all - for your contributions. I close this thread, concerning me. ---------------------------------------------------------------------- MarkMLl · 2020-10-23 08:56 Quote from: devEric69 on October 23, 2020, 09:46:59 amI am 'root' in development only. I always test afterwards, regularly, the software as 'user01'. You're missing the point. The IDE is an extremely complex piece of software, which activates an enormous number of libraries handling rendering etc. as the same user with which you invoke it. It's also prepared to fire up a browser etc. for external reference material from external sites. By running the IDE as root, you're giving software writers that you should not be trusting free rein over your development system, which you should now be considered to be compromised. There is no reason to do this, and you shouldn't be doing it. MarkMLl ---------------------------------------------------------------------- devEric69 · 2020-10-23 09:46 QuoteThe IDE is an extremely complex piece of software, which activates an enormous number of libraries handling rendering etc. I'm aware of this: it's now an old and huge piece of programming, very large and too complex for a small head like me. QuoteThere is no reason to do this, and you shouldn't be doing it. For sure: as 'root', I'm launching mountains of source code, which may contain malicious code... potentially breaking my computer. But exactly as I answer "yes" to the UAC under Windows, which asks me to take my responsibilities before launching a program that can break the Windows shell, I take these responsibilities with the shell of Nunux. So, if I run a silent hard disk formatting without a shell accountability message, I will only be able to blame myself . ---------------------------------------------------------------------- MarkMLl · 2020-10-23 10:57 I presume that you are familiar with Ken Thompson's "Reflections on Trusting Trust" lecture. I don't know whether anybody has considered such a thing with FPC, but there's really no defence against that sort of thing without robustly signed binaries: something that unix isn't very good at, and which could almost certainly be circumvented by the local root user. MarkMLl ---------------------------------------------------------------------- devEric69 · 2020-10-23 12:20 Okay, fine: I surrender . I'll switch the launch of Lazarus, as belonging to 'user01', as soon as possible . ---------------------------------------------------------------------- MarkMLl · 2020-10-23 12:47 If you need a hand with capabilities or debugging yell. I've spent the last few weeks on something that ran as an ordinary user but had elevated capabilities so that it could create a unix domain socket in /var/run and a UDP socket below 1024, and debugging using gdbserver (which does have to run as root). Only significant issue- as mentioned much earlier- was being unable to debug running a shell script directly. MarkMLl