1

I've read some of the warnings against using the sysctl() call in C, and it seems if I cannot use sysctl() safely, the only other way I can find to make the needed change would be to use soemething like:

system("echo  fs.inotify.max_user_watches=NEW_MAX_DIRECTORIES  >> /etc/sysctl.conf");
system("sysctl -p");

(of course, this assumes ensuring the binary is running as root. However, I would rather NOT have to shell out using system calls.

Can someone point me in the correct and safe of using sysctl()?

here is a snippet of the code I am using.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>        
int main ()
{
        int ret;
        const char *LOGNAME="iNotifyMonitor";
        logger(INFO, "================================================");
        ret = startDaemon();
        daemonRunning = ret;

        if (ret == 0)
        {
                daemonRunning = 1;
                FIRST_RUN = 0;
        }

        if(ret)
        {
                syslog(LOG_USER | LOG_ERR, "Error starting iNotifyMonitor");
                logger(ERR, "Unable to start iNotifyMonitor");
                closelog();
                return EXIT_FAILURE;
        }

        signal(SIGINT, signalHandler);
        signal(SIGHUP, signalHandler);
        char *log_file_name = malloc(sizeof(char *) * sizeof(char *));
        sprintf(log_file_name, "%s%s", INM_LOG_DIR, INM_LOG_FILE);
        /* Try to open log file to this daemon */
        if (INM_OPEN_LOG && INM_LOG_FILE)
        {
        log_stream = fopen(concatString(INM_LOG_DIR, INM_LOG_FILE), "a+");
                if (log_stream == NULL)
                {
                        char *errMsg;
                        sprintf(errMsg, "Cannot open log file %s, error: %s", concatString(INM_LOG_DIR, INM_LOG_FILE), strerror(errno));
            log_stream = stdout;
        }
        }
        else
        {
                log_stream = stdout;
    }

    while (daemonRunning == 1)
        {
        if (ret < 0)
                {
            logger(LOG_ERR, "Can not write to log stream: %s, error: %s", (log_stream == stdout) ? "stdout" : log_file_name, strerror(errno));
                        break;
                }
                ret = fflush(log_stream);
                if (ret != 0)
                {
                logger(LOG_ERR, "Can not fflush() log stream: %s, error: %s",
                                (log_stream == stdout) ? "stdout" : log_file_name, strerror(errno));
                        break;
                }
                int curcount =countDirectory("/home/darrinw/Development/CrossRoads/");
                directoryCount = curcount;
                if(directoryCounrt > INM_MAX_DIRECTORIES)
                {
                        int newVal = roundUp(directoryCount, 32768);

                        // call to sysctl() to modify fs.inotify.max_users_watches=newVal
                }


        sleep(INM_SCAN_INTERVAL);
        }
  • In your question, you seem to use the term "system call" to mean calling the `system()` function, which is incorrect. A system call is a way of interfacing with the operating system. – Thomas Jager Jun 26 '20 at 13:01
  • I might suggest editing your title to make it about sysctl instead of system(). Your question isn't really about system() except as one possible way to modify the sysctl variable, and in fact I don't think it's the best way to go about it. – Nate Eldredge Jun 26 '20 at 13:11
  • You are correct, and I will edit the title. I apologize in advance, I had a severe illness (NOT COVID-19) that left me hospitalized for the past 3 weeks, and today is the first time I've had access to any device (laptop/smartphone/etc) – ObsidianCommunications Jul 15 '20 at 21:34

1 Answers1

1

My understanding is that the modern recommended approach to access sysctl variables is via the pseudo-files in /proc/sys. So just open /proc/sys/fs/inotify/max_user_watches and write there.

int fd = open("/proc/sys/fs/inotify/max_user_watches", O_WRONLY);
dprintf(fd, "%d", NEW_MAX_DIRECTORIES);
close(fd);

Error checking left as an exercise.

Modifying /etc/sysctl.conf would make the setting persist across reboots (assuming your distribution uses the file this way, I am not sure if all of them do). That's kind of rude to do automatically; probably better to use the documentation to advise the system administrator to do it themselves if it's needed.

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
  • Modifying '''/etc/sysctl.conf''' to preserve settings across reboots would be preferred because of the way the Linux iNotify table works. Since it can only monitor X directories, and should my program when new directories are added, and are added to the watch thread, a reboot will erase this and the daemon would then again have to adjust. Else, for people with use say Emby or Plex directories, the scanner wouldn't be able to watch for new directories if all ready at that limit. – ObsidianCommunications Jun 26 '20 at 13:18
  • 1
    I still think it's better to tell the user to do it manually than to do it automatically. If you insist, then at least on Debian-like distributions, you should add a file to /etc/sysctl.d instead of editing /etc/sysctl.conf to avoid conflicts with other simultaneous edits. – Nate Eldredge Jun 26 '20 at 21:50
  • 1
    The problem is this is being used in an appliance we are developing and most users won't have the know-how to change this, and mostly once the system is running they will not ever directly log into the appliance, There is already a section in the manual that is being developed along side of the code to explain how to do this manually. But the problem arises when 1) out of sight, out of mind - they won't even think about it. And manually adjusting fs.inotify.max_user_watches in mulitiples of 32k isn't going to cause system performance issues. I'm going to just for the time being use open() – ObsidianCommunications Jun 27 '20 at 12:35
  • And since the product is based on Ubuntu, I think I'll just add a config file to /etc/sysctl.d – ObsidianCommunications Jun 27 '20 at 12:43
  • So then, you might as well place your file in /etc/sysctl.d as part of the installation process of your app - this is easy to do if it's a deb package, for instance. Alternatively, just update the variable via /proc/sys every time your app starts up, or dynamically when you get close to running out. But trying to update the config file from within your app itself seems to me like needless complexity. – Nate Eldredge Jun 27 '20 at 13:50
  • The app already does 2 things, counts directories that are defined in the config file similiar to countDir(int nunPatchs, _INConfigTable *Table) { – ObsidianCommunications Jun 27 '20 at 17:50
  • The app already does 2 things, counts directories that are defined in the config file similiar to ''' countDir(int nunPatchs, INConfigTable *Table) {for int = 0; a path[a], IN_INCREATE | IN_DELETE); ) then if ievent->mask equals IN CREATE/IN_DELETE/IN_INISDIR call a countDir() function, roundUp the resultant (couuntDir(), 32768) for 32k blocks and modify the.max_users_watches= value. I guess no directoy caling sysctl() will be out unless I just call it to reload all values? Honestly, I'd rather do it all via the sysctl() call, but ... – ObsidianCommunications Jun 27 '20 at 18:12