Lazarus

Programming => General => Topic started by: BosseB on July 11, 2020, 05:32:31 pm

Title: (SOLVED) Need to get username and computername for logging, win and unix
Post by: BosseB on July 11, 2020, 05:32:31 pm
I am porting an old application from Delphi to Lazarus to make it dual platform (Windows and Linux).
In this application there is a logging class (also used elsewhere) which among other things writes a summary at the top of each new log file.
In this summary is the currently logged on user and the name of the computer.

This code worked on Windows but it does not give me the computer name in Linux (Raspbian Buster):

Code: Pascal  [Select][+][-]
  1. const
  2.   {$IFDEF UNIX}
  3.     USERNAME = 'USER';
  4.     COMPUTER = 'HOSTNAME';
  5.   {$ELSE}
  6.     USERNAME = 'USERNAME';
  7.     COMPUTER = 'COMPUTERNAME';
  8.   {$ENDIF}
  9. ...
  10. begin
  11.   ...
  12.     slHeader.Add('Computer name:   ' + GetEnvironmentVariable(COMPUTER));
  13.     slHeader.Add('Current user:    ' + GetEnvironmentVariable(USERNAME));

The current user is properly extracted but not the computer name on Linux.
Both are present on Windows.

What can I do in order to get the computer name on Raspbian Linux?

Title: Re: Need to get username and computername for logging, win and unix
Post by: winni on July 11, 2020, 05:56:02 pm
Hi!

For Linux  start a TProcess with

Code: Pascal  [Select][+][-]
  1. MyProcess.Executable := 'hostname';

Winni
Title: Re: Need to get username and computername for logging, win and unix
Post by: rvk on July 11, 2020, 06:06:34 pm
What can I do in order to get the computer name on Raspbian Linux?
Strange.
What does echo $HOSTNAME give you in a console?
For me it gives the hostname on a RPI.
Title: Re: Need to get username and computername for logging, win and unix
Post by: BosseB on July 11, 2020, 06:46:06 pm
What can I do in order to get the computer name on Raspbian Linux?
Strange.
What does echo $HOSTNAME give you in a console?
For me it gives the hostname on a RPI.
How very strange....
I had done this to check why this happens:
Code: Text  [Select][+][-]
  1. $ env | grep HOSTNAME
  2. $
As you can see it returns empty.
But:
Code: Text  [Select][+][-]
  1. $ echo $HOSTNAME
  2. rpi4-gui
  3. $

So executing the echo brings out the hostname, but listing all the environment variables does not.
I started by listing them all when I found that the log was missing the hostname and env does not return the HOSTNAME entry...
Since the username is extracted using the same call I checked the definition of GetEnvironmentVariable found in osutil.inc below fpc rtl:

Code: Pascal  [Select][+][-]
  1. Function GetEnvironmentVariable(Const EnvVar : UnicodeString) : UnicodeString;
  2. begin
  3.   result:=UnicodeString(GetEnvironmentVariable(AnsiString(EnvVar)));
  4. end;

and:

Code: Pascal  [Select][+][-]
  1. Function GetEnvironmentVariable(Const EnvVar : String) : String;
  2. begin
  3.   { no need to adjust the code page of EnvVar to DefaultSystemCodePage, as only
  4.     ASCII identifiers are supported }
  5.   Result:=BaseUnix.FPGetenv(PChar(pointer(EnvVar)));
  6. end;
  7.  

There must be a difference in how echo grabs the variable HOSTNAME compared to USER, which is listed among the env vars.
So the question remains, how can I fix this?
Title: Re: Need to get username and computername for logging, win and unix
Post by: winni on July 11, 2020, 07:11:35 pm
Hi!

If the HOSTNAME is not in your enviromnment this is not correct .

Set your HOSTNAME in the environment

Code: Bash  [Select][+][-]
  1. env HOSTNAME=MySweetRaspi

Afterwards all works as it should

Winni
Title: Re: Need to get username and computername for logging, win and unix
Post by: rvk on July 11, 2020, 07:11:51 pm
It seems like HOSTNAME is a shell variable and not an environment variable.

https://stackoverflow.com/a/6353115/1037511
https://stackoverflow.com/a/56654639/1037511

