root/src/bin/pidof.cpp
/*
 * Copyright 2022, Haiku Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Oscar Lesta <oscar.lesta@gmail.com>
 */

#include <stdio.h>
#include <string.h>

#include <OS.h>

#include <extended_system_info.h>
#include <extended_system_info_defs.h>
#include <kernel/util/KMessage.h>


static void
usage(FILE* output = stdout)
{
        fprintf(output, "Return the process id(s) (PID) for the given process name.\n\n"
                "Usage:\tpidof [-hsv] process_name\n"
                "\t\t-h : Show this help.\n"
                "\t\t-s : Print only the PID for the first matching process.\n"
                "\t\t-v : Verbose output.\n"
        );
}


int
main(int argc, char* argv[])
{
        bool singleShot = false;
        bool verbose = false;

        int c;
        while ((c = getopt(argc, argv, "hvs")) != -1) {
                switch (c) {
                        case 'h':
                                usage();
                                return 0;
                        case 's':
                                singleShot = true;
                                break;
                        case 'v':
                                verbose = true;
                                break;
                        default:
                                usage(stderr);
                                return 1;
                }
        }

        if ((argc == optind) || (argc - optind) > 1) {
                if (verbose)
                        fprintf(stderr, "Error: A (single) process name is expected.\n");

                usage(stderr);
                return 1;
        }

        int32 cookie = 0;
        team_info teamInfo;
        bool processFound = false;
        const char* processName = argv[argc - 1];

        while (get_next_team_info(&cookie, &teamInfo) == B_OK) {
                KMessage extendedInfo;
                status_t status = get_extended_team_info(teamInfo.team, B_TEAM_INFO_BASIC, extendedInfo);

                if (status == B_BAD_TEAM_ID) {
                        // The team might have simply ended between the last function calls.
                        continue;
                } else if (status != B_OK) {
                        fprintf(stderr, "Error: get_extended_team_info() == 0x%" B_PRIx32 " (%s)\n",
                                status, strerror(status));
                        return 1;
                }

                const char* teamName = NULL;
                if (extendedInfo.FindString("name", &teamName) != B_OK)
                        continue;

                if (strcmp(teamName, processName) == 0) {
                        processFound = true;
                        printf("%" B_PRId32 "\n", teamInfo.team);
                        if (singleShot)
                                return 0;
                }
        }

        if (verbose && !processFound)
                fprintf(stderr, "Error: Process name not found.\n");

        return processFound ? 0 : 1;
}