0

I am having a GPS unit connected to com port. I wanted to make my system as a time server. How to do this?

I tried using settime(), SetLocalTime(), SetSystemTime(), etc. None are working for me. By using system("cmd") I tried, but again I am not getting administrator privileges. When using system("runas cmd") the command prompt is opening and closing as blink. Unable to understand what is happening.

How I can make this work?

Divins Mathew
  • 2,214
  • 3
  • 20
  • 31
Anand MN
  • 1
  • 2
  • 2
    When using those APIs (say SetSystemTime) what does the function return? If it returns false, what does GetLastError return? – Mike Vine Jan 12 '17 at 09:58
  • Both return values are 0 (zero). – Anand MN Jan 12 '17 at 10:16
  • You can check if the current process has admin privilege by using CheckTokenMembership. Not sure if system("runas cmd") works – Asesh Jan 12 '17 at 10:42
  • The main problem is that Windows refuses to act as a Time Server. Even if you set the time locally, Windows wouldn't know its stratum. (If you don't know what a stratum is, you should have read the NTP specs beforehand). – MSalters Jan 12 '17 at 10:49
  • If I have correctly understood your question, you are not asking the right one. Your problem is not with setting the time but with giving administrative priviledges to a C++ program. You will find interesting answers [here](http://stackoverflow.com/q/6418791/3545273) (through a *manifest* at build time, or via `ShellExecute` at start time). BTW runas cannot be used to start a command with elevated priviledge... – Serge Ballesta Jan 12 '17 at 11:28
  • @MSalters: A Windows machine can act as a time reference for other Windows machine that would use `NET TIME //machine_name_or_IP /SET`. NTP is not involved here (only Microsoft internals...) – Serge Ballesta Jan 12 '17 at 14:54

1 Answers1

2

Privilege management on Windows is always a tricky part. MSoft assumes that normal mortals should not care with that and documentation is often incomplete.

Here you problem is likely that normal processes (not running as administrator) have not the privilege to change the system time.

But what is possible is:

  • check whether the SeSystemtimePrivilege is granted to the running process
    • if it is just set the time
    • if not restart the process with same parameter as administrator via ShellExectute, verb="runas"

Here is an example of code (the part to parse the time string and actually set the time is not implemented):

#include <iostream>
#include <windows.h>
#include <tchar.h>

#ifdef UNICODE
#define tcout std::wcout
#else
#define tcout std::cout
#endif

// utility used to display error messages
void errmsg() {
    DWORD err = ::GetLastError();
    LPTSTR msg;
    ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
        err, LANG_NEUTRAL, (LPTSTR) &msg, 0, NULL);
    tcout << _T("Error") << std::endl;
}

int _tmain(int argc, TCHAR *argv[]) {
    // syntax check
    if (argc != 2) {
        tcout << _T("Usage ") << argv[0] << " _time_" << std::endl;
        return 1;
    }
    LPCTSTR time = argv[1];
    // get access to the current process token
    HANDLE process = ::GetCurrentProcess();
    HANDLE token;
    if (! ::OpenProcessToken(process, TOKEN_ALL_ACCESS, &token)) {
        errmsg();
        return 1;
    }
    // get priviledges from current token
    DWORD sz = 0, sz2;
    TOKEN_PRIVILEGES *privs;
    ::GetTokenInformation(token, TokenPrivileges, NULL, 0, &sz); // first only size
    if (sz == 0) {
        errmsg();
        return 1;
    }
    privs = (TOKEN_PRIVILEGES *) malloc(sz);
    if (! ::GetTokenInformation(token, TokenPrivileges, privs, sz, &sz2)) {
        errmsg();
        return 1;
    }

    // display found priviledges and look if SE_SYSTEMTIME_NAME is present
    BOOL ok = FALSE;
    for (size_t i=0; i<privs->PrivilegeCount; i++) {
        sz = 0;
        ::LookupPrivilegeName(NULL, &(privs->Privileges[i].Luid), NULL, &sz);
        LPTSTR name = (LPTSTR) malloc(sz * sizeof(TCHAR));
        if (! ::LookupPrivilegeName(NULL, &(privs->Privileges[i].Luid), name, &sz)) {
            errmsg();
            return 1;
        }
        if (0 == ::lstrcmp(name, SE_SYSTEMTIME_NAME)) {
            ok = TRUE;
        }
        // tcout << name << std::endl;
        free(name);
    }
    free(privs); // done with it
    tcout << (ok ? _T("Can") : _T("Cannot")) << _T(" change time") << std::endl;

    if (ok) {
        tcout << _T("Setting time with ") << time << std::endl;
        // actually parse the time string and call SetSystemTime
        //...
    }
    else {
        tcout << _T("Restart self as admin...") << std::endl;
        // start as cmd /K "full path" to have a chance to see eventual errors
        LPTSTR params = (LPTSTR) malloc(MAX_PATH + 7 + lstrlen(time));
        lstrcpy(params, _T(" /K \""));
        sz = ::GetModuleFileName(NULL, params + 5, MAX_PATH);
        // tcout << params + 5 << std::endl;
        ::lstrcat(params,_T("\" "));  // do not forget the trailing "
        lstrcat(params, time);
        // Let's go, the UAC control should pop to allow the privilege elevation
        if (((int) ShellExecute(NULL, _T("runas"), _T("cmd.exe"), params,
                _T("."), SW_SHOW)) < 32) {
            tcout << _T("Could not start self with elevated privileges") << std::endl;
            return 1;
        }
        free(params);
    }
    // time to clean...
    ::CloseHandle(token);
    ::CloseHandle(process);
    return 0;
}

Things to still work on:

  • when the command is restarted in admin mode, is uses a console with cmd /K to allow user to see what as happened. If you prefere to be more silent, you should use cmd /C instead and use SW_HIDE is ShellExecute.
  • this program assumes that the new time will be given as a simple string which may or not be relevant for your use case
  • the verbosity should be controlled with optional arguments
  • a system should prevent to try runas admin more than once (probably a second or optional parameter)
Serge Ballesta
  • 121,548
  • 10
  • 94
  • 199