Gavin Bloodworth
Gavin Bloodworth

Reputation: 13

Using a parameter in time() gives you incompatible pointer type

Why is it when you use this

struct utmp rec;

(although, using any variable declared as type time_t would also demostrate this problem ie declaring "time_t rec;" )

this is ok -

rec.ut_time = time(NULL);

But not this -

time(&rec.ut_time);

The second line of code has the error passing argument 1 of time from incompatible pointer type [-Wincompatible-pointer-types]

It is a c code file and I am using gcc to compile on Ubuntu.

The man page for time() states -

time_t time(time_t *tloc);

If tloc is non-NULL, the return value is also stored in the memory pointed to by tloc. But then, under Bugs: The tloc argument is obsolescent and should always be NULL in new code. When tloc is NULL, the call cannot fail.

In a (2003) linux programming text book I am using, they are using the method time(&rec.ut_time); And the time function with a parameter is what I see in a lot of older github examples.

But do newer versions of compliers have an issue with passing an argument in time() ?

Maybe I am focusing too much on this, and should just use rec.ut_time = time(NULL); as it works.

Upvotes: 1

Views: 630

Answers (2)

Gavin Bloodworth
Gavin Bloodworth

Reputation: 13

In case it helps people, I will show here the complete function from the text book (Understanding Unix/Linux Programming. Molay). With the problem code corrected (code lines are commented as "added code" and "problem code").

/* Marks a utmp record as logged out. Does not blank the username or
remote host. Returns -1 on error, 0 on success.
*/
int logout_tty(char *line) {    
    int fd;
    struct utmp rec;
    struct timeval tv; // added code. for use within gettimeofday(). 
    int len = sizeof(struct utmp);
    int retval = -1; // assume an error

    if ((fd = open(UTMP_FILE, O_RDWR)) == -1) {
        perror(UTMP_FILE);
        return -1;
    }
    
/* note on utmp and time(). Originally tried using time() to set the time to
ut_tv.tv_sec in the while loop below. time() expects a type time_t. 
But ut_tv.tv_sec in struct utmp is int32_t . Because of this the utmp man page
recommends the use of gettimeofday() instead of time().
*/
    // search and replace
    while (read(fd, &rec, len) == len) {
        if (strncmp(rec.ut_line, line, sizeof(rec.ut_line)) == 0) {
            rec.ut_type = DEAD_PROCESS; // set type
            //if (time(&rec.ut_time) != -1) // problem code. set time
            if ((gettimeofday(&tv, NULL)) != -1) { // added code. set time
                rec.ut_tv.tv_sec = tv.tv_sec; // added code                       
                if (lseek(fd, -len, SEEK_CUR) != -1) { // back up
                    if (write(fd, &rec, len) == len) { // update
                        retval = 0; // success
                    }
                }
                break;
            }
        }
    }
    
    if (close(fd) == -1) {
        retval = -1;
    }
    
    return retval;
}

Upvotes: 0

The book is wrong, of course. The Linux utmp(5) manual states that the struct utmp.ut_tv is not of type compatible with struct timeval

Note that on biarch platforms, that is, systems which can run both 32-bit and 64-bit applications (x86-64, ppc64, s390x, etc.), ut_tv is the same size in 32-bit mode as in 64-bit mode. The same goes for ut_session and ut_time if they are present. This allows data files and shared memory to be shared between 32-bit and 64-bit applications. This is achieved by changing the type of ut_session to int32_t, and that of ut_tv to a struct with two int32_t fields tv_sec and tv_usec. Since ut_tv may not be the same as struct timeval, then instead of the call:

gettimeofday((struct timeval *) &ut.ut_tv, NULL);

the following method of setting this field is recommended:

struct utmp ut;
struct timeval tv;

gettimeofday(&tv, NULL);
ut.ut_tv.tv_sec = tv.tv_sec;
ut.ut_tv.tv_usec = tv.tv_usec;

You get the warning because ut_time is defined as a macro that expands to ut_tv.tv_sec; and the tv_sec member of rec.ut_tv is int32_t even on systems that have 64-bit time_t and whose struct timeval would contain time_t tv_sec.

The proper way of setting the time is with gettimeofday as above because it would set the microsecond field too.

Upvotes: 2

Related Questions