לדלג לתוכן

פתרון שירות ומנהל השירותים

פתרון התרגיל הראשון

הפתיחה יכולה להיכשל מפני שלא קיימת רשומה בשם הזה, מפני שהשם שגוי או מפני שלמשתמש אין את הזכויות שביקשנו. נשמור את קוד השגיאה מיד אחרי הכשל, לפני פעולת לוג נוספת שעלולה לשנות אותו.

נוסיף לפני wmain מיפוי מפורש, ונשתמש בו ליד הערך המספרי.

const wchar_t* state_name(DWORD state) {
    switch (state) {
    case SERVICE_STOPPED: return L"stopped";
    case SERVICE_START_PENDING: return L"start pending";
    case SERVICE_STOP_PENDING: return L"stop pending";
    case SERVICE_RUNNING: return L"running";
    case SERVICE_CONTINUE_PENDING: return L"continue pending";
    case SERVICE_PAUSE_PENDING: return L"pause pending";
    case SERVICE_PAUSED: return L"paused";
    default: return L"unknown";
    }
}

const wchar_t* start_name(DWORD start) {
    switch (start) {
    case SERVICE_BOOT_START: return L"boot";
    case SERVICE_SYSTEM_START: return L"system";
    case SERVICE_AUTO_START: return L"automatic";
    case SERVICE_DEMAND_START: return L"demand";
    case SERVICE_DISABLED: return L"disabled";
    default: return L"unknown";
    }
}

המצב הוא תצפית בזמן השאילתה, לא הבטחה לעתיד. גם PID שנקרא מכאן אינו handle פתוח לתהליך. אם נרצה לחקור אותו אחר כך, נצטרך לטפל בסיום התהליך ובאפשרות שמזהה מספרי ימוחזר.

פתרון התרגיל השני

הרשימה היא MULTI_SZ: כל שם מסתיים בnull, והרשימה מסתיימת במחרוזת ריקה נוספת. בקוד הבא הpointer מצביע לbuffer תקין שהוחזר מהAPI. עבור קלט לא מהימן נקבל גם אורך ונבדוק גבולות בכל מעבר.

#include <cwchar>

void print_dependencies(const wchar_t* names) {
    if (!names || *names == L'\0') {
        std::wcout << L"no dependencies\n";
        return;
    }
    for (auto p = names; *p; p += std::wcslen(p) + 1) {
        if (*p == L'+') std::wcout << L"group: " << (p + 1) << L'\n';
        else std::wcout << L"service: " << p << L'\n';
    }
}

הבדיקה המקומית הבאה מציגה dependency לשירות ולאחריו dependency לקבוצה. הסימון בתחילת השם מבחין בין השניים; הוא אינו חלק משם שירות רגיל שמנסים לפתוח.

const wchar_t sample[] = L"LabStorage\0+LabGroup\0";
print_dependencies(sample);

המחרוזת מקבלת גם null סופי מהliteral עצמו, ולכן יש לה סיום כפול. נוודא שהפלט כולל שתי שורות, ולא רק את LabStorage.

פתרון התרגיל השלישי

נתחיל בפקודת ההפעלה השמורה במנהל, כולל quoting. נבדוק שהנתיב מצביע לbuild שרצינו ושהחשבון יכול לקרוא ולהריץ אותו. אם אין תהליך או אירוע כניסה ראשון, לא נתחיל מחקירת לולאת העבודה.

נבדוק האם הטוען מצליח למצוא את הDLLים הנדרשים בתנאי ההפעלה האלה. ההצלחה מתוך תיקיית הפרויקט אינה מוכיחה שהחיפוש יעבוד מאותה נקודה בזמן הפעלה כשירות. נעדיף נתיבי תלויות מוגדרים ולא שינוי גלובלי אקראי של סביבת המערכת.

נתעד את ההגעה לStartServiceCtrlDispatcherW ואת השגיאה אם הוא נכשל. השגיאה של הרצה כconsole יכולה להיות צפויה בניסוי ידני, ולכן לא נבלבל אותה עם שגיאת האתחול תחת הSCM.

נבדוק את הזהות בפועל ואת פתיחת האובייקט הראשון שנכשל, עם desired access וקוד שגיאה. כך אפשר לתקן DACL נקודתי או בקשת זכות מיותרת, במקום לתת לחשבון גישה לכל המחשב.

נרשום את הנתיב המלא של קובץ התצורה ואת שלב האתחול שבו הוא נקרא. אם הקוד השתמש בנתיב יחסי, נתקן את קביעת המיקום. אם התצורה נמצאה אך אינה תקינה, נציג שגיאת schema נפרדת. שתי הבעיות יכולות להופיע עם אותה הודעת "לא הצלחתי לטעון settings" אם לא תוכננו את האבחון.