You could do export HOSTNAME but otherwise you should follow the instructions of winni.
Title: Re: Need to get username and computername for logging, win and unix
Post by: Warfley on July 11, 2020, 07:27:52 pm
Pretty simple, don't rely on environment variables, because these are completely dependent on the parent process and how the current process was started.

For Linux the correct way to get the hostname is to read the pseudo file located at /proc/sys/kernel/hostname
Example using cat:
Code: Pascal  [Select][+][-]
  1. cat /proc/sys/kernel/hostname
Title: Re: Need to get username and computername for logging, win and unix
Post by: bylaardt on July 11, 2020, 07:29:26 pm
other wy is read /etc/hostname
in console:
Code: Bash  [Select][+][-]
  1. cat /etc/hostname
Title: Re: Need to get username and computername for logging, win and unix
Post by: winni on July 11, 2020, 07:33:04 pm
Hi!

Nobody knows everything:

The correct way to get the hostname in Linux is

Code: Bash  [Select][+][-]
  1. hostname

A good Linux has the hostname inside the environment.

It seems to be (again) an absurdity of Debian that it is not contained.

Suse and a lot of others do.

Winni


Title: Re: Need to get username and computername for logging, win and unix
Post by: Warfley on July 11, 2020, 07:56:49 pm
Nobody knows everything:

The correct way to get the hostname in Linux is

Code: Bash  [Select][+][-]
  1. hostname

No it's not hostname is an executable that is part of the gnu inet-utils (or BSD inet-tools), this must be installed on your distro. Sure most linux distros ship it automatically, but there is no guarantee that it will be shipped. Case in point: Archlinux.

