! Copyright (c) 2021 Approximatrix, LLC ! ! Permission is hereby granted, free of charge, to any person obtaining a copy ! of this software and associated documentation files (the "Software"), to deal ! in the Software without restriction, including without limitation the rights ! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ! copies of the Software, and to permit persons to whom the Software is ! furnished to do so, subject to the following conditions: ! ! The above copyright notice and this permission notice shall be included in ! all copies or substantial portions of the Software. ! ! The Software shall be used for Good, not Evil. ! ! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ! SOFTWARE. program player use config use instructions use player_endpoints use json_module use talking #ifdef WINDOWS use wsa_network, only: windows_network_startup => startup #endif implicit none character(len=1024)::url type(json_file)::j_checkin, j_instructions logical::work_to_do, checkin_json_available, instr_json_available integer::i_task integer::job_id character(128)::instruction_name #ifdef WINDOWS call windows_network_startup() #endif call parse_options() ! Change directory to the working directory now call chdir(working_directory) do while(.true.) ! Check in for work call get_check_in_url(url) checkin_json_available = request_json(url, j_checkin) if(checkin_json_available) then work_to_do = work_available(j_checkin) else Print *, "Checkin failed: "//trim(url) work_to_do = .false. end if if(work_to_do) then ! GNU Extension... :( Print *, fdate()//" Work Available" job_id = get_job_id_from_checkin(j_checkin) call get_instruction_name_from_checkin(j_checkin, instruction_name) call get_instruction_url(instruction_name, url) instr_json_available = request_json(url, j_instructions) if(instr_json_available) then ! Task loop call perform_tasks(j_instructions, job_id) call destroy_instructions(j_instructions) end if else #ifdef GNU call sleep(20) #endif end if ! Sleep a bit regardless #ifdef GNU call sleep(10) #endif ! Destroy any existing json if(checkin_json_available) then call j_checkin%destroy() end if end do contains subroutine usage() implicit none character(len=256)::pname call get_command_argument(0, pname) Print *, "Usage: "//trim(pname)//" " Print *, " " Print *, "captain is the build control server" Print *, " " Print *, "Options:" Print *, " -h Display this help" Print *, " -w Use dir as the working directory" Print *, " -l Use log as the logfile" Print *, " -i This player's identity" Print *, " -t This player's security token" end subroutine usage subroutine parse_options use config implicit none character(len=1024)::option integer::slen integer::i identity = " " token = "None" i = 1 do while(i <= command_argument_count()) call get_command_argument(i, option) if(option(1:1) /= "-") then captain = option else if(trim(option) == "-h") then call usage() stop else if(trim(option) == "-w") then i = i + 1 call get_command_argument(i, length=slen) allocate(character(len=slen) :: working_directory) call get_command_argument(i, working_directory) else if(trim(option) == "-l") then i = i + 1 call get_command_argument(i, length=slen) allocate(character(len=slen) :: logfile) call get_command_argument(i, logfile) else if(trim(option) == "-i") then i = i + 1 call get_command_argument(i, identity) else if(trim(option) == "-t") then i = i + 1 call get_command_argument(i, token) end if i = i + 1 end do ! Assign working directory from command if not specified if(.not. associated(working_directory)) then call get_command_argument(0, length=slen) allocate(character(len=slen) :: working_directory) call get_command_argument(i, working_directory) i = index(working_directory, "/", back=.true.) if(i == 0) then i = index(working_directory, "/", back=.true.) endif if(i == 0) then Print *, "Could not determine working_directory" stop else working_directory(i:slen) = ' ' end if end if ! Assign a temporary directory and file for a log file ! NOTE: will fail on Windows if(.not. associated(logfile)) then allocate(character(len=256) :: logfile) logfile = "/tmp/levitating-player.log" end if ! Assign this computer an identity if not explicitly specified if(len_trim(identity) == 0) then #ifdef GNU call hostnm(identity) #else Print *, "Could not determine host identity" stop #endif end if end subroutine parse_options function request_json(url, j) use json_module use gemini_protocol use instructions, only: parse_instructions use talking, only: request_to_temporary_file use utilities, only: delete_file implicit none logical::request_json character(*), intent(in)::url type(json_file), intent(out)::j character(:), pointer::filename integer::status_code status_code = request_to_temporary_file(url, filename) if(status_code == STATUS_SUCCESS) then j = parse_instructions(filename) request_json = .true. else request_json = .false. end if if(associated(filename)) then call delete_file(filename) deallocate(filename) end if end function request_json end program player