find-largest-file/find-largest-file/main.c
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
Basic demonstration of how to use select Logging APIs. |
*/ |
#include <dirent.h> |
#include <errno.h> |
#include <sys/stat.h> |
#include <os/log.h> |
size_t find_largest_file(const char *dirpath, char largest_file_path[PATH_MAX]) { |
size_t cur_max_filesize = 0; // The size of the largest file found within the current directory. |
if (dirpath == NULL) { // Safety first. |
return 0; |
} |
DIR *d = opendir(dirpath); |
if (d == NULL) { |
/* |
Here we use `os_log_info` because while we did encounter an error while |
trying to access a directory, it's nothing out of the ordinary. |
`os_log_error` captures the log messages preceding the error and saves |
them to disk, which is not an insignificant amount of work. See |
`os_log_info` below for more information on this system. |
*/ |
os_log_info(OS_LOG_DEFAULT, "Unable to access directory %s: %{errno}d", dirpath, errno); |
return 0; |
} |
// Successfully opened directory. |
/* |
`os_log_info` is used here because this is information that could be useful |
when troubleshooting a problem, just to get a sense of what the program |
was doing before it ran into an issue. With the default configuration, |
messages logged with `os_log_info` are saved in memory only, not to disk. |
If no error occurs, these messages will eventually be discarded. But if |
an error occurs, the most recent of these messages will be saved to disk |
to help troubleshoot the issue. |
*/ |
os_log_info(OS_LOG_DEFAULT, "Entering directory %s", dirpath); |
int cur_dir_fd = dirfd(d); |
struct dirent *dir; |
// Do all files first. |
while ((dir = readdir(d)) != NULL) { |
if (dir->d_type != DT_REG) { |
// Files only. |
continue; |
} |
// Stat the file. |
struct stat file_stat; |
if (fstatat(cur_dir_fd, dir->d_name, &file_stat, AT_SYMLINK_NOFOLLOW) == -1) { |
/* |
There was an error opening the current file, so we log the file |
that we can't open, and capture what we were doing before this. |
These strings are logged dynamically (i.e., they're not known at |
compile-time), so they are (by default) private information and |
will be redacted. The `{public}` specifier is used to override this. |
*/ |
os_log_error(OS_LOG_DEFAULT, "Cannot stat file %s/%s: %{public,errno}d", dirpath, dir->d_name, errno); |
continue; |
} |
/* |
This debug message is used to provide copious amounts of |
information. The idea is that the debug level of logging is only |
enabled when trying to troubleshoot a specific bug during |
development. These messages are in memory only (again, by |
default), and are saved only when a call to `os_log_error` or |
`os_log_fault` is made. |
*/ |
os_log_debug(OS_LOG_DEFAULT, "Processing file %s/%s", dirpath, dir->d_name); |
// Get the file's size. |
if (file_stat.st_size > cur_max_filesize) { |
cur_max_filesize = file_stat.st_size; |
strncpy(largest_file_path, dirpath, PATH_MAX); |
strncat(largest_file_path, "/", 1); |
strncat(largest_file_path, dir->d_name, dir->d_namlen); |
// Spit out a quick progress update for anybody that may be watching our logs live. |
/* |
For privacy reasons, dynamic strings and most dynamic objects are |
considered to be private, and are redacted in log messages |
automatically. Static strings and scalar values are public. Format |
specifiers can be used to change the default privacy of a given |
element, as shown here. |
NOTE: This string is marked as public in order to demonstrate how |
to properly use the format specifiers. The name of a file on a |
user's system should be private. |
*/ |
os_log_info(OS_LOG_DEFAULT, "%{public}s is the new largest file in the current directory with size %{private}zu bytes", largest_file_path, cur_max_filesize); |
} |
} |
// Now recurse on all the directories in the current directory. |
rewinddir(d); |
while ((dir = readdir(d)) != NULL) { |
if (dir->d_type != DT_DIR) { |
// Directories only. |
continue; |
} |
if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) { |
// Skip . and .. |
continue; |
} |
// Set up the path to each subdirectory. |
char cur_path[PATH_MAX] = { 0 }; |
strncpy(cur_path, dirpath, PATH_MAX); |
strncat(cur_path, "/", 1); |
strncat(cur_path, dir->d_name, dir->d_namlen); |
// Recurse on each subdirectory. |
size_t largest_in_dir = find_largest_file(cur_path, largest_file_path); |
if (largest_in_dir > cur_max_filesize) { |
cur_max_filesize = largest_in_dir; |
} |
} |
// We're done reading the directory's contents, so close and return. |
closedir(d); |
return cur_max_filesize; |
} |
int main(int argc, const char * argv[]) { |
char largest_path[PATH_MAX] = { 0 }; |
size_t largest_file_size; |
char search_root_path[PATH_MAX] = "/"; |
// If an argument is present, use it as the root path to search in. |
if (argc == 2) { |
realpath(argv[1], search_root_path); |
} |
/* |
`os_log` is used for the most relevant information. This is saved to disk |
by default, and is used for important events in the application, such as |
beginning a task. See also the Paper Company sample code for more detail on |
logging by activity in your application. |
*/ |
os_log(OS_LOG_DEFAULT, "Searching for the largest file in %s", search_root_path); |
largest_file_size = find_largest_file(search_root_path, largest_path); |
os_log(OS_LOG_DEFAULT, "The largest file is %s with a size of %zu bytes.", largest_path, largest_file_size); |
printf("The largest file is %s with a size of %zu bytes.\n", largest_path, largest_file_size); |
return 0; |
} |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-09-13