There are two correct ways first one is to use the kernel provided pseudo files like /proc/sys/kernel/hostname, which works fine under linux but might not under BSD or other Unix systems like MacOS (cant test it right now).
The other way is to use the function gethostname (https://linux.die.net/man/2/gethostname) defined in the posix standard. This is guaranteed to work on any posix compatible system, including but not limited to BSD, Linux and MacOS

Besides that hostname might not be available, starting a process takes orders of magnitude longer than a normal system call or reading a pseudo file.
Title: Re: Need to get username and computername for logging, win and unix
Post by: winni on July 11, 2020, 07:58:34 pm
Again and again:

The blind man talks about the color.

Winni
Title: Re: Need to get username and computername for logging, win and unix
Post by: Warfley on July 11, 2020, 08:01:18 pm
Again and again:

The blind man talks about the color.

Winni
Why? because I'm telling you that the hostname executable is not a reliable way to get the hostname?

Case in point:
Code: Bash  [Select][+][-]
  1. ➜  ~ docker run -it archlinux    
  2. [root@da845c2d18c9 /]# hostname
  3. bash: hostname: command not found
  4. [root@da845c2d18c9 /]# cat /proc/sys/kernel/hostname
  5. da845c2d18c9
  6.  
Title: Re: Need to get username and computername for logging, win and unix
Post by: rvk on July 11, 2020, 08:25:07 pm
So... Use the GetHostName function from the linux unit.

http://porthos.ist.utl.pt/docs/fpc/units/node288.html
Title: Re: Need to get username and computername for logging, win and unix
Post by: Warfley on July 11, 2020, 08:36:37 pm
This function is also in the Unix unit, anyone knows if theres a difference?
Title: Re: Need to get username and computername for logging, win and unix
Post by: marcov on July 11, 2020, 10:04:58 pm
Afaik fpuname() based detections (like gethostname) only echo static local name.

If the hostname is overriden by some daemon (yellow pages or whatever), then it doesn't reflect that.

Note that is just what I remember from what I read over the years, I don't have real experience with such setups.
Title: Re: Need to get username and computername for logging, win and unix
Post by: MarkMLl on July 11, 2020, 10:24:25 pm
If the hostname is overriden by some daemon (yellow pages or whatever), then it doesn't reflect that.

Note that is just what I remember from what I read over the years, I don't have real experience with such setups.

I tried to raise that for discussion a couple of months ago. A number of Linux distreaux still try to use YP by default, even if not installed.

Bo, you /do/ know about the "set" command that allows you to see what shell variables are defined, don't you?

MarkMLl
Title: Re: Need to get username and computername for logging, win and unix
Post by: tetrastes on July 11, 2020, 10:37:29 pm
This function is also in the Unix unit, anyone knows if theres a difference?
There is no GetHostName in linux unit, it is in
Code: Pascal  [Select][+][-]
  1. unit oldlinux deprecated 'Use Baseunix/Unix';
Title: Re: Need to get username and computername for logging, win and unix
Post by: BosseB on July 11, 2020, 10:44:06 pm
So... Use the GetHostName function from the linux unit.

http://porthos.ist.utl.pt/docs/fpc/units/node288.html

Does not work either...
I did this:
Code: Pascal  [Select][+][-]
  1. uses
  2.   {$IFNDEF FPC} //Delphi
  3.   Windows,
  4.   {$ENDIF}
  5.   {$IFDEF UNIX}
  6.   Linux,  //Added in order to resolve hostname issue
  7.   {$ENDIF}
  8.   Classes,
  9.   SysUtils;
  10. ....
  11.     {$IFDEF UNIX}
  12.       slHeader.Add('Computer name:   ' + GetHostName; //GetEnvironmentVariable(COMPUTER)); //Using suggested call, not working
  13.     {$ELSE}
  14.       slHeader.Add('Computer name:   ' + GetEnvironmentVariable(COMPUTER));
  15.     {$ENDIF}
But when I try to compile this I get the error:
Code: Text  [Select][+][-]
  1. logagi.pas(285,42) Error: Identifier not found "GetHostName"

But I have tested using the command "hostname" on the command line in a terminal on both Raspbian and Ubuntu and both return a valid reply.
And like Raspbian HOSTNAME is not available as an env variable, but echo $HOSTNAME shows the correct result.
Raspbian and Ubuntu operate the same.

So if I were writing a shellscript I could easily use this command to get the name I want.
But now I am writing a FreePascal console program using Lazarus and then I don't see how I can use this kind of call at all, now that the suggested solution actually does not work...

Seems like I really have to open a file on the disk (cat /etc/hostname) in order to read the hostname from it...

For example making a function like this (tested to be working):

Code: Pascal  [Select][+][-]
  1. {$IFDEF UNIX}
  2. {Since the env var HOSTNAME does not exist on Linux we need to read a file...}
  3. function TLogAGI.MyGetHostName: string;
  4. var
  5.   sl: TStringList;
  6. begin
  7.   Result := '';
  8.   sl := TStringList.Create;
  9.   try
  10.     sl.LoadFromFile('/etc/hostname');
  11.     if sl.Count > 0 then
  12.       Result := sl[0];
  13.   finally
  14.     sl.Free;
  15.   end;
  16. end;
  17. {$ENDIF}

Title: Re: Need to get username and computername for logging, win and unix
Post by: rvk on July 11, 2020, 10:48:34 pm
So... Use the GetHostName function from the linux unit.
http://porthos.ist.utl.pt/docs/fpc/units/node288.html
Does not work either...
I did this:
...
But when I try to compile this I get the error:
Code: Text  [Select][+][-]
  1. logagi.pas(285,42) Error: Identifier not found "GetHostName"
What happens if you use baseunix or unix unit like suggested.
Does that contain GetHostName?

Title: Re: Need to get username and computername for logging, win and unix
Post by: BosseB on July 11, 2020, 10:49:06 pm
If the hostname is overriden by some daemon (yellow pages or whatever), then it doesn't reflect that.

Note that is just what I remember from what I read over the years, I don't have real experience with such setups.

I tried to raise that for discussion a couple of months ago. A number of Linux distreaux still try to use YP by default, even if not installed.

Bo, you /do/ know about the "set" command that allows you to see what shell variables are defined, don't you?

MarkMLl
I did not know about the set command but it seems like it outputs what the env command does PLUS a whole lot more like something that looks like program code.
env just displays the environment variables and as I stated above that is how I found out that HOSTNAME is not defined as an environment variable.
Title: Re: Need to get username and computername for logging, win and unix
Post by: BosseB on July 11, 2020, 10:56:00 pm
So... Use the GetHostName function from the linux unit.
http://porthos.ist.utl.pt/docs/fpc/units/node288.html
Does not work either...
I did this:
...
But when I try to compile this I get the error:
Code: Text  [Select][+][-]
  1. logagi.pas(285,42) Error: Identifier not found "GetHostName"
What happens if you use baseunix or unix unit like suggested.
Does that contain GetHostName?
That is not what was suggested, the uses that was suggested was linux and that failed.
But using unix instead does work so now I do no longer have to read the file /etc/hostname..

Thanks!
Title: Re: Need to get username and computername for logging, win and unix
Post by: winni on July 11, 2020, 10:56:34 pm
Hi!

As I told before:
If you got a funny uncivilized Linux then first put the hostname into the environment:
Code: Bash  [Select][+][-]
  1. env HOSTNAME=MySweetRaspi
  2.  
And everything is done.

Winni



Title: Re: Need to get username and computername for logging, win and unix
Post by: BosseB on July 11, 2020, 11:02:13 pm
Hi!

As I told before:
If you got a funny uncivilized Linux then first put the hostname into the environment:
Code: Bash  [Select][+][-]
  1. env HOSTNAME=MySweetRaspi
  2.  
And everything is done.

Winni
Well, I do not know on what system my program is running and this is obviously a hack to edit the environment.
Where should that be done?
And it feels like a chicken and egg complication...
Title: Re: (SOLVED) Need to get username and computername for logging, win and unix
Post by: rvk on July 11, 2020, 11:09:33 pm
And for the username (not tested)

Code: Pascal  [Select][+][-]
  1. uses users,baseunix;
  2. begin
  3.   Writeln(GetUserName(fpgetuid));
  4. end.

Not sure if there is a cross-platform getusername in fpc.
No need for environment reading at all then  8)

Title: Re: Need to get username and computername for logging, win and unix
Post by: Warfley on July 12, 2020, 12:18:44 am
Seems like I really have to open a file on the disk (cat /etc/hostname) in order to read the hostname from it...
I don't know about /etc/hostname, but /proc/sys/kernel/hostname is a pseudofile, meaning it a file path that does not correspond to a real file on the disk, but one that the operatingsystem handles specially. Meaning not a single disk operation will be executed when reading/writing to such files. So it is orders of magnitude faster than normal file accesses and should be only be slightly slower than the respected posix function (GetHostname) (probably around a factor of 3 times slower, because you need 3 system calls instead of one, fopen, fread and fclose).

That said, using the posix function of course is preferable if you can do so (in some languages you might not have such easy access like in bash)
Title: Re: Need to get username and computername for logging, win and unix
Post by: winni on July 12, 2020, 12:45:12 am
Hi!

As I told before:
If you got a funny uncivilized Linux then first put the hostname into the environment:
Code: Bash  [Select][+][-]
  1. env HOSTNAME=MySweetRaspi
  2.  
And everything is done.

Winni
Well, I do not know on what system my program is running and this is obviously a hack to edit the environment.
Where should that be done?
And it feels like a chicken and egg complication...


Yes - news from the egg :

You have to do that as root -otherwise it won't help you.
System operions have to be done as root.

Winni

PS.:

Posted by: Warfley
I don't know about /etc/hostname ...


Yes, that's true


Title: Re: (SOLVED) Need to get username and computername for logging, win and unix
Post by: trev on July 12, 2020, 06:56:16 am
For macOS (an actual UNIX-certified OS) and for FreeBSD (and probably all BSD variants) the following is THE way to discover the hostname.

Code: Pascal  [Select][+][-]
  1. function get_Hostname: AnsiString;
  2.  
  3. var
  4.   status : Integer;
  5.   len : size_t;
  6.   p   : PChar;
  7.  
  8. begin
  9.   status := fpSysCtlByName('kern.hostname', Nil, @len, Nil, 0);
  10.   if status <> 0 then RaiseLastOSError;
  11.  
  12.   GetMem(p, len);
  13.  
  14.   try
  15.     status := fpSysCtlByName('kern.hostname', p, @len, Nil, 0);
  16.     if status <> 0 then RaiseLastOSError;
  17.     Result := p;
  18.   finally
  19.     FreeMem(p);
  20.   end;
  21. end;
TinyPortal © 2005-2018